AOP-based declarative transaction control

1. Overview of Spring transaction programming

Transaction is an indispensable thing in development. When using JDBC development, we use connnection to control transactions. When using MyBatis, we use SqlSession to control transactions. The disadvantages are obvious. When we switch database access technologies, the way of transaction control will always change. Spring provides an interface for unified control of transactions based on these technologies. Spring's transactions are divided into: programmatic transaction control and declarative transaction control.

insert image description here

There are three main classes related to Spring transaction programming:

insert image description here

Although we don't learn programmatic transaction control, we need to understand the classes corresponding to programmatic transaction control, because we will also see the shadow of these classes when we perform declarative transaction control through configuration.

2. Build a test environment

To build a transfer environment, the dao layer has a method for transferring money out, a method for transferring money in, and the service layer has a method for transferring money, and the method for transferring money out and transferring money in the dao layer is called internally. The preparation work is as follows: ⚫
Database Prepare an account table tb_account;
⚫ Dao layer prepares an AccountMapper, including two methods of incrMoney and decrMoney;
⚫ Service layer prepares a transferMoney method, which calls incrMoney and decrMoney methods respectively;
⚫ Perform Bean management configuration in the applicationContext file;
⚫ The test is normal Transfers and abnormal transfers.

3. XML-based declarative transaction control

Combining the AOP technology we learned above, it is easy to imagine that AOP can be used to enhance the transaction of the Service method.
⚫ Target class: AccountServiceImpl
⚫ Point cut: all business methods in the service business class
⚫ Notification class: provided by Spring, the notification method has been defined, just need to configure

We analyze:
⚫ The notification class is provided by Spring, and the relevant coordinates of the Spring transaction need to be imported;
⚫ Configure the target class AccountServiceImpl;
⚫ Use the advisor tag to configure the aspect.

Import the relevant coordinates of Spring transactions, spring-jdbc coordinates have introduced spring-tx coordinates

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>5.2.13.RELEASE</version>
</dependency>

insert image description here

Configure the target class AccountServiceImpl

<bean id="accountService" class="com.itheima.service.impl.AccoutServiceImpl">
	<property name="accountMapper" ref="accountMapper"></property>
</bean>

Configure aspects using the advisor tag

<aop:config>
	<aop:advisor advice-ref="Spring提供的通知类" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:config>

Question: Who is the notification class provided by Spring? It is provided by the advice tag configuration under the spring-tx package

xmlns:tx="http://www.springframework.org/schema/tx" 
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/springtx.xsd

<!--Spring提供的事务通知-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="transferMoney"/>
	</tx:attributes>
</tx:advice>

<!--平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

<aop:config>
	<aop:advisor advice-ref="myAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"/>
</aop:config>

Let's explain the above configuration in detail:

First of all, the platform transaction manager PlatformTransactionManager is a standard interface provided by Spring to encapsulate the specific operation of the transaction, and encapsulates the commit and rollback methods of the transaction

public interface PlatformTransactionManager extends TransactionManager {
    
    
	TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
	void commit(TransactionStatus var1) throws TransactionException;
	void rollback(TransactionStatus var1) throws TransactionException;
}

Different persistence layer frameworks may operate in different ways, so different persistence layer frameworks may have different platform transaction manager implementations.
For example: when MyBatis is used as the persistence layer framework, the platform transaction manager implementation used is DataSourceTransactionManager.
When Hibernate is used as the persistence layer framework, the platform transaction manager used is HibernateTransactionManager.

Secondly, transaction definition information configuration, each transaction has many characteristics, such as: isolation level, read-only status, timeout time, etc., these information can be specified through connection during development, and here it is configured through configuration files.

<tx:attributes>
	<tx:method name="方法名称"
			isolation="隔离级别"
			propagation="传播行为"
			read-only="只读状态"
			timeout="超时时间"/>
</tx:attributes>

Among them, the name attribute name specifies which method will perform attribute configuration of which transactions. What needs to be distinguished here is the difference between the method specified by the pointcut expression and the method specified here? The pointcut expression is to filter which methods can perform transaction enhancement; the name of the transaction attribute information is to specify which method to configure which transaction attributes.

