Home  >  Article  >  Java  >  Spring transaction management operation method

Spring transaction management operation method

PHP中文网
PHP中文网Original
2017-06-20 16:20:291739browse

Question, I did add the annotation @Transactional to the service as usual. Why do I still find data inconsistencies when querying the database? I think it must be that the transaction did not work and an exception occurred. The data is not rolled back. So I tested the relevant code, and found that I had stepped into two pits. It was indeed the data inconsistency caused by the transaction not being rolled back. Let’s summarize the lessons learned:

Spring transaction management operation method

  • Programmatic transaction management

    • Rarely used in actual applications

    • Manually manage transactions by using TransactionTemplate

  • ##Declarative Transaction management

    • Recommended for development (minimum code intrusion)

    • Spring’s declarative transaction is implemented through AOP

#Mainly master declarative transaction management.

Two reasons why spring transactions are not rolled back

To summarize the two reasons why transactions are not rolled back, one is the internal method call of the Service class, and the other is the try...catch exception.

1. Service class internal method call
Probably there is a method A in Service, which will call method B internally. Method A has no transaction management, and method B uses declarative transactions. Declare Transactional annotation on the method for transaction management. The sample code is as follows:

@Servicepublic class RabbitServiceImpl implements RabbitService {

    @Autowiredprivate RabbitDao rabbitDao;
    @Autowiredprivate TortoiseDao tortoiseDao;

    @Overridepublic Rabbit methodA(String name){return methodB(name);
    }

    @Transactional(propagation = Propagation.REQUIRED)public boolean methodB(String name){
        rabbitDao.insertRabbit(name);
        tortoiseDao.insertTortoise(name);return true;
    }

}
The unit test code is as follows:

public class RabbitServiceImplTest {

    @Autowiredprivate RabbitService rabbitService;// 事务未开启    @Testpublic void testA(){
        rabbitService.methodA("rabbit");
    }// 事务开启    @Testpublic void testB(){
        rabbitService.methodB("rabbit");
    }
}
As you can see from the previous section, declarative transactions are passed AOP dynamic proxy is implemented, which will generate a proxy class for transaction management, but the target class (service) itself cannot perceive the existence of the proxy class.

For methods annotated with @Transactional, when calling the method of the proxy class, the transaction will be started through the interceptor TransactionInterceptor, and then the method of the target class will be called. Finally, after the call is completed, the TransactionInterceptor will Submit or rollback a transaction, the general process is as follows:

Spring transaction management operation method

#Summary, when calling method B in method A, it is actually through the reference of "this", that is, directly calling method of the target class instead of the proxy class obtained through the Spring context, so the transaction will not be started.

2. try...catch exception
The database exception is processed in a piece of business logic, using the try...catch clause to capture the exception and throw a custom exception , this situation resulted in the transaction not being rolled back. The sample code is as follows:

@Transactional(propagation = Propagation.REQUIRED)public boolean methodB(String name) throws BizException {try {
        rabbitDao.insertRabbit(name);
        tortoiseDao.insertTortoise(name);
    } catch (Exception e) {throw new BizException(ReturnCode.EXCEPTION.code, ReturnCode.EXCEPTION.msg);
    }return true;
}
The definition of BizException is as follows:

public class BizException extends Exception {// 自定义异常}
in the above code When an exception occurs in a declarative transaction, the transaction will not be rolled back. Although I caught the exception in the code, I also threw an exception at the same time. Why was the transaction not rolled back? I guessed that the exception type was wrong, so I started to search for the reason, looked through Spring's official documentation, and found the answer. The following is translated from the Spring official website.

17.5.3 Rollback of declarative transactions
The previous section introduced how to set up Spring transactions, which are generally set in the Service layer code of your application. This section will introduce How to control transaction rollback in simple and popular declarative transactions.

The recommended transaction rollback method in the Spring FrameWork transaction framework is to throw an exception in the currently executing transaction context. If the exception is not handled, Spring FrameWork's transaction framework code will catch any unhandled exception when the exception is thrown up the call stack, and then decide whether to mark the transaction for rollback.

  • In the default configuration, the transaction framework code of Spring FrameWork will only mark transactions with

    runtime, unchecked exceptions as rollback; that is to say, throwing in the transaction The exception that occurs is RuntimeException or its subclass, so that the transaction will be rolled back (Error will also cause the transaction to be rolled back by default). With the default configuration, all checked exceptions will not cause transaction rollback.

Note: Unchecked Exception includes Error and RuntimeException. All subclasses of RuntimeException also belong to this category. The other type is checked Exception.

  • #You can precisely configure the exception type and specify transaction rollback for this exception type, including checked exceptions. The following XML code snippet shows how to configure checked exceptions to cause transaction rollback and apply a custom exception type:

<advice>
  <attributes>
  <method></method>
  <method></method>
  </attributes>
</advice>
The annotation form with the same effect is as follows:

@Transactional(rollbackForClassName={"Exception"})
或者
@Transactional(rollbackFor={Exception.class})
  • 在你遇到异常不想回滚事务的时候,同样的你也可指定不回滚的规则,下面的一个例子告诉你,即使遇到未处理的 InstrumentNotFoundException 异常时,Spring FrameWork 的事务框架同样会提交事务,而不回滚。

<advice>
  <attributes>
  <method></method>
  <method></method>
  </attributes>
</advice>

  与其有同样作用的注解形式如下:   

@Transactional(noRollbackForClassName={"InstrumentNotFoundException"})
或者
@Transactional(noRollbackFor={InstrumentNotFoundException.class})
  • 还有更灵活的回滚规则配置方法,同时指定什么异常回滚,什么异常不回滚。当Spring FrameWork 的事务框架捕获到一个异常的时候,会去匹配配置的回滚规则来决定是否标记回滚事务,使用匹配度最强的规则结果。因此,下面的配置例子表达的意思是,除了异常 InstrumentNotFoundException 之外的任何异常都会导致事务回滚。

<advice>
  <attributes>
  <method></method>
  </attributes>
</advice>
  • 你也可以通过编程式的方式回滚一个事务,尽管方法非常简单,但是也有非常强的代码侵入性,使你的业务代码和Spring FrameWork 的事务框架代码紧密的绑定在一起,示例代码如下:

public void resolvePosition() {  try {      // some business logic...
  } catch (NoProductInStockException ex) {      // trigger rollback programmatically      TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  }
}

  如果可能的话,强烈推荐您使用声明式事务方式回滚事务,对于编程式事务,如果你强烈需要它,也是可以使用的,but its usage flies in the face of achieving a clean POJO-based architecture.(没懂...)

看完官方文档这节内容找到了问题的答案,原来是因为我们自定义的异常不是 RuntimeException。我的解决办法是,在注解@Transactional中添加 rollbackFor={BizException.class}。可能你会问我为什么不将自定义异常修改为继承RuntimeException,因为我需要BizException是一个checked 异常。

The above is the detailed content of Spring transaction management operation method. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn