Implementation of Spring transaction

I don't know if you've ever been bothered by spring's transactions, transaction propagation behavior, transaction isolation level, aop concepts. Do you want to surrender? This article will take you to ravage these difficult concepts and break through the final understanding of things.

Spring provides a transaction management interface. PlatformTransactionManager provides common transaction operation methods:


public interface PlatformTransactionManager {

//Get transaction status information
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
/ / commit transaction
    void commit(TransactionStatus var1) throws TransactionException;
/ / rollback transaction
    void rollback(TransactionStatus var1) throws TransactionException;
}


The platform transaction manager interface defines the basic methods of submitting, rolling back and other operations on things.

There are two interfaces:

1: TransactionDefinition

It defines the basic properties of things

public interface TransactionDefinition {

/ / returns the propagation behavior of things

int getPropagationBehavior();
//Returns the isolation level of the transaction, according to which the transaction manager controls which data in this transaction can be seen by another transaction.

int getIsolationLevel();
//How many seconds must the return transaction complete
  int getTimeout();
//Whether the transaction is read-only or not, the transaction manager can optimize according to the return value to ensure that the transaction is read-only.
  boolean isReadOnly();

}

The propagation behavior of things indicates how things propagate when a method is called by another method.

 

I have an article that explains the communication behavior of things in detail:

 

The isolation level of things I have detailed in this article:

 

getTimeout() returns the timeout of a thing. The execution of the thing will lock the table in different degrees. If the time is too long, the operation of other things on the table will be affected. After the execution timeout, the thing will roll back.

isReadOnly() returns whether the object is read-only. If the object is only to read the database, we can set it as read-only, and the database can be optimized accordingly to improve the execution efficiency.

 

2: TransactionStatus

This interface provides the specific state of things running

public interface TransactionStatus extends SavepointManager, Flushable {
     boolean isNewTransaction();//Is it new
     boolean hasSavepoint();//Whether there is recovery point
     void setRollbackOnly();//Set to rollback only
     boolean isRollbackOnly();//Rollback only or not
     boolean isCompleted();//Completed or not
 }
The state of these things is required as a parameter when rolling back or committing operations.
PlatformTransactionManager is just an interface for business management. What we really want to use is the implementation class of the interface. If you use Jdbc or mybatis to operate the database, you need to use the transaction management class:
org.springframework.jdbc.datasource.DataSourceTransactionManager
If you use Hibernate, you need to use the transaction management class:
org.springframework.orm.hibernate5.HibernateTransactionManager
 
There are usually two ways to implement things in spring:
1:  Declarative transaction control based on XML (configuration mode):
This is illustrated by an example of a transfer:


Database:

                                             

Step 1: import dependency

<dependencies>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>5.0.2.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>5.0.2.RELEASE</version>
     </dependency>
         <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.46</version>
 </dependency>
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>5.0.2.RELEASE</version>
 </dependency>
<dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
     </dependency>
     <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.8.7</version>
     </dependency>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-tx</artifactId>
         <version>5.0.2.RELEASE</version>
     </dependency>
 </dependencies>
 
Spring TX is the dependency of things management. Because the bottom layer of things Management in spring uses aop, we need to import the dependency aspectjweaver of aop.
Step 2: create a configuration file for spring and import constraints
<?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
 </beans>
Step 3: create Account entity class
Step 4: write Dao interface and implementation class


public interface IAccountDao {
//Query account information by name
Account findAccountByName(String name);
//Update account information
void updateAccount(Account account);

}

 

 

public class AccountDaoImpl implements IAccountDao {
     @Autowired
     private JdbcTemplate jt;
     public Account findAccountByName(String name) {
         List<Account> lst = jt.query("select * from account where name like ?",new BeanPropertyRowMapper<Account>(Account.class),name);
         return lst.isEmpty()?null:lst.get(0);
     }
     public void updateAccount(Account account) {
         jt.update("update account set money = ? where id = ? ",account.getMoney(),account.getId());
     }
 }
Step 5: write business layer interface and implementation class
public interface IAccountService {
 * Transfer accounts
 * @param sourceName Name of transfer out account
 * @param targeName Name of transfer in account
 * @param money Transfer amount
 */
 void transfer(String sourceName,String targeName,Float money);//Additions and deletions
}
 
/**
 * Business layer implementation class of account
 */
 public class AccountServiceImpl implements IAccountService {
     private IAccountDao accountDao;
         public void transfer(String sourceName, String targeName, Float money) {
         //1. Query two accounts by name
         Account source = accountDao.findAccountByName(sourceName);
         Account target = accountDao.findAccountByName(targeName);
        //2. Modify the amount of two accounts
         source.setMoney(source.getMoney()-money);//Decrease in transfer out account
         target.setMoney(target.getMoney()+money);//Add money to transfer in account
        //3. Update two accounts
         accountDao.updateAccount(source);
         int i=1/0;
         accountDao.updateAccount(target);
     }
 }
