1. Not managed by Spring
As shown in the following example:
// @Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { // update order; } }
If the @ Service annotation is commented out at this time, this class will not be loaded into a Bean, and this class will not be managed by Spring, and the transaction will naturally become invalid.
2. The method is not public -- the exception will generally be recognized by the compiler
The following are from the official Spring documentation:
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
The general meaning is that @ Transactional can only be used on public methods, otherwise the transaction will not fail. If it is used on non-public methods, you can turn on AspectJ proxy mode.
Example:
@Service public class DemoServiceImpl implements DemoService { @Transactional(rollbackFor = SQLException.class) @Override int saveAll(){ // The compiler usually gives an error prompt in this place // do someThing; return 1; } }
idea recognition error png
3. Self call problem
Let's look at two examples:
@Service public class OrderServiceImpl implements OrderService { public void update(Order order) { updateOrder(order); } @Transactional public void updateOrder(Order order) { // update order; } }
There is no @ Transactional annotation on the update method. Call the updateOrder method with @ Transactional annotation. Does the transaction on the updateOrder method work?
Let's look at the following example:
@Service public class OrderServiceImpl implements OrderService { @Transactional public void update(Order order) { updateOrder(order); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateOrder(Order order) { // update order; } }
This time, @ Transactional is added to the update method, and requirements is added to the updateOrder_ New starts a new transaction. Does the new transaction work?
The answer to these two examples is: it doesn't work!
Because they call themselves, they call their own methods of the class without passing through the Spring proxy class. By default, transactions will take effect only when they are called externally. This is also an old classic problem.
One of the solutions to this problem is to inject yourself into your class and call another method with the injected object. This is not very elegant. Another feasible solution can be referred to< How does Spring open another transaction in one transaction? >This article.
4. The data source does not have a transaction manager configured
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
As shown above, if the current data source is not configured with a transaction manager, it is useless!
5. Transaction not supported
Consider the following example:
@Service public class OrderServiceImpl implements OrderService { @Transactional public void update(Order order) { updateOrder(order); } @Transactional(propagation = Propagation.NOT_SUPPORTED) public void updateOrder(Order order) { // update order; } }
Propagation.NOT_SUPPORTED: indicates that it will not run as a transaction. Currently, if there is a transaction, it will be suspended. For details, please refer to< Transaction isolation level and propagation mechanism >This article. If you don't support running in transaction mode, it's useless for the transaction to take effect!
6. Abnormal was eaten
This is also a more frequent scene:
@Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { try { // update order; }catch (Exception e){ //do something; } } }
If you eat the exception and don't throw it out, the transaction will not take effect
7. Exception type error or format configuration error
The above example throws another exception:
@Service public class OrderServiceImpl implements OrderService { @Transactional // @Transactional(rollbackFor = SQLException.class) public void updateOrder(Order order) { try { // update order }catch (Exception e){ throw new Exception("Update error"); } } }
In this way, the transaction will not take effect, because the default rollback is RuntimeException. If you want to trigger the rollback of other exceptions, you need to configure it on the annotation, such as:
@Transactional(rollbackFor = Exception.class)
This configuration is limited to the Throwable exception class and its subclasses.
summary
This paper summarizes eight scenarios of transaction failure. In fact, the most common scenarios are self invocation, exception being eaten, and incorrect exception throwing type