Spring @Transactional commits at the end of each service method

17,751

Solution 1

With the help of Spring transaction internals, Open session in view filter I came up with a solution.

According to the first link, in a Spring MVC context a transaction is begun in the controller context and the @Transactional methods in service layer then participates, creates or make another appropriate transaction on top of the already existing transaction. But if the UI layer is something other than Spring MVC this does not happen.

Spring transaction flow

As shown in this Spring transaction image the transaction advisor is the one who takes the decision whether to commit a transaction or mark for rollback. So if we just let a service method proxy to create a transaction where non exists beforehand, at the end of that very proxy method the advisor takes the decision to commit the transaction. To overcome this we have to create a transaction well before the two service method calls, so those two methods will join the transaction rather than committing by them selves.

In Hibernate context the Spring MVC supports OpenSessionInView, which creates a transaction per request. What I have done is create a similar servlet filter to create a transaction at the beginning of a request and commits at the end of filter chain. This works perfectly in Vaadin, Spring and JDBC environment. If you are so concerned that a Spring transaction and hence a database connection is acquired on every HTTP request, try using a LazyConnectionDataSourceProxy.

See the solution here: I want to... Use JDBC, Spring transactions and @Transactional

Solution 2

If you want to have one transaction for both method calls, you have to make sure, that the method, where the two methods are called, also have the transaction annotation, like this example:

@Transactional
public void callingMethod() {
    method1();
    method2();
}

@Transactional
public void method1() {
}

@Transactional
public void method2() {
}
Share:
17,751
SashikaXP
Author by

SashikaXP

Updated on July 12, 2022

Comments

  • SashikaXP
    SashikaXP almost 2 years

    I have configured a usual declarative transaction management in a Vaadin, Spring project. I have added <tx:annotation-driven transaction-manager="transactionManager" /> in my root-context.xml and all other required maven dependencies in the pom. My Service methods are annotated with @Transactional with default propagation.

    I want to call two service methods from a method in UI side expecting those two service method to partcipate in a single transaction as the default propagation is PROPAGATION_REQUIRED. But those two methods are committed to the db independantly. That means if the second method fails, the first one has anyway committed to the db. I have not used try{}catch{{ blocks so any RuntimeException will be bubbled up.

    The spring logs are attached. Some lines are removed to reduce the #of lines

    [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/getTransaction Creating new transaction with name [...UserServiceImpl.turnOnPwdResetFlag]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/doBegin Switching JDBC Connection [com.jolbox.bonecp.ConnectionHandle@42398a05] to manual commit [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/handleExistingTransaction Participating in existing transaction [qtp187048467-48] DEBUG o.s.jdbc.core.JdbcTemplate/doInStatement SQL update affected 1 rows [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/processCommit Initiating transaction commit [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/doCommit Committing JDBC transaction on Connection [com.jolbox.bonecp.ConnectionHandle@42398a05] [qtp187048467-48] DEBUG o.s.jdbc.datasource.DataSourceUtils/doReleaseConnection Returning JDBC Connection to DataSource ` ` [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/getTransaction Creating new transaction with name [...UserServiceImpl.turnOffPwdResetFlag]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT;  [qtp187048467-48] DEBUG o.s.j.d.DataSourceTransactionManager/handleExistingTransaction Participating in existing transaction [qtp187048467-48] DEBUG o.s.jdbc.datasource.DataSourceUtils/doReleaseConnection Returning JDBC Connection to DataSource 
    
  • SashikaXP
    SashikaXP about 9 years
    So that means if the method1 and method2 is being called from a method in a UI related class, I have to annotate that specific method also with @Transactional?
  • dunni
    dunni about 9 years
    Yes. The most top method with a Transactional annotation in the call stack defines the transaction boundaries. In my example the transaction is started on entering the callingMethod and committed when exiting the callingMethod. If i would remove the Transactional annotation on callingMethod, then the behaviour is like yours (one transaction for method1 and another one for method2).
  • SashikaXP
    SashikaXP about 9 years
    Usually annotating a method in ui side is not a good thing and sometimes impossible. In the case the ui part is vaadin, annotating a method in ui is impossible. But I found out as you said if a transaction is already available when the callingMethod () is called from ui, the other methods just participate in that transaction as expected. Found a solution using a servlet filter. I will update the answer later
  • dunni
    dunni about 9 years
    Another way would be to create a method in your service layer, which calls your other two methods and annotate that new method with Transactional.