Table of contents
Spring transaction_transaction introduction
Spring Transaction_Spring Transaction Management Solution
Spring Transaction_Spring Transaction Manager
Spring transaction_transaction control API
Related configuration of Spring transaction_transaction
Spring transaction _ transaction propagation behavior
Spring transaction_transaction isolation level
Spring transaction_annotation configuration declarative transaction
Spring transaction_transaction introduction
Transactions: Indivisible atomic operations. That is, a series of operations either succeed at the same time or fail at the same time.
During the development process, transaction management is generally in the service layer, and the database may be operated multiple times in the service layer, and these operations are inseparable. Otherwise, when the program reports an error, data exceptions may occur.
For example: when Zhang San transfers money to Li Si, the database needs to be operated twice: Zhang San's deposit decreases and Li Si's deposit increases. If an exception occurs between these two database operations, data errors will result.
1. Prepare the database
CREATE DATABASE `spring` ; USE `spring`; DROP TABLE IF EXISTS `account`; CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `balance` double DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; insert into `account`(`id`,`username`,`balance`) values (1,'张三',1000),(2,'李四',1000);
2. Create a maven project and introduce dependencies
<dependencies> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!-- mysql驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connectorjava</artifactId> <version>8.0.26</version> </dependency> <!-- druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>springcontext</artifactId> <version>5.3.13</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.13</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>springjdbc</artifactId> <version>5.3.13</version> </dependency> <!-- MyBatis与Spring的整合包,该包可以让Spring创建MyBatis的对象 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatisspring</artifactId> <version>2.0.6</version> </dependency> <!-- junit,如果Spring5整合junit,则junit版本至少在4.12以上 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- spring整合测试模块 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springtest</artifactId> <version>5.3.13</version> <scope>test</scope> </dependency> </dependencies>
3. Create a configuration file
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 包扫描 --> <context:component-scan basepackage="com.tong"></context:component-scan> <!-- 创建druid数据源对象 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///spring"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.tong.dao"></property> </bean> </beans>
4. Write Java code
// 账户 public class Account { private int id; // 账号 private String username; // 用户名 private double balance; // 余额 // 省略getter/setter/tostring/构造方法 } @Repository public interface AccountDao { // 根据id查找用户 @Select("select * from account where id = #{id}") Account findById(int id); // 修改用户 @Update("update account set balance = #{balance} where id = #{id}") void update(Account account); } @Service public class AccountService { @Autowired private AccountDao accountDao; /** * 转账 * @param id1 转出人id * @param id2 转入人id * @param price 金额 */ public void transfer(int id1, int id2, double price) { // 转出人减少余额 Account account1 = accountDao.findById(id1); account1.setBalance(account1.getBalance()-price); accountDao.update(account1); int i = 1/0; // 模拟程序出错 // 转入人增加余额 Account account2 = accountDao.findById(id2); account2.setBalance(account2.getBalance()+price); accountDao.update(account2); } }
5. Test transfer
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath :applicationContext.xml") public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer(){ accountService.transfer(1,2,500); } }
At this time, if there is no transaction management, Zhang San's balance will decrease, but Li Si's balance will not increase. Therefore, transaction processing is located in the business layer, that is, a service method cannot be separated.
Spring Transaction_Spring Transaction Management Solution
Manually adding transactions at the service layer can solve this problem:
@Autowired
private SqlSessionTemplate sqlSession;
public void transfer(int id1, int id2,double price) {
try{
// account1修改余额
Account account1 = accountDao.findById(id1);
account1.setBalance(account1.getBalance()- price);
accountDao.update(account1);
int i = 1/0; // 模拟转账出错
// account2修改余额
Account account2 = accountDao.findById(id2);
account2.setBalance(account2.getBalance()+price);
accountDao.update(account2);
sqlSession.commit();
}catch(Exception ex){
sqlSession.rollback();
}
}
But manual commit and rollback of transactions is not allowed under Spring management. At this point we need to use Spring's transaction management solution, which provides two transaction management solutions in the Spring framework:
1 Programmatic transaction : realize transaction management by writing code.
2 Declarative transaction : Realize transaction management based on AOP technology.
In the Spring framework, programmatic transaction management is rarely used, and we will study declarative transaction management in detail. Spring's declarative transaction management adopts AOP technology at the bottom layer. Its biggest advantage is that it does not need to manage transactions through programming. It only needs to declare relevant rules in the configuration file to apply transaction rules to business logic.
Use AOP technology to add the following notification to the service method:
Spring Transaction_Spring Transaction Manager
Spring relies on the transaction manager for transaction management. The transaction manager is a notification class. We set the cut point for the notification class as a service layer method to complete automatic transaction management. Since different technologies operate databases, the methods for performing transaction operations are different. For example: JDBC commits transaction is connection.commit(), MyBatis commits transaction is sqlSession.commit(), so Spring provides multiple transaction managers.
We use MyBatis to operate the database, and then use DataSourceTransactionManager for transaction management.
1. Introduce dependencies
<!-- 事务管理 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.13</version> </dependency> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
2. Introduce constraints in the configuration file
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
3. Perform transaction configuration
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 进行事务相关配置 --> <tx:advice id = "txAdvice"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 配置切点 --> <aop:pointcut id="pointcut" expression="execution(* com.tong.service.*.*(..))"/> <!-- 配置通知 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor> </aop:config>
Spring transaction_transaction control API
Spring's transaction control function is provided by three interfaces. These three interfaces are implemented by Spring. We rarely use them in development. We only need to understand their functions:
PlatformTransactionManager interface
PlatformTransactionManager is a transaction manager interface provided by Spring, and all transaction managers implement this interface. This interface provides three transaction operation methods:
1. TransactionStatus getTransaction (TransactionDefinition definition): Get transaction status information.
2. void commit (TransactionStatus status): transaction commit
3. void rollback (TransactionStatus status): transaction rollback
TransactionDefinition interface
TransactionDefinition is the definition information object of the transaction, which has the following methods:
String getName(): Get the transaction object name.
int getIsolationLevel(): Get the isolation level of the transaction.
int getPropagationBehavior(): Get the propagation behavior of the transaction.
int getTimeout(): Get the timeout time of the transaction.
boolean isReadOnly(): Gets whether the transaction is read-only.
TransactionStatus interface
TransactionStatus is the status interface of the transaction, which describes the status information of the transaction at a certain point in time. It has the following methods:
void flush() refresh transaction
boolean hasSavepoint() Gets whether there is a savepoint
boolean isCompleted() Get whether the transaction is completed
boolean isNewTransaction() Get whether it is a new transaction
boolean isRollbackOnly() Get whether to roll back
void setRollbackOnly() Set transaction rollback
Related configuration of Spring transaction_transaction
Transaction related configuration can be done in <tx:advice>:
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*"/>
<tx:method name="find*" readonly="true"/>
</tx:attributes>
</tx:advice>
Spring transaction _ transaction propagation behavior
Transaction propagation behavior refers to how transactions are propagated among methods when multiple methods containing transactions call each other. If other service methods are called in the method of the service layer, assuming that the transaction must be started every time the service method is executed, then there is no guarantee that the outer method and the inner method are in the same transaction.
// method1的所有方法在同一个事务中 public void method1(){ // 此时会开启一个新事务,这就无法保证method1()中所有的代码是在同一个事务中 method2(); System.out.println("method1"); } public void method2(){ System.out.println("method2"); }
The propagation feature of the transaction is to solve this problem. Spring helps us put the outer method and the inner method into the same transaction.
Spring transaction_transaction isolation level
The transaction isolation level reflects the processing attitude when the transaction is submitted for concurrent access. The higher the isolation level, the lower the possibility of data problems, but the lower the efficiency.
Spring transaction_annotation configuration declarative transaction
Spring supports the configuration of declarative transactions using annotations. The usage is as follows:
1. Registration transaction annotation driver
<!-- 注册事务注解驱动 --> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
2. Add @Transactional to the method or class that needs transaction support
@Service // 作用于类上时,该类的所有public方法将都具有该类型的事务属性 @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT) public class AccountService { @Autowired private AccountDao accountDao; /** * 转账 * @param id1 转出人id * @param id2 转入人id * @param price 金额 */ // 作用于方法上时,该方法将都具有该类型的事务属性 @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT) public void transfer(int id1, int id2,double price) { // account1修改余额 Account account1 = accountDao.findById(id1); account1.setBalance(account1.getBalance()-price); accountDao.update(account1); int i = 1/0; // 模拟转账出错 // account2修改余额 Account account2 = accountDao.findById(id2); account2.setBalance(account2.getBalance()+price); accountDao.update(account2); } }
3 The configuration class replaces the annotation transaction support in xml: write @EnableTranscationManagement above the configuration class
@Configuration @ComponentScan("com.tong") @EnableTransactionManagement public class SpringConfig { @Bean public DataSource getDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("com.mysql.jdbc.Driver"); druidDataSource.setUrl("jdbc:mysql:///spring"); druidDataSource.setUsername("root"); druidDataSource.setPassword("root"); return druidDataSource; } @Bean public SqlSessionFactoryBean getSqlSession(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } @Bean public MapperScannerConfigurer getMapperScanner(){ MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage("com.tong.dao"); return mapperScannerConfigurer; } @Bean public DataSourceTransactionManager getTransactionManager(DataSource dataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }