Interview questions | In which scenarios will Spring's transactions fail?

Hello everyone, I'm Glacier~~

In daily work, if Spring's transaction management function is used improperly, it will cause the problem that Spring transactions do not take effect. The problem that Spring transactions do not take effect is also a question that is frequently asked in job-hopping interviews.

Today, let's sort out which scenarios will cause Spring transactions to take effect.

Note: Part of the content is quoted from the book "In -depth Understanding of Distributed Transactions: Principles and Practice " published by Glacier and Cat .

Articles are included on GitHub and Gitee:

GitHub: https://github.com/sunshinelyz/technology-binghe

Gitee: https://gitee.com/binghe001/technology-binghe

Overview of Spring transactions not taking effect

In short, Spring transactions fail in several specific scenarios, as shown in the following figure.

picture

The database does not support transactions

The prerequisite for Spring transactions to take effect is that the connected database supports transactions. If the underlying database does not support transactions, Spring transactions will definitely fail. For example, if the database used is MySQL and the MyISAM storage engine is selected, Spring's transactions will fail.

Transaction methods are not managed by Spring

If the class in which the transaction method is located is not loaded into the Spring IOC container, that is, the class in which the transaction method is located is not managed by Spring, the Spring transaction will fail. The 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);
 }
}

There is no @Service annotation on the ProductService class, and the instance of Product is not loaded into the Spring IOC container, which will cause the transaction of the updateProductStockCountById() method to fail in Spring.

The method is not modified by public

If the method in which the transaction is located is not modified by public, then Spring's transaction will be invalid, 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 ProductService is marked with the @Service annotation, the updateProductStockCountById() method is marked with the @Transactional(propagation = Propagation.REQUIRES_NEW) annotation.

However, since the updateProductStockCountById() method is an internal private method (modified with private), the transaction of the updateProductStockCountById() method will be invalid in Spring at this time.

Method calls in the same class

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

@Service
public class OrderService {
    
    

 @Autowired
 private OrderDao orderDao;

 @Autowired
 private ProductDao productDao;

 public void submitOrder(){
    
    
  //生成订单
  Order order = new Order();
  long number = Math.abs(new Random().nextInt(500));
  order.setId(number);
  order.setOrderNo("order_" + number);
  orderDao.saveOrder(order);
  //减库存
  this.updateProductStockCountById(1, 1L);
 }

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

The submitOrder() method and the updateProductStockCountById() method are both in the OrderService class. The submitOrder() method is not marked with transaction annotations, and the updateProductStockCountById() method is marked with transaction annotations. The submitOrder() method calls the updateProductStockCountById() method. At this time, updateProductStockCountById () method transactions are invalidated in Spring.

Transaction manager not configured

If Spring's transaction manager is not configured in the project, even if Spring's transaction management function is used, Spring's transaction 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's transaction will be invalid.

The transaction propagation type of the method does not support transactions

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

For example, as shown in the following code.

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

 @Transactional(propagation = Propagation.REQUIRED)
 public void submitOrder(){
    
    
  //生成订单
  Order order = new Order();
  long number = Math.abs(new Random().nextInt(500));
  order.setId(number);
  order.setOrderNo("order_" + number);
  orderDao.saveOrder(order);
  //减库存
  this.updateProductStockCountById(1, 1L);
 }

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

Since the transaction propagation type of the updateProductStockCountById() method is NOT_SUPPORTED and does not support transactions, the transaction of the updateProductStockCountById() method will be invalid in Spring.

Incorrectly caught exception

Incorrectly caught exceptions can also cause Spring's transactions to fail, as shown below.

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


 @Transactional(propagation = Propagation.REQUIRED)
 public void submitOrder(){
    
    
  //生成订单
  Order order = new Order();
  long number = Math.abs(new Random().nextInt(500));
  order.setId(number);
  order.setOrderNo("order_" + number);
  orderDao.saveOrder(order);
  //减库存
  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("扣减库存异常:", e.getMesaage());
  }
 }
}

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

Wrong annotation exception type

If the wrong exception type is marked in the @Transactional annotation, the rollback of the Spring transaction will be invalid. The example is as follows.

@Transactional(propagation = Propagation.REQUIRED)
public void updateProductStockCountById(Integer stockCount, Long id){
    
    
 try{
    
    
  productDao.updateProductStockCountById(stockCount, id);
 }catch(Exception e){
    
    
  logger.error("扣减库存异常:", e.getMesaage());
  throw new Exception("扣减库存异常");
 }
}

The 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 the updateProductStockCountById() method will be invalid.

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

By default, the Exception exception cannot be caught in the Spring transaction, so the rollback of the transaction in the updateProductStockCountById() method will be invalid.

At this point, 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.

Okay, let's stop here today, I'm Glacier, see you next time~~

write at the end

If you want to enter a big factory, want to get a promotion and a salary increase, or are confused about your current job, you can privately message me to communicate, I hope some of my experiences can help you~~

Recommended reading:

Okay, let's stop here today, friends, like, favorite, comment, and start walking with one click, I'm Glacier, see you in the next issue~~

Guess you like

Origin blog.csdn.net/l1028386804/article/details/124471028