1.访问权限问题
如果我们自定义的事务方法(即目标方法),它的访问权限不是public
,而是private、default或protected的话,spring则不会提供事务功能。
2.方法用final修饰
如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。
如果某个方法是static的,同样无法通过动态代理,变成事务方法。
3.方法内部调用
在同一个类中的方法直接内部调用,会导致事务失效。
解决方法:
a.新加一个Service方法,把@Transactional注解加到新Service方法上,把需要事务执行的代码移到新方法中。
b.如果不想再新加一个Service类,在该Service类中注入自己也可。
c.在该Service类中使用AopContext.currentProxy()获取代理对象。如 ((ServiceA)AopContext.currentProxy()).doSave(user)
4.未被spring管理
如果类没有加@Service
注解,那么该类不会交给spring管理,所以它的方法也不会生成事务。
5.多线程调用
我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
6.数据表不支持事务
数据库引擎myisam
并不支持事务,在mysql5以后,myisam已经逐渐退出了历史的舞台,取而代之的是innodb。
7.未开启事务
springboot通过DataSourceTransactionManagerAutoConfiguration
类,已经默默的帮你开启了事务。你所要做的事情很简单,只需要配置spring.datasource
相关参数即可。
但如果你使用的还是传统的spring项目,则需要在applicationContext.xml文件中,手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。
8.错误的传播特性
我们在使用@Transactional
注解时,是可以指定propagation
参数的。
该参数的作用是指定事务的传播特性,spring目前支持7种传播特性:
-
REQUIRED
如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。 -
SUPPORTS
如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。 -
MANDATORY
如果当前上下文中存在事务,否则抛出异常。 -
REQUIRES_NEW
每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。 -
NOT_SUPPORTED
如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。 -
NEVER
如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。 -
NESTED
如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务
目前只有这三种传播特性才会创建新事务:REQUIRED,REQUIRES_NEW,NESTED。
9.自己吞了异常
开发者在代码中手动try...catch了异常。因为开发者自己捕获了异常,又没有手动抛出,换句话说就是把异常吞掉了。
10.手动抛了别的异常
spring事务,默认情况下只会回滚RuntimeException
(运行时异常)和Error
(错误),对于普通的Exception(非运行时异常),它不会回滚。
11.自定义了回滚异常
可以通过设置rollbackFor
参数,来完成自定义回滚的异常。如
@Transactional(rollbackFor = Exception.class)
如果使用默认值,一旦程序抛出了Exception,事务不会回滚,这会出现很大的bug。所以,建议一般情况下,将该参数设置成:Exception或Throwable。
12.嵌套事务回滚多了
可以将内部嵌套事务放在try/catch中,并且不继续往上抛异常。这样就能保证,如果内部嵌套事务中出现异常,只回滚内部事务,而不影响外部事务。
13.大事务问题
@Transactional
注解,如果被加到方法上,有个缺点就是整个方法都包含在事务当中了。
如果方法非常多,调用层级很深,而且有部分查询方法比较耗时的话,会造成整个事务非常耗时,而从造成大事务问题。
上面聊的这些内容都是基于@Transactional
注解的,主要说的是它的事务问题,我们把这种事务叫做:声明式事务
。
其实,spring还提供了另外一种创建事务的方式,即通过手动编写代码实现的事务,我们把这种事务叫做:编程式事务
。
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
queryData1();
queryData2();
transactionTemplate.execute((status) => {
addData1();
updateData2();
return Boolean.TRUE;
})
}
在spring中为了支持编程式事务,专门提供了一个类:TransactionTemplate,在它的execute方法中,就实现了事务的功能。
相较于@Transactional
注解声明式事务,我更建议大家使用,基于TransactionTemplate
的编程式事务。主要原因如下:
-
避免由于spring aop问题,导致事务失效的问题。
-
能够更小粒度的控制事务的范围,更直观。