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?
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:
DataSource
managed by WildflyYou 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.
DataSource
managed by WildflyYou 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.