Step 6: configure the business layer and persistence layer in the configuration file
<context:component-scan base-package="cn.xh"></context:component-scan>
     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
         <property name="dataSource" ref="dataSource"></property>
     </bean>
     <!-- To configure service -->
     <bean id="accountService" class="cn.xh.service.AccountServiceImpl">
 
     </bean>
     <!-- To configure dao -->
     <bean id="accountDao" class="cn.xh.dao.AccountDaoImpl">
 
     </bean>
     <!-- Configure data sources -->
     <bean id="dataSource"
           class="org.springframework.jdbc.datasource.DriverManagerDataSource">
         <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
         <property name="url" value="jdbc:mysql:///spring_demo01"></property>
         <property name="username" value="root"></property>
         <property name="password" value="123456"></property>
</bean>


Step 7: configure transactions

Spring transactions are based on aop. I have a detailed explanation of aop in this article:

 

 

 

Configuration steps:

1: Configure transaction manager:

<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<! -- inject datasource -- >
<property name="dataSource" ref="dataSource"></property>
</bean>

2: To configure the notification reference transaction manager for a transaction:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
</tx:advice>

 

3: To configure the properties of a transaction:

<! -- configure notifications for transactions -- >
 <tx:advice id="txAdvice" transaction-manager="transactionManager">
<! -- configure the properties of the transaction
 Isolation: Specifies the isolation level of the transaction. The DEFAULT value is DEFAULT, which means the DEFAULT isolation level of the database is used.
Propagation: used to specify the propagation behavior of a transaction.
Read only: used to specify whether the transaction is read-only. Only query methods can be set to true. The default value is false, which means read and write.
Timeout: used to specify the timeout of the transaction. The default value is - 1, which means never timeout. If a value is specified, in seconds.
Rollback for: used to specify an exception. When the exception is generated, the transaction is rolled back. When other exceptions are generated, the transaction is not rolled back. There is no default value. Indicates that any exception is rolled back.
No rollback for: used to specify an exception. When the exception is generated, the transaction will not be rolled back. When other exceptions are generated, the transaction will be rolled back. There is no default value. Indicates that any exception is rolled back.
     -->
     <tx:attributes>
         <tx:method name="*" propagation="REQUIRED" read-only="false"/>
         <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
     </tx:attributes>
 </tx:advice>


 

4: To configure AOP pointcut expressions:

<! -- configure AOP -- >
 <aop:config>
<! -- configure pointcut expressions -- >
     <aop:pointcut id="pt1" expression="execution(* cn.xh.service.*.*(..))"></aop:pointcut>
<! -- establish correspondence between pointcut expression and transaction notification -- >
     <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
 </aop:config>


 

Let's look at this configuration:

<! -- configure pointcut expressions -- >
    <aop:pointcut id="pt1" expression="execution(* cn.xh.service.*.*(..))"></aop:pointcut>

Describes adding transactions to all methods of all classes under cn.xh.service.

Take a look at this configuration:

<! -- configure notifications for transactions -- >
 <tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
         <tx:method name="*" propagation="REQUIRED" read-only="false"/>
         <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
     </tx:attributes>
 </tx:advice>


This configuration indicates that the propagation mode of the method transaction starting with find is SUPPORTS, which is read-only. The propagation mode of other method transactions is REQUIRED, which is not read-only.

 

Now you can test it:

@RunWith(SpringJUnit4Cla***unner.class)
 @ContextConfiguration("classpath:applicationContext.xml")
 public class serviceTest {
     @Autowired
     private IAccountService serviceImpl;
     @Test
     public void testDo(){
         serviceImpl.transfer("%Zhang%","%Plum%",1000f);
     }
 }
Transfer 1000 from Zhang San's account to Li Si's account. Due to the error of division by zero, the transaction is rolled back. Zhang San's account is still 2000, and Li Si's account is still 3000
 
Let's look at the second way of transaction implementation
2:  Annotation based configuration


Let's focus on the annotation configuration of transactions. For others, see the above.

Configuration steps:

Step 1: configure the transaction manager and inject the data source

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

Step 2: use @ Transactional annotation in the business layer:

@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements IAccountService {
      @Autowired
      private IAccountDao accountDao;
      @Override
      public Account findAccountById(Integer id) {
              return accountDao.findAccountById(id);
      }


      @Override
      @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
      public void transfer(String sourceName, String targeName, Float money) {
/ / 1. Query two accounts by name
             Account source = accountDao.findAccountByName(sourceName);
             Account target = accountDao.findAccountByName(targeName);
/ / 2. Modify the amount of two accounts
source.setMoney(source.getMoney()-money); / / transfer out account to reduce money
target.setMoney(target.getMoney()+money); / / add money to transfer in account
/ / 3. Update two accounts
            accountDao.updateAccount(source);
           //int i=1/0;
           accountDao.updateAccount(target);

}
}
The attribute meaning of the annotation is consistent with that of xml. The annotation can appear on the interface, class and method.
On the interface, all implementation classes of the interface have transaction support.
Appears on the class, indicating that all methods in the class have transaction support
Appears on the method, indicating that the method has transaction support.
Priority of the above three positions: method > class > interface

Step 3: enable spring's support for annotation transactions in the configuration file:
<tx:annotation-driven transaction-manager="transactionManager"/>


Tags: Java Spring JDBC Database xml

Posted on Mon, 03 Feb 2020 01:10:53 -0800 by nodster