This is the third day of my participation in Gwen Challenge

This article is participating in “Java Theme Month – Java Development in Action”, see the activity link for details

preface

What is a transaction?

A transaction is a logical set of operations that either all succeed or all fail. #### transaction features:

  • atomicAtomicity refers to the fact that a transaction is an indivisible unit of work in which all or none of the operations occur.
  • consistencyConsistency means that data integrity must be consistent before and after a transaction.
  • Isolation,Isolation means that when multiple users concurrently access the database, the transactions of one user cannot be disturbed by the transactions of other users, and multiple concurrent transactions must be isolated from each other.
  • persistencePersistence means that once a transaction is committed, its changes to the data in the database are permanent and should not be affected even if the database fails.

Spring can be divided into two approaches to transaction management: programmatic and declarative

Programmatic transaction management

Example of transaction management based on the underlying API:

public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionDefinition txDefinition;
private PlatformTransactionManager txManager;
 
public boolean transfer(Long fromId, Long toId,double amount) {
  TransactionStatus txStatus = txManager.getTransaction(txDefinition);
  boolean result = false;
  try {
    result = bankDao.transfer(fromId, toId, amount);
    txManager.commit(txStatus);
  } catch (Exception e) {
    result = false;
    txManager.rollback(txStatus);
    System.out.println("Transfer Error!");
  }
  returnresult; }}Copy the code

Sample transaction management configuration file based on the underlying API:

<? xml version="1.0" encoding="utf-8"? > <bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
<property name="txManager" ref="transactionManager"/>
<property name="txDefinition">
<bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>
</property>
</bean>
Copy the code

As shown above, two properties have been added to the class: one of type TransactionDefinition, which defines a transaction; Another is the PlatformTransactionManager types of attributes, used to perform the transaction management operation.

If method requires implementing transaction management, we first need to start a transaction before method implemented, called PlatformTransactionManager. GetTransaction (…). Method to start a transaction. Once the transaction has been created and started, you can start coding the business logic and then perform the commit or rollback of the transaction where appropriate.

Programmatic transaction management based on TransactionTemplate

As you can see from the previous examples, this approach to transaction management is easy to understand, but the trouble is that the code for transaction management is scattered across the business logic code, breaking the original code structure, and each business method contains similar boilerplate code for starting and committing/rolling back transactions. Fortunately, Spring recognizes this and provides a simplified approach, which is the template callback pattern that Spring is very common in the data access layer.

txTemplate.execute(new TransactionCallbackWithoutResult() {
	@Override
	protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
		if(userNoticeBo.getUserContract() ! =null) {
			userContractMapper.insertAndUpdate(userNoticeBo.getUserContract());
			IousWhiteUser iousWhiteUser = iousWhiteUserMapper.selectByUserId(userNoticeBo.getUserContract().getUserId());
			if ((iousWhiteUser == null| |! UserInWhiteListEnum.IN_WHITE_LIST.getCode().equals(iousWhiteUser.getWhiteStatus())) && UserInWhiteListEnum.IN_WHITE_LIST.equals(userNoticeBo.getUserContract().getIsInWhiteList())){ iousWhiteUserMapper.insert(createInsertBody(userNoticeBo.getUserContract())); } } userInfoRecordMapper.insertSelective(userNoticeBo.getUserInfoRecord());if(userNoticeBo.getBusiNotice() ! =null) {
			busiNoticeMapper.insertSelective(userNoticeBo.getBusiNotice());
		}
		if(userNoticeBo.getExtNoticeInfo() ! =null){
			extNoticeInfoMapper.insertSelective(userNoticeBo.getExtNoticeInfo());
		}
		if(userNoticeBo.getTblCampaignAudience() ! =null){ audienceService.saveOrUpdateAudience(userNoticeBo.getTblCampaignAudience()); }}});Copy the code

Corresponding XML configuration:

<tx:annotation-driven transaction-manager="ctripcfbTxManager"/>
<bean id="ctripcfbTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <property name="dataSource" ref="dataSource"/> </bean> <! -- programmatic transaction --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
 <property name="transactionManager" ref="ctripcfbTxManager" />
</bean>
Copy the code

The execute() method of TransactionTemplate takes an argument of type TransactionCallback. This interface defines a doInTransaction() method, Typically we implement the TransactionCallback interface as an anonymous inner class and write the business logic code in its doInTransaction() method. The default transaction commit and rollback rules can be used here, which eliminates the need to explicitly call any transaction management apis in the business code. The doInTransaction() method has a parameter of type TransactionStatus, and we can call the setRollbackOnly() method of that parameter anywhere in the method to identify the transaction as rolled back to perform the transaction rollback.

According to the rules of the default, if in the process of implement the callback method throw unchecked exceptions, or explicitly call TransacationStatus. SetRollbackOnly () method, the rollback transaction; If the transaction completes or throws an exception of type Checked, the transaction is committed.

TransactionCallback interface is a subinterface TransactionCallbackWithoutResult, the interface defines a doInTransactionWithoutResult () method, TransactionCallbackWithoutResult interface is mainly used in the transaction process does not need to return a value. Of course, for cases where you don’t need to return a value, you can still use the TransactionCallback interface and just return any value in the method.

Declarative transaction management

Transactional Transactional Transactional management

Enable the configuration of post-processing beans

<tx:annotation-driven transaction-manager="transactionManager"/>
Copy the code

Similarly, the default value for the transaction-Manager property is transactionManager, which can be omitted if the transaction manager Bean name is that value.

Although the @Transactional annotation can be applied to interfaces, interface methods, classes, and class methods, the Spring team recommends not using the annotation on interfaces or interface methods because it only works when using interface-based proxies. In addition, the @Transactional annotation should only be applied to public methods, due to the nature of Spring AOP. If you use the @Transactional annotation ona protected, private, or default visibility method, this is ignored and no exceptions are raised.

conclusion

Declarative transaction management makes it easier to maintain pure business code without contaminate it. It is also recommended by Spring. The only disadvantage of declarative transactions compared to programmatic transactions is that the fineness of the latter applies only to the method level, not to the code block level as programmatic transactions can. But even if there is such a need, there are many workarounds, such as separating the blocks of code that need transaction management into methods.