0
votes

How can Slick (using 2.1) be set up so that it uses the same transaction manager associated to a Spring configuration?

More context: We have an app that relies on Slick for its db work, and uses a library (Activiti) whose transaction management is provided by Spring. We wrap calls to that lib around Spring transactions like shown below, and we'd like that whenever a transaction fails on Activiti's side, then the queries issued by our Slick calls get rolled back as well.

def withSpringTransaction[T](f: TransactionStatus => T)(implicit   transactionTemplate: TransactionTemplate) =
transactionTemplate.execute(new TransactionCallback[T] {
  protected def doInTransaction(status: TransactionStatus) = f(status)
})


withSpringTransaction { transactionStatus =>
   db.withTransaction { session =>
       // Activiti API calls
       // Slick API calls
  }
}

I know we could call both transactionStatus.setRollbackOnly() and session.rollBack() in the preceding code if something goes bad, but our problem lies in more complex scenarios where Activiti calls some listener elsewhere where there is no access to the session declared in this scope.

1

1 Answers

0
votes

I finally solved this by just letting Spring entirely handle the transaction. If the transaction is rolled-back by Activiti, or if we manually roll it back in our code with transactionStatus.setRollbackOnly(), then our db queries done through Slick will be rolled-back as well as long as we configure Slick to point to the same datasource as Spring, wrapped into a TransactionAwareDataSourceProxy. So Spring's app context should have something like

<bean id="dataSourceRaw" class="com.example.spring.DataSourceProvider" factory-method="getDataSource" />

<bean id="dataSourceTx" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
    <property name="targetDataSource" ref="dataSourceRaw" />
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">       
  <property name="dataSource" ref="dataSourceRaw" />
</bean>

So the Spring beans needing a reference to a datasource point to dataSourceRaw, and we create a new bean dataSourceTx which wraps that same datasource with a TransactionAwareDataSourceProxy, that is used by Slick.

Then the example in the question becomes something like

def withSpringAndSlickTransaction[T](f: TransactionStatus => Session => T)(implicit transactionTemplate: TransactionTemplate, dataSourceTx: TransactionAwareDataSourceProxy) = {
    transactionTemplate.execute(new TransactionCallback[T] {
      protected def doInTransaction(status: TransactionStatus) = {
        val proxiedDb: scala.slick.driver.PostgresDriver.simple.Database = Database.forDataSource(dataSourceTx)
        val session = proxiedDb.createSession()
        f(status)(session)
      }
    })
  }

withSpringAndSlickTransaction { transactionStatus => session =>
   // Activiti API calls
   // Slick API calls
}

Note we should manually create the session in Slick, since a call to db.withSession would try to set the underlying connection to autocommit mode, which throws an exception since Spring already set it to not autocommit. Of course we're also forbidden to manually roll-back the transaction with Slick with session.rollback and instead let Spring automatically do it (or ourselves manually with transactionStatus.setRollbackOnly()).