Search code examples
spring-boottransactionsmybatisspring-mybatis

Spring-boot with MyBatis doesn't rollback transactions


I've got a Spring Boot (version 2.1.8.RELEASE) web application (deployed inside a Wildfly 9 application container), with MyBatis, being auto-configured using the Spring Boot starter, but when using the @transactional annotation, the statements are always committed, even when they should be rolled back. My pom.xml fragment looks like this:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

I've got the following lines in my application.properties:

spring.datasource.url=jdbc:sqlserver://my.server.com:1433;databaseName=MyDatabase
spring.datasource.username=myUsername
spring.datasource.password=myPassword
mybatis.config-location=classpath:mybatis-config.xml

And this is my mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="localCacheScope" value="STATEMENT"/>
    </settings>
    <typeAliases>
        <package name="my.package.model"/>
    </typeAliases>
    <mappers>
    ...
    </mappers>
</configuration>

My application initialiser class looks like this:

@SpringBootApplication
@EnableTransactionManagement
@ComponentScan("my.packag e")
public class ServletInitializer extends SpringBootServletInitializer
{
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) 
    {
        // some config here

        return builder.sources(ServletInitializer.class);
    } // end method configure()

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException
    {
        super.onStartup(servletContext);
        // some config here
    } // end method onStartup()

    // some other beans here

    public static void main(String[] args) 
    {
        SpringApplication.run(ServletInitializer.class, args);
    }

} // end class ServletInitializer

I've got a controller, which isn't part of the transaction but which autowires in a service layer bean:

@Controller
public class DataMigrationController
{

    @AutoWired private MyService service;

    @GetMapping("/path")
    public @ResponseBody Boolean something(Model model, HttpSession session)
    {
        service.doTask();
        return true;
    }
}

And my service class is like this:

@Service
public class MyService
{

    @AutoWired private MyMapper mapper;

    @Transactional(rollbackFor=Throwable.class) 
    public void doTask()
    {
        Person p= new Person();
        p.setPersonID("999999");
        p.setSurname("TEST");
        p.setForename1("TEST");
        p.setTitle("Mr");

        mapper.insertPerson(p);
        throw new RuntimeException();
    }
}

I would expect the transaction to be rolled back because of the RuntimeException being thrown at the end of the doTask() method but when I check the database, the row is present. I've also tried using TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); instead of throwing the exception, but I get the same result.

I'm using the transaction debug class suggested by this blog post which tells me that there's no transaction in the controller (which I would expect) and there is one in the service class. But for some reason, the transaction just isn't rolling back.

Can anyone please advise?


Solution

  • I found a reason that commit a transaction. The Spring Boot use JTA transaction management under Java EE(Jakarta EE) environment by default settings. But the DataSource that created via Spring Boot can not join it.

    You can select a solution as follows:

    • Disable the JTA transaction management
    • Use a transactional DataSource managed by Wildfly

    How to disable JTA transaction management

    You can disable the JTA transaction management as follow:

    spring.jta.enabled=false
    

    For details, see https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/htmlsingle/#boot-features-jta.

    How to use a DataSource managed by Wildfly

    You can use the a DataSource managed by Wildfly.

    spring.datasource.jndi-name=java:jboss/datasources/demo
    

    For details, see https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/htmlsingle/#boot-features-connecting-to-a-jndi-datasource

    Note: How to configure a DataSource (need to enable JTA) on Wildfly, see https://docs.jboss.org/author/display/WFLY9/DataSource+configuration?_sscc=t.