During the interview, you must ask | what scenarios will Spring transactions fail?

In daily work, improper use of Spring's transaction management function will cause the problem that Spring transactions do not take effect. The problem that Spring transactions do not take effect is also frequently asked in job hopping interviews.

Today, let's sort out which scenarios will lead to the effectiveness of Spring transactions.

Note: some of the contents are quoted from the published by glacier and adult cat< Deep understanding of distributed transactions: principle and Practice >A book. Article in GitHub and Gitee: GitHub: https://github.com/sunshinelyz/technology-binghe Gitee: https://gitee.com/binghe001/technology-binghe

Overview of Spring transactions not taking effect

Simply put, Spring transactions will fail in several specific scenarios, as shown in the following figure.

The database does not support transactions

The precondition for Spring transactions to take effect is that the connected database should support transactions. If the underlying database does not support transactions, Spring transactions will certainly fail. For example, if the database used is MySQL and the MyISAM storage engine is selected, Spring transactions will fail.

Transaction methods are not managed by Spring

If the class of the transaction method is not loaded into the Spring IOC container, that is, the class of the transaction method is not managed by Spring, the Spring transaction will fail. An example is as follows.

public class ProductService {
 @Autowired
 private ProductDao productDao;

 @Transactional(propagation = Propagation.REQUIRES_NEW)
 public void updateProductStockCountById(Integer stockCount, Long id){
  productDao.updateProductStockCountById(stockCount, id);
 }
}

If the @ Service annotation is not marked on the ProductService class and the instance of Product is not loaded into the Spring IOC container, the transaction of updateproductscockcountbyid() method will become invalid in Spring.

Method is not modified by public

If the method of the transaction is not modified by public, the Spring transaction will fail, for example, as shown in the following code.

@Service
public class ProductService {
 @Autowired
 private ProductDao productDao;

 @Transactional(propagation = Propagation.REQUIRES_NEW)
 private void updateProductStockCountById(Integer stockCount, Long id){
  productDao.updateProductStockCountById(stockCount, id);
 }
}

Although the @ Service annotation is marked on the ProductService, the @ transactional (propagation = propagation. Requirements_new) annotation is marked on the updateProductStockCountById() method.

However, since the updateProductStockCountById() method is an internal private method (decorated with private), the transaction of the updateProductStockCountById() method will become invalid in Spring.

Method calls in the same class

If two methods in the same class are A and B respectively, no transaction annotation is added to method A, and @ Transactional transaction annotation is added to method B, and method A calls method B, the transaction of method B will become invalid. For example, the following code is shown.

@Service
public class OrderService {

 @Autowired
 private OrderDao orderDao;

 @Autowired
 private ProductDao productDao;

 public void submitOrder(){
  //Generate order
  Order order = new Order();
  long number = Math.abs(new Random().nextInt(500));
  order.setId(number);
  order.setOrderNo("order_" + number);
  orderDao.saveOrder(order);
  //Inventory reduction
  this.updateProductStockCountById(1, 1L);
 }

 @Transactional(propagation = Propagation.REQUIRES_NEW)
 public void updateProductStockCountById(Integer stockCount, Long id){
  productDao.updateProductStockCountById(stockCount, id);
 }
}

Both the submitOrder() method and the updateproductstock countbyid () method are in the OrderService class. The submitOrder() method is not marked with a transaction annotation, the updateproductstock countbyid () method is marked with a transaction annotation, and the submitOrder() method calls the updateproductstock countbyid () method. At this time, the transaction of the updateproductstock countbyid () method will become invalid in Spring.

Transaction manager is not configured

If Spring's transaction manager is not configured in the project, even if Spring's transaction management function is used, Spring's transactions will not take effect.

For example, the following code is not configured in the configuration class of the project.

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
 return new DataSourceTransactionManager(dataSource);
}

At this point, Spring transactions will fail.

The transaction propagation type of the method does not support transactions

If the transaction propagation type of the internal method is a propagation type that does not support transactions, the transaction of the internal method will become invalid in Spring.

For example, the following code is shown.

@Service
public class OrderService {
 @Autowired
 private OrderDao orderDao;
 @Autowired
 private ProductDao productDao;

 @Transactional(propagation = Propagation.REQUIRED)
 public void submitOrder(){
  //Generate order
  Order order = new Order();
  long number = Math.abs(new Random().nextInt(500));
  order.setId(number);
  order.setOrderNo("order_" + number);
  orderDao.saveOrder(order);
  //Inventory reduction
  this.updateProductStockCountById(1, 1L);
 }

 @Transactional(propagation = Propagation.NOT_SUPPORTED)
 public void updateProductStockCountById(Integer stockCount, Long id){
  productDao.updateProductStockCountById(stockCount, id);
 }
}

Because the transaction propagation type of updateProductStockCountById() method is NOT_SUPPORTED, if transaction is not supported, the transaction of updateproductsstockcountbyid() method will be invalidated in Spring.

Incorrect catch exception

Incorrect exception capture will also invalidate Spring transactions, as shown in the following example.

@Service
public class OrderService {
 @Autowired
 private OrderDao orderDao;
 @Autowired
 private ProductDao productDao;


 @Transactional(propagation = Propagation.REQUIRED)
 public void submitOrder(){
  //Generate order
  Order order = new Order();
  long number = Math.abs(new Random().nextInt(500));
  order.setId(number);
  order.setOrderNo("order_" + number);
  orderDao.saveOrder(order);
  //Inventory reduction
  this.updateProductStockCountById(1, 1L);
 }

 @Transactional(propagation = Propagation.REQUIRED)
 public void updateProductStockCountById(Integer stockCount, Long id){
  try{
   productDao.updateProductStockCountById(stockCount, id);
   int i = 1 / 0;
  }catch(Exception e){
   logger.error("Deduction inventory exception:", e.getMesaage());
  }
 }
}

The try catch code block is used in the updateProductStockCountById() method to catch exceptions. Even if an exception is thrown inside the updateProductStockCountById() method, it will also be caught by the catch code block. At this time, the transaction of the updateProductStockCountById() method will be committed but not rolled back, and the transaction of the submitOrder() method will be committed but not rolled back, This causes the rollback failure of Spring transactions.

Wrong label exception type

If the wrong exception type is marked in the @ Transactional annotation, the rollback of Spring transactions will fail, as shown in the following example.

@Transactional(propagation = Propagation.REQUIRED)
public void updateProductStockCountById(Integer stockCount, Long id){
 try{
  productDao.updateProductStockCountById(stockCount, id);
 }catch(Exception e){
  logger.error("Deduction inventory exception:", e.getMesaage());
  throw new Exception("Deduction inventory exception");
 }
}

An Exception is caught in the updateProductStockCountById() method, and an Exception of type Exception is thrown in the Exception. At this time, the transaction rollback of updateproductstocountbyid() method will fail.

Why does it fail? This is because the transaction Exception type for default rollback in Spring is RuntimeException, and the above code throws an Exception.

By default, Exception exceptions cannot be caught in Spring transactions, so the transaction rollback of updateProductStockCountById() method will fail at this time.

At this time, you can manually specify the transaction exception type marked by the updateProductStockCountById() method, as shown below.

@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)

Here, it should be noted that the rollbackFor attribute in the Spring transaction annotation @ Transactional can specify the Throwable exception class and its subclasses.

Added by nblackwood on Tue, 21 Dec 2021 18:41:06 +0200