Transactions in Spring
1. Business Review
Transaction: a logical set of operations, either all successful or all failed. Transaction characteristics: ACID
Atomicity: transaction indivisibility consistency: data integrity is consistent before and after transaction execution. Isolation: when a transaction is executed, it should not be disturbed by other transactions. Persistence: once it is completed, the data will be permanently saved to the database
If isolation is not considered: dirty reading: when a transaction reads uncommitted data from another transaction, it cannot be read repeatedly: when a transaction reads that another transaction has committed data (update), the results of multiple queries of a transaction are inconsistent. Virtual reading: when a transaction reads that another transaction has committed data (insert), the results of multiple queries of a transaction are inconsistent
Transaction isolation level: uncommitted read: all of the above can occur. Committed reads: avoid dirty reads, but do not repeat them. Virtual reads are possible. Repeatable reading: avoid dirty reading and non repeatable reading, but virtual reading may occur. Serial: avoid all of the above
2. Transactions in spring
Hierarchical development of transaction management in Spring: transactions are in the Service layer
Spring provides transaction management API s
Platform transaction manager: platform transaction manager
getTransaction(TransactionDefinition definition)
rollback(TransactionStatus status)
commit(TransactionStatus status)
TransactionDefinition: transaction definition
ISOLation_ 30: Transaction isolation level
PROPAGATION_ 30: The propagation of transactions
TransactionStatus: transaction status , whether there is a save point , whether it is a new transaction , whether the transaction has been committed , relationship: PlatformTransactionManager sets transaction related information through TransactionDefinition to manage transactions. During the process of managing transactions, some transaction statuses are generated, and the statuses are recorded by TransactionStatus.
API details: PlatformTransactionManager: interface. Spring provides different PlatformTransactionManager interface implementations for different persistence frameworks
When using Spring JDBC or iBatis to persist data, use (key) org.springframework.jdbc.datasource.DataSourceTransactionManager
When using Hibernate to persist data, use} org.springframework.orm.Hibernate.hibernatetransaction manager
Used when using JPA for persistence
org.springframework.orm.jpa.JpaTransactionManager
Used when the persistence mechanism is Jdo
org.springframework.jdo.JdoTransactionManager
Use a JTA implementation to manage transactions. When a transaction spans multiple resources, you must use {org.springframework.transaction.jta.jtatatransactionmanager
TransactionDefinition: ISOLATION_DEFAULT: default level
Mysql --> repeatable_read | Oracle -->> read_commited
The levels are as follows:
ISOLATION_READ_UNCOMMITTED
ISOLATION_READ_COMMITTED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE
3. Propagation behavior of transactions in spring
Transaction propagation behavior: (not JDBC transaction management, used to solve actual development problems.) propagation behavior: solve the relationship between invoked transactions between business layers
PROPAGATION_REQUIRED: supports the current transaction. If it does not exist, create a new one
-
A. B if a has a transaction, B uses the transaction of A. if a has no transaction, B starts a new transaction. (A and B are in a transaction.)
PROPAGATION_SUPPORTS: supports the current transaction. If it does not exist, it will not be used
-
A. B if a has a transaction, B uses a's transaction. If a has no transaction, B does not use a transaction
PROPAGATION_MANDATORY: supports the current transaction. If it does not exist, an exception will be thrown
-
A. B if a has a transaction, B uses a's transaction. If a has no transaction, an exception is thrown
PROPAGATION_REQUIRES_NEW: if a transaction exists, suspend the current transaction and create a new transaction
-
A. B if a has a transaction, B suspends a's transaction and creates a new transaction. (A and B are not in a transaction. Transactions do not affect each other.)
PROPAGATION_NOT_SUPPORTED: runs in a non transactional manner. If a transaction exists, suspend the current transaction
-
A. B runs in a non - transactional manner. If a has a transaction, it will suspend the current transaction
PROPAGATION_NEVER: run in non transaction mode. If there is a transaction, throw an exception PROPAGATION_NESTED: if the current transaction exists, the nested transaction is executed
-
Based on SavePoint technology
A. B a has a transaction. After a executes, save the content after a transaction execution to SavePoint.B. if the transaction is abnormal, the user needs to set whether the transaction is committed or rolled back
-
Common: (key) promotion_ REQUIRED PROPAGATION_ REQUIRES_ NEW PROPAGATION_ NESTED
4. Transaction management in spring
Spring's transaction management is divided into two types: programmatic transaction management: manually writing code to complete transaction management. (understand) declarative transaction management: no need to manually write code and configure
5.Spring transaction case
Simulate the problem of transfer between two people. When we do not join the transaction operation, if the transfer out operation is executed, an exception is thrown between the transfer out operation and the transfer in operation. As we all know, the program will stop when it encounters an exception, so the result is that this person transfers the money and the other person does not receive the money. There is a big bug, so we introduce the transaction operation
5.1 complete transaction management by manual coding (understand)
Disadvantages: the amount of code increases and the code is invasive
(1) Create database and money table
(2) Create db.properties file
db.driverClassName = com.mysql.cj.jdbc.Driver db.url = jdbc:mysql://localhost:3306/javacoffee?serverTimezone=Asia/Shanghai&characterEncoding=UTF8 db.username=root db.password=1234
(3) Import dependencies, I will directly copy them here, including the packages required below
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.10</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency> </dependencies>
(4) Create mapper and implementation classes and define methods to transfer out money and increase money
package com.coffee.mapper; public interface Money { public void setMoney(String money,Integer id); public void getMoney(String money,Integer id); }
package com.coffee.mapper.impl; import com.coffee.mapper.Money; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class MoneyImpl extends JdbcDaoSupport implements Money { @Override public void setMoney(String money,Integer id) { String sql = "update money set money = money - ? where id = ?"; getJdbcTemplate().update(sql,money,id); } @Override public void getMoney(String money,Integer id) { String sql = "update money set money = money + ? where id = ?"; getJdbcTemplate().update(sql,money,id); } }
(5) Create a service and its implementation class
package com.coffee.service; import java.sql.SQLException; public interface MoneyService { public void money(String money,Integer id1,Integer id2) throws SQLException; }
In the implementation class, the transaction template object is used
package com.coffee.service.impl; import com.coffee.mapper.impl.MoneyImpl; import com.coffee.service.MoneyService; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; public class MoneyServiceImpl implements MoneyService { private MoneyImpl moneyImpl; private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } public void setMoneyImpl(MoneyImpl moneyImpl) { this.moneyImpl = moneyImpl; } @Override public void money(String money, Integer id1, Integer id2) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { moneyImpl.setMoney(money,id1); // You can write an exception here to see if the transaction rollback is executed // int sb= 1/0; moneyImpl.getMoney(money,id2); } }); } }
(6) Complete dependency injection in applicationContext.xml file
<?xml version="1.0" encoding="UTF-8"?> <beans xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"> <!-- Scan the file to obtain the database properties stored in the file--> <context:property-placeholder location="classpath:db.properties"></context:property-placeholder> <!-- Inject four attributes to obtain the channel database connection object datasource--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${db.username}"></property> <property name="password" value="${db.password}"></property> <property name="url" value="${db.url}"></property> <property name="driverClassName" value="${db.driverClassName}"></property> </bean> <!-- The transaction core manager, which encapsulates all transaction operations, is very important--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- Transaction template object--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="dataSourceTransactionManager"></property> </bean> <!-- mapper Layer, using JdbcTemplate To modify, so it needs to be passed in datasource--> <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- service Layer, will mapper Dependency injection is performed on the objects obtained in the layer, as well as the transaction template object--> <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl"> <property name="moneyImpl" ref="moneyImpl"></property> <property name="transactionTemplate" ref="transactionTemplate"></property> </bean> </beans>
(7) Test class
import com.coffee.service.MoneyService; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.sql.SQLException; public class MyTest { @Test public void test(){ ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); MoneyService money2 = (MoneyService)classPathXmlApplicationContext.getBean("moneyService"); try { //The three parameters are the money transferred, the id of the account transferred out, and the id of the account transferred in money2.money("100",1,2); } catch (SQLException e) { e.printStackTrace(); } } }
Both of them had 1000 yuan. If the account id is 1, 100 yuan will be transferred to the account id is 2
give the result as follows
When an exception occurs immediately after the transfer out, the transaction rollback will be executed. If an exception occurs between the two methods
After executing the console output statement:
The database is:
You can see that the balance of the two accounts has not changed
5.7 XML configuration (aop) to complete transaction management
Modify AccountServiceImpl
package com.coffee.service.impl; import com.coffee.mapper.impl.MoneyImpl; import com.coffee.service.MoneyService; public class MoneyServiceImpl implements MoneyService { private MoneyImpl moneyImpl; public void setMoneyImpl(MoneyImpl moneyImpl) { this.moneyImpl = moneyImpl; } @Override public void money(String money, Integer id1, Integer id2) { { moneyImpl.setMoney(money,id1); // You can write an exception here to see if the transaction rollback is executed int sb= 1/0; moneyImpl.getMoney(money,id2); } } }
Modify applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"> <!-- Scan the file to obtain the database properties stored in the file--> <context:property-placeholder location="classpath:db.properties"></context:property-placeholder> <!-- Inject four attributes to obtain the channel database connection object datasource--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${db.username}"></property> <property name="password" value="${db.password}"></property> <property name="url" value="${db.url}"></property> <property name="driverClassName" value="${db.driverClassName}"></property> </bean> <!-- The transaction core manager, which encapsulates all transaction operations, is very important--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--Configure transaction notifications--> <!-- <!– Configuration in Enterprise CRUD Methods generally use method names+wildcard*The method name in the class should be consistent with the configured method name –>--> <!-- <!– In method,Specifies what transaction properties the method applies--> <!-- isolation: Specifies the isolation level of the transaction. The default value is DEFAULT,Indicates that the default isolation level of the database is used.--> <!-- propagation: Specifies the propagation behavior of the transaction. The default value is REQUIRED,It means that there will be transactions, adding, deleting and modifying options. The query method can be selected SUPPORTS. --> <!-- read-only: Specifies whether the transaction is read-only. Only query methods can be set to true. The default value is false,Indicates reading and writing.--> <!-- timeout: Used to specify the timeout of a transaction. The default value is-1,Means never timeout. If a value is specified, in seconds.--> <!-- rollback-for: Used to specify an exception. When the exception is generated, the transaction will be rolled back. When other exceptions are generated, the transaction will not be rolled back. There is no default value. Indicates that any exceptions are 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 exceptions are rolled back.--> <!-- –>--> <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager"> <tx:attributes> <tx:method name="money" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="false"></tx:method> </tx:attributes> </tx:advice> <!-- Transaction template object--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="dataSourceTransactionManager"></property> </bean> <!-- mapper Layer, using JdbcTemplate To modify, so it needs to be passed in datasource--> <!-- Configuration weaving--> <aop:config> <!-- Configure pointcuts--> <aop:pointcut id="pc" expression="execution(* com.coffee.service.impl.MoneyServiceImpl.money(..))"/> <!-- Configure section--> <!-- Configure section : notice+Tangent point advice-ref:Name of the notification pointcut-ref:Name of tangent point --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor> </aop:config> <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- service Layer, will mapper Dependency injection is performed on the objects obtained in the layer, as well as the transaction template object--> <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl"> <property name="moneyImpl" ref="moneyImpl"></property> </bean> </beans>
5.8 complete transaction management by annotation configuration (aop)
Modify applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"> <!-- Scan the file to obtain the database properties stored in the file--> <context:property-placeholder location="classpath:db.properties"></context:property-placeholder> <!-- Inject four attributes to obtain the channel database connection object datasource--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${db.username}"></property> <property name="password" value="${db.password}"></property> <property name="url" value="${db.url}"></property> <property name="driverClassName" value="${db.driverClassName}"></property> </bean> <!-- The transaction core manager, which encapsulates all transaction operations, is very important--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- Open annotation transaction--> <tx:annotation-driven/> <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- service Layer, will mapper Dependency injection is performed on the objects obtained in the layer, as well as the transaction template object--> <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl"> <property name="moneyImpl" ref="moneyImpl"></property> </bean> </beans>
Modify AccountServiceImpl
package com.coffee.service.impl; import com.coffee.mapper.impl.MoneyImpl; import com.coffee.service.MoneyService; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class MoneyServiceImpl implements MoneyService { private MoneyImpl moneyImpl; public void setMoneyImpl(MoneyImpl moneyImpl) { this.moneyImpl = moneyImpl; } @Override //Annotations can be directly represented on the class, but if the method is different from the configuration on the class name, annotations can also be configured on this method alone @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false) public void money(String money, Integer id1, Integer id2) { { moneyImpl.setMoney(money,id1); // You can write an exception here to see if the transaction rollback is executed int sb= 1/0; moneyImpl.getMoney(money,id2); } } }