1. Manually throw other exceptions
手动抛出其他异常
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
@Transactional
public void addDemo(Demo demo) throws Exception {
demoMapper.save(demo);
demoFlowMapper.saveFlow(demo);
throw new Exception();
}
}
- The reason: In the above code example, an Exception is manually thrown, but it will not be rolled back, because Spring only handles
RuntimeException
and Error by default, and will not roll back for ordinaryExceptions
unless the configuration is specified with therollbackFor
attribute.
原因:在上面的代码示例中,手动抛出了一个Exception,但不会回滚,因为Spring默认只处理RuntimeException
和Error,不会为普通回滚,Exceptions
除非用属性指定了配置rollbackFor
。 - Solution: Add attribute configuration
@Transactional(rollbackFor = Exception.class)
.
解决方案:添加属性配置@Transactional(rollbackFor = Exception.class)
。
2. The transaction annotation is overwritten, causing the transaction to fail
事务批注被覆盖,导致事务失败
public interface MyRepository {
@Transactional
void save(String data);
}
public class MyRepositoryImpl implements MyRepository {
@Override
public void save(String data) {
// mysql
}
}
public class MyService {
@Autowired
private MyRepository myRepository;
@Transactional
public void doSomething(String data) {
myRepository.save(data);
}
}
public class MyDemoService extends MyService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething(String data) {
super.doSomething(data);
}
}
The reason: MyDemoService
is a subclass of MyService
and overrides the doSomething()
method. This method uses a different propagation behavior (REQUIRES_NEW
) to override the @Transactional
annotation of the parent class. In this case, when the doSomething()
method of MyDemoService
is called, because the annotations in the subclass method override the annotations of the parent class, the Spring framework will not start the transaction in the method of the parent class. Therefore, when the save()
method of MyRepository
is called, the transaction will not be started nor rolled back. This will cause data inconsistency issues because the database operations performed in MyRepository
’s save()
method will not be rolled back.
原因是: MyDemoService
是方法的子类, MyService
并覆盖了 doSomething()
方法。此方法使用不同的传播行为( REQUIRES_NEW
)来覆盖 @Transactional
父类的注释。在这种情况下,当调用 doSomething()
的方法 MyDemoService
时,由于子类方法中的注释覆盖了父类的注释,Spring框架将不会在父类的方法中启动事务。因此,当调用 save()
的方法 MyRepository
时,事务将不会启动也不会回滚。这将导致数据不一致问题,因为在的方法中执行的数据库操作 MyRepository
save()
将不回滚。
3. The transaction method is modified by final and static keywords
事务方法由final和static关键字修改
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
@Transactional
public final void addDemo(Demo demo) throws Exception {
demoMapper.save(demo);
demoFlowMapper.saveFlow(buildFlowByDemo(demo));
throw new Exception();
}
}
- The reason: If a method is declared final or static, the method cannot be overridden by subclasses, that is to say, dynamic proxy cannot be performed on the method, which will cause Spring to fail to generate transaction proxy objects to manage transactions.
原因:如果一个方法被声明为final或者static,那么这个方法就不能被子类覆盖,也就是说不能对这个方法进行动态代理,这会导致Spring无法生成事务代理对象来管理事务。 - Solution: Do not modify the
addDemo
transaction method with final or static.
解决方案:不要addDemo
用final或static修改事务处理方法。
4. In the same class, the method is called internally
在同一个类中,内部调用该方法
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
public void addDemo(TianLuo tianluo){
this.executeAddDemo(demo);
}
@Transactional
public void executeAddDemo(Demo demo) {
demoMapper.save(demo);
demoFlowMapper.saveFlow(buildFlowByDemo(demo));
}
}
- The reason: The transaction is implemented through the
Spring AOP
proxy, and in the same class, when one method calls another method, the calling method directly calls the code of the target method instead of calling through the proxy class. That is to say, in the above code, the targetexecuteAddDemo
method is not called through the proxy class, so the transaction does not take effect.
原因:事务是通过代理实现Spring AOP
的,而在同一个类中,当一个方法调用另一个方法时,调用方法直接调用目标方法的代码,而不是通过代理类调用。也就是说,在上面的代码中,目标executeAddDemo
方法不是通过代理类调用的,因此事务不会生效。 - Solution: You can create one more class so that these two methods are separated and in different classes. as follows:
解决方案:您可以再创建一个类,以便将这两个方法分开并放在不同的类中。具体如下:
@Service
public class DemoExcuteServiceImpl implements DemoExcuteService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
public void addDemo(TianLuo tianluo){
this.executeAddDemo(demo);
}
@Transactional
public void executeAddDemo(Demo demo) {
demoMapper.save(demo);
demoFlowMapper.saveFlow(buildFlowByDemo(demo));
}
}
@Service
public class DemoAddServiceImpl implements DemoAddService {
@Autowired
private DemoExcuteService demoExecuteService;
public void addTianLuo(Demo demo){
demoExecuteService.executeAddDemo(demo);
}
}
Of course, sometimes you can also inject yourself into the Service
class, or get the proxy object through AopContext.currentProxy()
.
当然,有时候你也可以将自己注入到类中 Service
,或者让代理对象通过 AopContext.currentProxy()
。
5. The access permission of the method is not public
方法的访问权限不是公共的
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
@Transactional
private void executeAddDemo(Demo demo) {
demoMapper.save(demo);
demoFlowMapper.saveFlow(buildFlowByDemo(demo));
}
}
The reason: The access authority of the spring transaction method addDemo
is not public, so the transaction does not take effect, because the Spring
transaction is implemented by the AOP
mechanism, the essence of the AOP
mechanism is a dynamic proxy, and if the transaction method of the proxy is not public, computeTransactionAttribute()
will return null, that is, the transaction attribute does not exist at this time. You can look at the source code of AbstractFallbackTransactionAttributeSource
:
原因:spring transaction方法的访问权限 addDemo
不是public的,所以transaction不会生效,因为 Spring
transaction是由机制实现 AOP
的,机制的本质 AOP
是一个动态代理,如果代理的transaction方法不是public的, computeTransactionAttribute()
会返回null,也就是transaction属性此时不存在。你可以看一下源代码 AbstractFallbackTransactionAttributeSource
:
6. The storage engine of the database does not support transactions
数据库的存储引擎不支持事务
The bottom layer of Spring
transactions still depends on the transaction support of the database itself. In MySQL
, the MyISAM
storage engine does not support transactions, but the InnoDB
engine supports transactions. Therefore, when designing tables during the development phase, make sure that the storage engine you choose supports transactions.
事务的底层 Spring
仍然依赖于数据库本身的事务支持。在 MySQL
中, MyISAM
存储引擎不支持事务,但 InnoDB
引擎支持事务。因此,在开发阶段设计表时,请确保您选择的存储引擎支持事务。
7. Misconfigured @Transactional annotation
@Transactional注释配置错误
@Transactional(readOnly = true)
public void updateUser(User user) {
userDao.updateUser(user);
}
- The reason: Although the
@Transactional
annotation is used, thereadOnly=true
attribute in the annotation indicates that this is a read-only transaction, so an exception will be thrown when the User entity is updated.
原因:虽然使用了@Transactional
注释,但是readOnly=true
注释中的属性表明这是一个只读事务,因此当更新User实体时会抛出异常。 - Solution: Set the
readOnly
attribute to false, or remove thereadOnly
attribute in the@Transactional
annotation.
解决方案:将属性设置readOnly
为false,或删除注释中的readOnly
属性@Transactional
。
8. The transaction timeout setting is too short
事务超时设置太短
@Transactional(timeout = 1)
public void doSomething() {
//...
}
The reason: In the above example, the timeout attribute is set to 1 second, which means that if the transaction cannot be completed within 1 second, it will be reported that the transaction has timed out.
原因:在上面的例子中,timeout属性被设置为1秒,这意味着如果交易不能在1秒内完成,则会报告交易超时。
9. Using the wrong transaction propagation mechanism
使用错误的事务传播机制
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void executeAddDemo(Demo demo) {
demoMapper.save(demo);
demoFlowMapper.saveFlow(buildFlowByDemo(demo));
}
}
- The reason: the
Propagation.NOT_SUPPORTED
propagation feature does not support transactions.
原因是:Propagation.NOT_SUPPORTED
传播特性不支持事务。 - Solution: Choose the correct transaction propagation mechanism.
解决方案:选择正确的事务传播机制。
Spring provides 7 transaction propagation mechanisms:
Spring提供了7种事务传播机制:
- REQUIRED (default): If a transaction currently exists, join the transaction; otherwise, create a new transaction. This propagation level indicates that the method must be executed within a transaction.
必需(默认):如果当前存在事务,则加入该事务;否则,创建新事务。此传播级别指示该方法必须在事务中执行。 - SUPPORTS: If a transaction currently exists, join the transaction; otherwise, continue execution in a non-transactional manner.
SUPPORTS:如果当前存在事务,则加入该事务;否则,以非事务方式继续执行。 - MANDATORY: If a transaction currently exists, join the transaction; otherwise, throw an exception.
MANDATORY:如果当前存在事务,则加入该事务;否则抛出异常。 - REQUIRES_NEW: Creates a new transaction, and suspends the transaction if one exists.
要求_新:创建一个新事务,如果存在事务,则挂起该事务。 - NOT_SUPPORTED: The operation is performed in a non-transactional manner, and the transaction is suspended if there is a current transaction.
不支持:操作以非事务方式执行,并且如果存在当前事务,则事务被挂起。 - NEVER: The operation is performed in a non-transactional manner, and an exception is thrown if a transaction currently exists.
NEVER:操作以非事务的方式执行,如果当前存在事务,则抛出异常。 - NESTED: If a transaction currently exists, it is executed within a nested transaction. If there is no transaction, it is executed at the
REQUIRED
propagation level. A nested transaction is part of an outer transaction and can be partially committed or rolled back when the outer transaction commits or rolls back.
NESTED:如果当前存在事务,则在嵌套事务中执行该事务。如果没有事务,则在传播级别执行事务REQUIRED
。嵌套事务是外部事务的一部分,并且可以在外部事务提交或回滚时部分提交或回滚。
10. The rollbackFor property configuration error
rollbackFor属性配置错误
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
@Transactional(rollbackFor = Error.class)
public void addDemo(Demo demo){
demoMapper.save(demo);
demoFlowMapper.saveFlow(demo);
throw new Exception();
}
}
- The reason: The exception specified by the
rollbackFor
attribute must be Throwable or its subclass. By default, bothRuntimeException
andError
are automatically rolled back. But because of the above code example,rollbackFor = Error.class
is specified, but the exception thrown isException
, andException
andError
have no inheritance relationship, so the transaction will not take effect.
原因:属性指定的异常rollbackFor
必须是Throwable或其子类。默认情况下,和RuntimeException
Error
都将自动回滚。但由于上面的代码示例中,rollbackFor = Error.class
指定的是,但抛出的异常是Exception
,和Exception
和Error
没有继承关系,所以事务不会生效。
Transactional annotation source code:
事务注释源代码:
Solution: The exception specified by the rollbackFor
attribute matches the exception that was thrown.
解决方案:属性指定的异常 rollbackFor
与引发的异常匹配。
11. Your service class is not managed by Spring
服务类不是由Spring管理的
public class DemoServiceImpl implements DemoService {
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoFlowMapper demoFlowMapper;
@Transactional
public void addDemo(Demo demo){
demoMapper.save(demo);
demoFlowMapper.saveFlow(demo);
}
}
- The reason: In the above example, after the
@Service
annotation, the spring transaction (@Transactional
) does not take effect, because theSpring
transaction is implemented by theAOP
mechanism, that is to say, when the bean is obtained from theSpring
IOC
container, Spring will be the targetClasses
create proxies to support transactions. But after@Service
is annotated, your service classes are not managed by Spring, so how to create proxy classes to support transactions?
原因:在上面的例子中,annotation之后@Service
,spring transaction(@Transactional
)并不生效,因为Spring
transaction是由机制实现AOP
的,也就是说,当bean从容器中获取Spring
IOC
时,Spring会为目标Classes
创建代理来支持transaction。但是注释之后@Service
,你的服务类就不是Spring管理的了,那么如何创建代理类来支持transactions呢?
- Solution: Add
@Service
annotation.
解决方案:添加@Service
注释。
Author:omgzui A full stack engineer, with some medicinal knowledge, likes to read and share.
FROM:https://medium.com/javarevisited/
文章评论