目录
| 事务控制方式 | 解释 |
| 编程式事务控制 | Spring提供了事务控制的类和方法,使用编程的方法对业务代码进行事务控制,事务控制代码和业务操作代码耦合在一起,开发中不使用 |
| 声明式事务 | Spring将事务控制的代码封装起来,对外提供xml和注解的配置方式,通过配置的方式完成事务的控制,可以达到事务控制和业务操作代码的解耦,开发中推荐使用 |
Spring事务编程相关的类主要有以下三个
| 类名 | 功能 |
|---|---|
| PlatformTransactionManager | 平台事务管理器,抽象了不同的事务技术(如 JDBC、JTA)下的事务管理器。它定义了事务的开始、提交和回滚等操作接口,由具体实现提供相应的实现。Spring 提供了多种实现,不同持久层有不同实现方案,如 DataSourceTransactionManager、HibernateTransactionManager 和 JpaTransactionManager 等。 |
| TransactionDefinition | 事务定义,用于定义事务的隔离级别、超时时间等属性。Spring 定义了多种常量值,如 ISOLATION_DEFAULT、ISOLATION_READ_COMMITTED、ISOLATION_REPEATABLE_READ 等隔离级别;TIMEOUT_DEFAULT、TIMEOUT_NONE 等超时时间。 |
| TransactionStatus | 事务状态,包括是否新事务、是否已完成、是否回滚等状态。将该状态对象传递给事务管理器的 commit() 或 rollback() 方法可以控制事务的提交或回滚操作。 |

- package com.example.Mapper;
-
- import org.apache.ibatis.annotations.Param;
- import org.apache.ibatis.annotations.Update;
- import org.springframework.stereotype.Repository;
-
- @Repository("accountMapper")
- public interface AccountMapper {
- @Update("update tb_account set money = money + #{money} where account_name=#{accountName}")
- public void incrMoney(@Param("accountName") String accountName, @Param("money") Integer money);
-
- @Update("update tb_account set money = money - #{money} where account_name=#{accountName}")
-
- public void decrMoney(@Param("accountName") String accountName, @Param("money") Integer money);
- }
- package com.example.Service.ServiceImpl;
-
- import com.example.Mapper.AccountMapper;
- import com.example.Service.AccountService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- @Service("accountService")
- public class AccountServiceImpl implements AccountService {
- @Autowired
- private AccountMapper accountMapper;
-
- @Override
- public void transferMoney(String outAccount, String inAccount, Integer money) {
- accountMapper.decrMoney(outAccount, money);
- System.out.println(outAccount + "转出" + money + "元");
- accountMapper.incrMoney(inAccount, money);
- System.out.println(inAccount + "转入" + money + "元");
- }
- }
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- 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/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">
-
-
- <context:component-scan base-package="com.example"/>
-
- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/db02"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
- bean>
-
-
- <bean class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource"/>
- bean>
-
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.example.Mapper"/>
- bean>
-
-
-
- beans>
测试正常转账和异常转账
- package com.example.Test;
-
- //import com.example.Config.MyBatisConfig;
- import com.example.Service.AccountService;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestAccount {
- public static void main(String[] args) {
- // ApplicationContext context = new AnnotationConfigApplicationContext(MyBatisConfig.class);
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- AccountService accountService = (AccountService) context.getBean("accountService");
- accountService.transferMoney("tom", "lucy", 500);
- }
- }
运行结果如下:


- package com.example.Service.ServiceImpl;
-
- import com.example.Mapper.AccountMapper;
- import com.example.Service.AccountService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- @Service("accountService")
- public class AccountServiceImpl implements AccountService {
- @Autowired
- private AccountMapper accountMapper;
-
- @Override
- public void transferMoney(String outAccount, String inAccount, Integer money) {
- accountMapper.decrMoney(outAccount, money);
- System.out.println(outAccount + "转出" + money + "元");
- int i = 1 / 0;
- accountMapper.incrMoney(inAccount, money);
- System.out.println(inAccount + "转入" + money + "元");
- }
- }
同样运行上述测试代码
运行结果如下(出现数据无法对应)


- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- 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/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">
-
-
- <context:component-scan base-package="com.example"/>
-
- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/db02"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
- bean>
-
-
- <bean class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource"/>
- bean>
-
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="com.example.Mapper"/>
- bean>
-
-
-
- <bean name="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"/>
- bean>
-
-
- <tx:advice id="AccountServiceAdvice" transaction-manager="transactionManager1">
- <tx:attributes>
- <tx:method name="*"/>
- tx:attributes>
- tx:advice>
-
-
- <aop:config>
-
- <aop:pointcut id="AccountService"
- expression="execution(* com.example.Service.ServiceImpl.AccountServiceImpl.*(..))"/>
-
- <aop:advisor advice-ref="AccountServiceAdvice" pointcut-ref="AccountService"/>
- aop:config>
-
- beans>
对于事务、AOP、代理对象的产生等相关知识,可以前往我的主页进行关键字搜索,自行查阅。
事务的创建:事务平台管理器通过获取事务配置,从而创建一个新的事务对象,用于执行目标方法。
事务的提交:在目标方法正常执行结束后,事务平台管理器会将事务提交,从而将所有修改操作永久保存到数据库。
事务的回滚:在目标方法执行发生异常时,事务平台管理器会自动回滚事务,将所有修改操作撤销,保持系统数据的一致性。
事务的传播行为:事务平台管理器可以根据配置,将事务从一个方法传播到另一个方法,以保证不同方法之间的数据一致性。
事务隔离级别:事务平台管理器可以设置事务的隔离级别,从而决定不同事务之间可见的数据范围和并发度等问题。
Spring 支持多种事务平台管理器,例如 DataSourceTransactionManager、HibernateTransactionManager、JpaTransactionManager 等。不同的事务平台管理器对应着不同的持久化技术和数据库访问框架,在使用时需要注意选择合适的方案。
当我们使用 Spring 进行事务管理时,事务平台管理器就是整个事务处理流程中的核心组件。它通过管理和控制事务,保证了数据操作的原子性、一致性、隔离性和持久性等特性,从而维护了数据库的完整性和系统的稳定性。



其对应的平台事务管理器是DataSourceTransactionManager,MyBatis框架也是如此,因为MyBatis底层实现也是JDBC进行操作的。


| isolation | 解释 |
| DEFAULT | 默认隔离级别,取决于当前数据库隔离级别,例如MySQL默认隔离级别是PEPEATABLE_READ |
| READ_UNCOMMITTED | A事务可以读取B事务尚未提交的事务记录,不能解决任何并发问题,安全性最顶,性能最高 |
| READ_COMMITTED | A事务只能读取到其它事务已经提交的事务,不能提取到未提交的记录,可以解决脏读问题,但不可以解决不可重复读和幻读问题。 |
| PEPEATABLE_READ | A事务多次从数据库读取某条记录结果一致,可以解决不可重复读、不可以解决幻读 |
| SERIALIZABLE | 串行化,可以解决任何并发问题,安全性最高,但性能最低。 |
| 事务传播行为 | 解释(前提都是A方法调用B方法) |
| REQUIRED(默认值) | A调用B,B需要事务,如果A有事务B就加入A事务中,如果A没有事务,B就自己创建一个事务。 |
| REQUIRED_NEW | A调用B,B需要新事物,如果A有事务就挂起,B自己创建一个新事务 |
| SUPPORTS | A调用B,B有无事务无所谓,A有事务就加入到A事务中,A无事务B就以非事务的方式执行 |
| NOT_SUPPORTS | A调用B,B就以无事务的方式进行,A如果有事务就挂起。 |
| NEVER | A调用B,B以无事务方式执行,A如果有事务就抛出异常 |
| MANDATORY | A调用B,B要加入A的事务中,如果A无事务就抛出异常 |
| NESTED | A调用B,B创建一个新事务,A有事务就作为嵌套事务存在,A没有事务就以创建的新事物执行 |
目前跟着翻阅源代码还不是很清楚(先学后面的)


使用全注解来对上述案例进行汇总
- package com.example.Config;
-
- import com.alibaba.druid.pool.DruidDataSource;
- import org.mybatis.spring.SqlSessionFactoryBean;
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.PropertySource;
- import org.springframework.jdbc.datasource.DataSourceTransactionManager;
-
- import javax.sql.DataSource;
-
- //注解配置类替代配置文件,实现纯注解开发
- @Configuration // 表示该类是一个核心配置类,同时将该类交给Spring容器管理(内置了@Component注解)
- @ComponentScan({"com.example"})//
- @PropertySource({"classpath:jdbc.properties"}) // 加载外部properties文件
- @MapperScan("com.example.Mapper") //加载对应的Mapper接口类
- //@EnableTransactionManagement Spring版本过低可能需要该注解
- public class SpringConfig {
- @Bean
- public DataSource dataSource(
- @Value("${jdbc.driver}") String driver,
- @Value("${jdbc.url}") String url,
- @Value("${jdbc.username}") String username,
- @Value("${jdbc.password}") String password) {
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setDriverClassName(driver);
- dataSource.setUrl(url);
- dataSource.setUsername(username);
- dataSource.setPassword(password);
- return dataSource;
- }
-
- @Bean
- public SqlSessionFactoryBean SqlSessionFactory(DataSource dataSource) {
- SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
- sqlSessionFactoryBean.setDataSource(dataSource);
- return sqlSessionFactoryBean;
- }
-
- @Bean
- public DataSourceTransactionManager transactionManager(DataSource dataSource) {
- DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
- dataSourceTransactionManager.setDataSource(dataSource);
- return dataSourceTransactionManager;
- }
- }
使用全注解的方式,则获取Spring容器的方式也需要相应改变,测试类代码如下
- package com.example.Test;
-
- //import com.example.Config.MyBatisConfig;
-
- import com.example.Config.SpringConfig;
- import com.example.Service.AccountService;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-
- public class TestAccount {
- public static void main(String[] args) {
- ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
- AccountService accountService = (AccountService) context.getBean("accountService");
- accountService.transferMoney("tom", "lucy", 500);
- }
- }