Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Transaction Management not working with Spring Boot + MyBatis?

I am trying to get Spring Transaction Management working in my new Spring Boot + MyBatis application.

So far I have managed to get everything working with minimal issues - it's just getting the @Transactional annotation to function correctly. Currently, all statements are committed immediately regardless of whether the method is annotated or not.

Spring Boot does so much of the boilerplate configuration for you that it's difficult to find the missing link.

My build.gradle contains the following dependencies:

compile("org.springframework.boot:spring-boot-starter-amqp")
compile("org.mybatis.spring.boot:mybatis-spring-boot-starter:1.0.0")
compile("mysql:mysql-connector-java:5.1.38")

My application.properties contains the following datasource configuration:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/my_db
spring.datasource.username=my_user
spring.datasource.password=my_pass

A simple example of a method in a bean that is not acting as expected is as follows:

@Transactional
public void performTransactionTest() throws Exception {

    Person person = new Person();
    person.setPersonId(123);
    personMapper.insert(person);

    throw new Exception("This should force a rollback!");

}

The Exception gets thrown but the record has already been inserted.

There is basically no documentation currently in existence on transaction configuration for Spring Boot AND MyBatis together but as far as I understand, it should mostly wire itself up as would be done manually in a Spring + MyBatis application and where it doesn't - we are able to configure it further. With that said I have tried the following configurations in my applicationContext.xml with no luck:

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />

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

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>

I can confirm that even without any of the above configurations a DataSourceTransactionManager is configured with the same DataSource that the MyBatis mappers' SqlSession uses.

Any help or ideas that could push me in the right direction would be greatly appreciated. If you require any further information I am happy to provide it!

Thanks in advance!

Xandel

like image 976
Xandel Avatar asked May 18 '16 21:05

Xandel


1 Answers

So I got it working by annotating the class definition with @Transactional instead of the method definition.

I am not sure if this is common practice. The Spring Boot Transaction Management documentation doesn't do it like that here but the Mybatis Spring sample does do it that way in their documentation here...

If anyone has further information that could explain this I will happily mark that answer as the correct one.

For now however, my problem is solved.

EDIT

Returning to this problem month's later I have finally gotten to the bottom of it. There were 2 main issues here.

  1. As Kazuki correctly mentioned, you need to explicitly declare that rollbacks need to happen for checked exceptions using the @Transactional(rollbackFor = Exception.class) annotation.

  2. "Transaction boundaries are only created when properly annotated methods are called through a Spring proxy. This means that you need to call your annotated method directly through an @Autowired bean or the transaction will never start." (reference to this source below)

In my sample code I was calling this.performTransactionTest() from the same class. In this way the transaction will be ignored. If I instead call it via a wired reference to my class such as myAutoWiredBean.performTransactionTest() everything works as expected. This also explains why it appeared only the class level annotation was working, but that's because any method called would have been referenced by a wired bean.

Two articles which were MAJOR assistance to me in understanding the finer details of Spring's transaction management are here. A big thanks to the authors Nitin Prabhu and Tim Mattison.

https://dzone.com/articles/spring-transaction-management

http://blog.timmattison.com/archives/2012/04/19/tips-for-debugging-springs-transactional-annotation/

I hope this helps somebody!

like image 188
Xandel Avatar answered Sep 23 '22 00:09

Xandel