TransactionSynchronizationManager
It is a core class of transaction management. Through transaction synchronization manager, we can manage the transactions of the current thread. Most of the time, we use this class to facilitate us to implement some of our own logic before the transaction ends or starts.
Similar to the following logic, we want to perform some business after the transaction is completed. Therefore, you can use TransactionSynchronizationManager.registerSynchronization to implement different functions of the TransactionSynchronization interface to implement different logic at different stages of a transaction.
@Service @Transactional @Slf4j public class UserService { @Autowired private UserRepository userRepository; @Autowired private OrderService orderService; public User addUser(User user) { log.info("Mission start!"); userRepository.save(user); TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void beforeCommit(boolean readOnly) { log.info("Transaction start commit"); } @Override public void afterCommit() { log.info("Transaction commit end"); orderService.addOrder(); } }); log.info("The task is over!"); return user; } }
In general, this can be done. However, when we execute the task in the afterCommit method, it also contains transactions. For example, orderService.addOrder() itself is a transaction method that uses JPA to save data.
@Service @Transactional @Slf4j public class OrderService { @Autowired private OrderRepository orderRepository; @Transactional public Order addOrder() { log.info("Start insertion order data"); Order order = new Order(); order.setMoney(100D); orderRepository.save(order); log.info("insert order End of data"); return order; } }
At this point, you will find that orderRepository.save(order) tried to save the data, but the final data was not saved to the database.
Possible causes
The following is not the result of the analysis of the source code. It's a summary of personal experience, so it may not be reliable to solve the problems you encounter in the end.
Add the following code in the addUser and addOrder methods to print the current transaction.
String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("currentTransactionName is {}",currentTransactionName);
Finally, the following results can be obtained
2021-01-05 23:28:28.669 INFO 12908 --- [ main] dai.samples.jpa2.service.UserService : currentTransactionName is dai.samples.jpa2.service.UserService.addUser 2021-01-05 23:28:28.673 INFO 12908 --- [ main] dai.samples.jpa2.service.OrderService : currentTransactionName is dai.samples.jpa2.service.UserService.addUser
The two methods use the same transaction, but it should be noted that the addOrder method is executed after the afterCommit transaction is committed. At this time, the JPA data in addOrder cannot be submitted after saving. So we need to put addOrder into a new transaction.
terms of settlement
In the @ Transactional annotation, the propagation parameter is used to control the propagation of transactions. It is set to Propagation.REQUIRED by default
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { ..... /** */ Propagation propagation() default Propagation.REQUIRED; ...... }
The logic of Propagation.REQUIRED is to create a new transaction if there is no transaction at present. If there is already a transaction, join it. In the above business, we do not want it to be added to the existing transaction, so just introduce the above logic. If we want to save JPA data to the database, we need to modify the transaction annotation to the @ transactional (propagation = propagation. Requirements_new) parameter
@Service @Transactional @Slf4j public class OrderService { @Autowired private OrderRepository orderRepository; @Transactional(propagation = Propagation.REQUIRES_NEW) public Order addOrder() { log.info("Start insertion order data"); Order order = new Order(); order.setMoney(100D); orderRepository.save(order); log.info("insert order End of data"); return order; }
PS. however, in many cases, we hope that the newly added methods can be managed by the same firm and use propagation.requirements_ New will cause the current operation to be out of the control of the upper level transaction. Therefore, be careful when using @ transactional (propagation = propagation. Requirements_new) and strictly control its abuse.
Reference blog: Problems caused by transaction propagation in the use of TransactionSynchronizationManager.registerSynchronization