Spring 事务管理不适用于 Spring Boot + MyBatis?
Spring Transaction Management not working with Spring Boot + MyBatis?
我正在尝试让 Spring 事务管理在我的新 Spring Boot + MyBatis 应用程序中工作。
到目前为止,我已经设法让所有的东西都能正常工作,问题很少 - 它只是让 @Transactional
注释正常工作。目前,无论方法是否被注解,所有语句都会立即提交。
Spring Boot 为您做了很多样板配置,很难找到丢失的 link.
我的 build.gradle
包含以下依赖项:
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")
我的 application.properties
包含以下数据源配置:
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
bean 中的方法未按预期运行的简单示例如下:
@Transactional
public void performTransactionTest() throws Exception {
Person person = new Person();
person.setPersonId(123);
personMapper.insert(person);
throw new Exception("This should force a rollback!");
}
抛出异常但记录已经插入。
目前基本上没有关于 Spring Boot 和 MyBatis 的事务配置的文档,但据我所知,它应该主要像在 [=45= 中手动完成的那样自行连接起来] + MyBatis 应用程序以及它没有的地方 - 我们可以进一步配置它。话虽如此,我已经在 applicationContext.xml
中尝试了以下配置,但没有成功:
<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>
我可以确认,即使没有上述任何配置,DataSourceTransactionManager 也配置了与 MyBatis 映射器的 SqlSession 使用的相同的数据源。
任何可以将我推向正确方向的帮助或想法将不胜感激。如果您需要任何进一步的信息,我很乐意提供!
提前致谢!
Xandel
所以我通过用 @Transactional
注释 class 定义而不是方法定义来让它工作。
我不确定这是否是常见的做法。 Spring 引导事务管理文档不是那样做的 here but the Mybatis Spring sample does do it that way in their documentation here...
如果有人有进一步的信息可以解释这一点,我会很乐意将该答案标记为正确答案。
但是现在,我的问题已经解决了。
编辑
几个月后回到这个问题,我终于找到了它的底部。这里有 2 个主要问题。
正如 Kazuki 正确提到的那样,您需要使用 @Transactional(rollbackFor = Exception.class)
注释明确声明需要对已检查的异常进行回滚。
"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."(参考以下来源)
在我的示例代码中,我从同一个 class 调用 this.performTransactionTest()
。这样交易将被忽略。如果我改为通过对我的 class(例如 myAutoWiredBean.performTransactionTest()
)的有线引用来调用它,一切都会按预期进行。这也解释了为什么它似乎只有 class 级别注释有效,但那是因为任何调用的方法都会被有线 bean 引用。
这里有两篇文章,它们对我理解 Spring 的事务管理的更详细信息有很大帮助。非常感谢作者 Nitin Prabhu 和 Tim Mattison。
https://dzone.com/articles/spring-transaction-management
http://blog.timmattison.com/archives/2012/04/19/tips-for-debugging-springs-transactional-annotation/
希望对大家有所帮助!
Spring Transaction Management
的默认行为是在发生检查异常时提交。如果你想回滚一个事务,你可以抛出一个未经检查的异常(RuntimeException
)。 @Transactional(rollbackFor = Exception.class)
也给出相同的结果。
请试试这个。
谢谢。
根本原因是在我们显式创建数据源而不是依赖 spring 引导根据 application.yml
的参数自动创建的情况下缺少事务管理器。
也不需要注释 class 而是像下面这样注释方法并在启动期间初始化事务管理器 bean。
@Transactional(propagation = Propagation.REQUIRED, transactionManager = "transactionManager", rollbackFor = CustomExcp.class)
public int updt(Emp vo) throws CustomExcp {
...
}
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
log.info("DataSource Transaction Manager");
return transactionManager;
}
您可能有两个使用 MapperScan 的数据源可能会弄乱 mybatis 配置。您需要添加 SqlSessionFactory 和 SqlSessionTemplate ,如此处所述 http://mybatis.org/spring/getting-started.html.
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
@Slf4j
@MapperScan(value = "com.abc.xyx.aaa", sqlSessionTemplateRef = "PrimarySessionTemplate")
public class MyBatisPrimaryConfig {
@Bean(name = "PrimarySessionFactory")
@Primary
public SqlSessionFactory sessionFactory(@Qualifier("PrimaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "PrimarySessionTemplate")
@Primary
public SqlSessionTemplate primarySessionTemplate(@Qualifier("PrimarySessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
我正在尝试让 Spring 事务管理在我的新 Spring Boot + MyBatis 应用程序中工作。
到目前为止,我已经设法让所有的东西都能正常工作,问题很少 - 它只是让 @Transactional
注释正常工作。目前,无论方法是否被注解,所有语句都会立即提交。
Spring Boot 为您做了很多样板配置,很难找到丢失的 link.
我的 build.gradle
包含以下依赖项:
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")
我的 application.properties
包含以下数据源配置:
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
bean 中的方法未按预期运行的简单示例如下:
@Transactional
public void performTransactionTest() throws Exception {
Person person = new Person();
person.setPersonId(123);
personMapper.insert(person);
throw new Exception("This should force a rollback!");
}
抛出异常但记录已经插入。
目前基本上没有关于 Spring Boot 和 MyBatis 的事务配置的文档,但据我所知,它应该主要像在 [=45= 中手动完成的那样自行连接起来] + MyBatis 应用程序以及它没有的地方 - 我们可以进一步配置它。话虽如此,我已经在 applicationContext.xml
中尝试了以下配置,但没有成功:
<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>
我可以确认,即使没有上述任何配置,DataSourceTransactionManager 也配置了与 MyBatis 映射器的 SqlSession 使用的相同的数据源。
任何可以将我推向正确方向的帮助或想法将不胜感激。如果您需要任何进一步的信息,我很乐意提供!
提前致谢!
Xandel
所以我通过用 @Transactional
注释 class 定义而不是方法定义来让它工作。
我不确定这是否是常见的做法。 Spring 引导事务管理文档不是那样做的 here but the Mybatis Spring sample does do it that way in their documentation here...
如果有人有进一步的信息可以解释这一点,我会很乐意将该答案标记为正确答案。
但是现在,我的问题已经解决了。
编辑
几个月后回到这个问题,我终于找到了它的底部。这里有 2 个主要问题。
正如 Kazuki 正确提到的那样,您需要使用
@Transactional(rollbackFor = Exception.class)
注释明确声明需要对已检查的异常进行回滚。"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."(参考以下来源)
在我的示例代码中,我从同一个 class 调用 this.performTransactionTest()
。这样交易将被忽略。如果我改为通过对我的 class(例如 myAutoWiredBean.performTransactionTest()
)的有线引用来调用它,一切都会按预期进行。这也解释了为什么它似乎只有 class 级别注释有效,但那是因为任何调用的方法都会被有线 bean 引用。
这里有两篇文章,它们对我理解 Spring 的事务管理的更详细信息有很大帮助。非常感谢作者 Nitin Prabhu 和 Tim Mattison。
https://dzone.com/articles/spring-transaction-management
http://blog.timmattison.com/archives/2012/04/19/tips-for-debugging-springs-transactional-annotation/
希望对大家有所帮助!
Spring Transaction Management
的默认行为是在发生检查异常时提交。如果你想回滚一个事务,你可以抛出一个未经检查的异常(RuntimeException
)。 @Transactional(rollbackFor = Exception.class)
也给出相同的结果。
请试试这个。
谢谢。
根本原因是在我们显式创建数据源而不是依赖 spring 引导根据 application.yml
的参数自动创建的情况下缺少事务管理器。
也不需要注释 class 而是像下面这样注释方法并在启动期间初始化事务管理器 bean。
@Transactional(propagation = Propagation.REQUIRED, transactionManager = "transactionManager", rollbackFor = CustomExcp.class)
public int updt(Emp vo) throws CustomExcp {
...
}
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
log.info("DataSource Transaction Manager");
return transactionManager;
}
您可能有两个使用 MapperScan 的数据源可能会弄乱 mybatis 配置。您需要添加 SqlSessionFactory 和 SqlSessionTemplate ,如此处所述 http://mybatis.org/spring/getting-started.html.
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
@Slf4j
@MapperScan(value = "com.abc.xyx.aaa", sqlSessionTemplateRef = "PrimarySessionTemplate")
public class MyBatisPrimaryConfig {
@Bean(name = "PrimarySessionFactory")
@Primary
public SqlSessionFactory sessionFactory(@Qualifier("PrimaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "PrimarySessionTemplate")
@Primary
public SqlSessionTemplate primarySessionTemplate(@Qualifier("PrimarySessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}