insert image description here

When configuring the method name, you can also use * for fuzzy matching, for example:

<tx:advice id="myAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<!--精确匹配transferMoney方法-->
		<tx:method name="transferMoney"/>
		<!--模糊匹配以Service结尾的方法-->
		<tx:method name="*Service"/>
		<!--模糊匹配以insert开头的方法-->
		<tx:method name="insert*"/>
		<!--模糊匹配以update开头的方法-->
		<tx:method name="update*"/>
		<!--模糊匹配任意方法,一般放到最后作为保底匹配-->
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

isolation attribute: Specifies the isolation level of the transaction. There are three major problems in transaction concurrency: dirty reads, non-repeatable reads, and phantom/virtual reads. The occurrence of concurrency problems can be guaranteed by setting the isolation level of the transaction, commonly used are READ_COMMITTED and REPEATABLE_READ.

insert image description here

read-only attribute: Set the current read-only status. If it is a query, set it to true, which can improve query performance. If it is an update (addition, deletion, modification) operation, set it to false.

<!-- 一般查询相关的业务操作都会设置为只读模式 -->
<tx:method name="select*" read-only="true"/>
<tx:method name="find*" read-only="true"/>

timeout attribute: Set the timeout time for transaction execution, in seconds. If the time limit is exceeded but the transaction has not been completed, the transaction will be automatically rolled back and will not continue to execute. The default value is -1, that is, there is no timeout limit.

<!-- 设置查询操作的超时时间是3秒 -->
<tx:method name="select*" read-only="true" timeout="3"/>

propagation attribute: Set the propagation behavior of the transaction, mainly to solve the problem of the propagation method of the transaction when the A method calls the B method. For example: use a unilateral transaction, or both A and B use their own transaction, etc. The propagation behavior of a transaction has the following seven property values ​​that can be configured:

insert image description here
insert image description here

Let’s analyze the principle of declarative transaction control in xml mode: The
namespace processor used by the <tx:advice> tag is TxNamespaceHandler, and the parser registered internally is
TxAdviceBeanDefinitionParser

this.registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());

The BeanDefinition to be registered is specified in TxAdviceBeanDefinitionParser

protected Class<?> getBeanClass(Element element) {
    
    
	return TransactionInterceptor.class;
}

The parse method of the TxAdviceBeanDefinitionParser secondary parent class AbstractBeanDefinitionParser registers the TransactionInterceptor with the configured name in the Spring container

parserContext.registerComponent(componentDefinition);

The invoke method in TransactionInterceptor will be executed, track the invoke method, and finally you will see the opening and submission of the transaction
⚫ The transaction opened in line 132 of AbstractPlatformTransactionManager;
⚫ The transaction is committed in line 242 of TransactionAspectSupport.

4. Annotation-based declarative transaction control

Annotations are an alternative to xml

@Service("accountService")
public class AccoutServiceImpl implements AccountService {
    
    
	@Autowired
	private AccountMapper accountMapper;
	
	// <tx:method name="*" isolation="REPEATABLE_READ" propagation="REQUIRED“/>
	@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false,timeout = 5)
	public void transferMoney(String decrAccountName, String incrAccountName, int money) {
    
    
		accountMapper.decrMoney(decrAccountName,money); //转出钱
		int i = 1/0; //模拟某些逻辑产生的异常
		accountMapper.incrMoney(incrAccountName,money); //转入钱
	}
}

Similarly, the platform transaction manager still needs to be configured for the transaction annotation used, and the transaction annotation switch needs to be turned on.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

<!--配置事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>

If you use full annotations, use the following configuration class instead of the configuration file:

@Configuration
@ComponentScan("com.itheima.service")
@PropertySource("classpath:jdbc.properties")
@MapperScan("com.itheima.mapper")
@EnableTransactionManagement
public class ApplicationContextConfig {
    
    
@Bean
public PlatformTransactionManager tansactionManager(DataSource dataSource){
    
    
	DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
	transactionManager.setDataSource(dataSource);
	return transactionManager;
}
	// ... 省略其他配置 ...
}

Guess you like

Origin blog.csdn.net/qq_36602071/article/details/129953523