Spring学习04:事务控制(TransactionManager)

Spring学习04:事务控制(TransactionManager)Spring 学习 04 事务控制 TransactionM Spring 事务控制 Spring 中事务控制的 API 使用 Spring 进行事务控制 Spring 配置式事务控制项目准备使用 xml 配置事务控制使用半注解配置事务控制使用纯注解式事务配置 Spring 编程式事务控制 Spring 事务控制 JavaEE 体系进行分层开发 事务处理位于业务层 Spring 提供了分层设计业务层的事务处理解决方

Spring事务控制

  1. JavaEE 体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方


  2. Spring 框架为我们提供了一组事务控制的接口,这组接口在spring-tx-5.0.2.RELEASE.jar
  3. Spring 的事务控制都是基于AOP的,它既可以使用配置的方式实现,也可以使用编程的方式实现.推荐使用配置方式实现.

数据库事务的基础知识

  1. 事务的四大特性:ACID
    1. 原子性(Atomicity): 事务包含的所有操作要么全部成功,要么全部失败回滚;成功必须要完全应用到数据库,失败则不能对数据库产生影响.
    2. 一致性(Consistency):事务执行前和执行后必须处于一致性状态.例如:转账事务执行前后,两账户余额的总和不变.
    3. 隔离性(Isolation): 多个并发的事务之间要相互隔离.
    4. 持久性(Durability): 事务一旦提交,对数据库的改变是永久性的.
  2. 事务的隔离级别:
    1. ISOLATION_READ_UNCOMMITTED: 读未提交.事务中的修改,即使没有提交,其他事务也可以看得到.会导致脏读,不可重复读,幻读.
    2. ISOLATION_READ_COMMITTED: 读已提交(Oracle数据库默认隔离级别).一个事务不会读到其它并行事务已修改但未提交的数据.避免了脏读,但会导致不可重复读,幻读.
    3. ISOLATION_REPEATABLE_READ: 可重复读(Mysql数据库默认的隔离级别).一个事务不会读到其它并行事务已修改且已提交的数据,(只有当该事务提交之后才会看到其他事务提交的修改).避免了脏读,不可重复读,但会导致幻读.
    4. ISOLATION_SERIALIZABLE: 串行化.事务串行执行,一个时刻只能有一个事务被执行.避免了脏读,不可重复读,幻读.

    可以通过下面的例子理解事务的隔离级别:

    事务A 事务B
    启动事务
    查询得到原始值origin=1

    启动事务
    查询得到值1
    将1改成2
    查询得到值value1
    提交事务B
    查询得到值value2
    提交事务A
    查询得到值value3

    对不同的事务隔离级别,事务A三次查询结果分别如下:

    事务隔离级别 原始值origin value1 value2 value3
    ISOLATION_READ_UNCOMMITTED 1 2(脏读) 2 2
    ISOLATION_READ_COMMITTED 1 1 2(不可重复读) 2
    ISOLATION_REPEATABLE_READ 1 1 1 2
    ISOLATION_SERIALIZABLE 1 1 1 1
  3. 事务的安全隐患有如下三种,他们可以通过设置合理的隔离级别来避免:
    1. 脏读: 一个事务读到另外一个事务还未提交(可能被回滚)的脏数据.
    2. 不可重复读: 一个事务执行期间另一事务提交修改,导致第一个事务前后两次查询结果不一致.
    3. 幻读: 一个事务执行期间另一事务提交添加数据,导致第一个事务前后两次查询结果到的数据条数不同.
脏读 不可重复读 幻读
ISOLATION_READ_UNCOMMITTED
ISOLATION_READ_COMMITTED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE

Spring中事务控制的API

  1. PlatformTransactionManager接口是Spring提供的事务管理器,它提供了操作事务的方法如下:
    • TransactionStatus getTransaction(TransactionDefinition definition): 获得事务状态信息
    • void commit(TransactionStatus status): 提交事务
    • void rollback(TransactionStatus status): 回滚事务

    在实际开发中我们使用其实现类:

    • org.springframework.jdbc.datasource.DataSourceTransactionManager使用SpringJDBCiBatis进行持久化数据时使用
    • org.springframework.orm.hibernate5.HibernateTransactionManager使用Hibernate版本进行持久化数据时使用
  2. TransactionDefinition: 事务定义信息对象,提供查询事务定义的方法如下:
    • String getName(): 获取事务对象名称
    • int getIsolationLevel(): 获取事务隔离级别,设置两个事务之间的数据可见性

      事务的隔离级别由弱到强,依次有如下五种:(可以参考文章事务的四种隔离级别,数据库事务4种隔离级别及7种传播行为)

      1. ISOLATION_DEFAULT: Spring事务管理的的默认级别,使用数据库默认的事务隔离级别.
      2. ISOLATION_READ_UNCOMMITTED: 读未提交.
      3. ISOLATION_READ_COMMITTED: 读已提交.
      4. ISOLATION_REPEATABLE_READ: 可重复读.
      5. ISOLATION_SERIALIZABLE: 串行化.
    • getPropagationBehavior(): 获取事务传播行为,设置新事务是否事务以及是否使用当前事务.

      我们通常使用的是前两种: REQUIREDSUPPORTS.事务传播行为如下:

      1. REQUIRED: Spring默认事务传播行为. 若当前没有事务,就新建一个事务;若当前已经存在一个事务中,加入到这个事务中.增删改查操作均可用
      2. SUPPORTS: 若当前没有事务,就不使用事务;若当前已经存在一个事务中,加入到这个事务中.查询操作可用
      3. MANDATORY: 使用当前的事务,若当前没有事务,就抛出异常
      4. REQUERS_NEW: 新建事务,若当前在事务中,把当前事务挂起
      5. NOT_SUPPORTED: 以非事务方式执行操作,若当前存在事务,就把当前事务挂起
      6. NEVER:以非事务方式运行,若当前存在事务,抛出异常
      7. NESTED:若当前存在事务,则在嵌套事务内执行;若当前没有事务,则执行REQUIRED类似的操作
    • int getTimeout(): 获取事务超时时间. Spring默认设置事务的超时时间为-1,表示永不超时.
    • boolean isReadOnly(): 获取事务是否只读. Spring默认设置为false,建议查询操作中设置为true
  3. TransactionStatus: 事务状态信息对象,提供操作事务状态的方法如下:
    • void flush(): 刷新事务
    • boolean hasSavepoint(): 查询是否存在存储点
    • boolean isCompleted(): 查询事务是否完成
    • boolean isNewTransaction(): 查询是否是新事务
    • boolean isRollbackOnly(): 查询事务是否回滚
    • void setRollbackOnly(): 设置事务回滚

使用Spring进行事务控制

Spring配置式事务控制

项目准备

  1. 导入jar包到项目的lib目录

    在这里插入图片描述

  2. 创建bean.xml并导入约束
     
          <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/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">  
           beans> 
  3. 准备数据库表和实体类

    创建数据库表如下

    create database learnSpringTransaction; --创建数据库 use learnSpringTransaction; -- 创建表 create table account( id int primary key auto_increment, name varchar(40), money float )charset=utf8; 

    准备java实体类如下:

    public class Account implements Serializable { 
          private Integer id; private String name; private Float money; public Integer getId() { 
         return id; } public void setId(Integer id) { 
         this.id = id; } public String getName() { 
         return name; } public void setName(String name) { 
         this.name = name; } public Float getMoney() { 
         return money; } public void setMoney(Float money) { 
         this.money = money; } @Override public String toString() { 
         return "Account{id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } } 
  4. 编写Service层接口和实现类

    Service层接口

    // 业务层接口 public interface IAccountService { 
          // 根据id查询账户信息 Account findAccountById(Integer accountId); // 转账 void transfer(String sourceName,String targetName,Float money); } 

    Service层实现类

    // 业务层实现类,事务控制应在此层 @Service("accountService") public class AccountServiceImpl implements IAccountService { 
          @Autowired private IAccountDao accountDao; @Override public Account findAccountById(Integer accountId) { 
          return accountDao.findAccountById(accountId); } @Override public void transfer(String sourceName, String targetName, Float money) { 
          System.out.println("start transfer"); // 1.根据名称查询转出账户 Account source = accountDao.findAccountByName(sourceName); // 2.根据名称查询转入账户 Account target = accountDao.findAccountByName(targetName); // 3.转出账户减钱 source.setMoney(source.getMoney() - money); // 4.转入账户加钱 target.setMoney(target.getMoney() + money); // 5.更新转出账户 accountDao.updateAccount(source); int i = 1 / 0; // 6.更新转入账户 accountDao.updateAccount(target); } } 
  5. 编写Dao层接口和实现类

    Dao层接口

    // 持久层接口 public interface IAccountDao { 
          // 根据Id查询账户 Account findAccountById(Integer accountId); // 根据名称查询账户 Account findAccountByName(String accountName); // 更新账户 void updateAccount(Account account); } 

    Dao层实现类

    //持久层实现类 @Repository("accountDao") public class AccountDaoImpl implements IAccountDao { 
          @Autowired private JdbcTemplate jdbcTemplate; // 根据id查询账户 @Override public Account findAccountById(Integer accountId) { 
          List<Account> accounts = jdbcTemplate.query("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), accountId); return accounts.isEmpty() ? null : accounts.get(0); } // 根据用户名查询账户 @Override public Account findAccountByName(String accountName) { 
          List<Account> accounts = jdbcTemplate.query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), accountName); if (accounts.isEmpty()) { 
          return null; } if (accounts.size() > 1) { 
          throw new RuntimeException("结果集不唯一"); } return accounts.get(0); } // 更新账户 @Override public void updateAccount(Account account) { 
          jdbcTemplate.update("update account set name=?,money=? where id=?", account.getName(), account.getMoney(), account.getId()); } } 
  6. bean.xml中配置数据源以及要扫描的包
     
          <context:component-scan base-package="com.itheima"> 
           context:component-scan>  
           <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"> 
            property> <property name="url" value="jdbc:mysql://localhost:3306/spring_day02"> 
             property> <property name="username" value="root"> 
              property> <property name="password" value="1234"> 
               property>  
                bean>  
                <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"> 
                 property>  
                  bean> 

使用xml配置事务控制

Spring的配置式事务控制本质上是基于AOP的,因此下面我们在bean.xml中配置事务管理器PlatformTransactionManager对象并将其与AOP配置建立联系.

  1. 配置事务管理器并注入数据源
     
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
          <property name="dataSource" ref="dataSource" />  
           bean> 
  2. 配置事务通知并在通知中配置其属性

    使用
    标签声明事务配置,其属性如下:

    • id: 事务配置的id
    • transaction-manager: 该配置对应的事务管理器


    标签内包含
    标签表示配置事务属性


    标签内包含
    标签,为切面上的方法配置事务属性,
    标签的属性如下:

    • name: 拦截到的方法,可以使用通配符*
    • isolation: 事务的隔离级别,Spring默认使用数据库的事务隔离级别
    • propagation: 事务的传播行为,默认为REQUIRED.增删改方法应该用REQUIRED,查询方法可以使用SUPPORTS
    • read-only: 事务是否为只读事务,默认为false.增删改方法应该用false,查询方法可以使用true
    • timeout: 指定超时时间,默认值为-1,表示永不超时
    • rollback-for: 指定一个异常,当发生该异常时,事务回滚;发生其他异常时,事务不回滚.无默认值,表示发生任何异常都回滚
    • no-rollback-for: 指定一个异常,当发生该异常时,事务不回滚;发生其他异常时,事务回滚.无默认值,表示发生任何异常都回滚
     
          <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes>  
          <tx:method name="*" propagation="REQUIRED" read-only="false" rollback-for="" no-rollback-for=""/>  
          <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>  
           
           tx:attributes>  
            tx:advice> 
  3. 配置AOP并为事务管理器事务管理器指定切入点
     
          <aop:config>  
          <aop:pointcut id="pt1" expression="execution(* cn,maoritian.service.impl.*.*(..))"> 
           aop:pointcut>  
           <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>  
            aop:config> 

使用半注解配置事务控制

  1. 配置事务管理器并注入数据源
     
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
          <property name="dataSource" ref="dataSource"> 
           property>  
            bean> 
  2. 在业务层使用@Transactional注解,其参数与
    的属性一致.

    该注解可以加在接口,类或方法上

    • 对接口加上@Transactional注解,表示对该接口的所有实现类进行事务控制
    • 对类加上@Transactional注解,表示对类中的所有方法进行事务控制
    • 对具体某一方法加以@Transactional注解,表示对具体方法进行事务控制

    三个位置上的注解优先级依次升高

    // 业务层实现类,事务控制应在此层 @Service("accountService") @Transactional(propagation = Propagation.REQUIRED, readOnly = false) // 读写型事务配置 public class AccountServiceImpl implements IAccountService { 
          @Autowired private IAccountDao accountDao; @Override @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) // 只读型事务配置,会覆盖上面对类的读写型事务配置  public Account findAccountById(Integer accountId) { 
          return accountDao.findAccountById(accountId); } @Override public void transfer(String sourceName, String targetName, Float money) { 
          // 转账操作的实现... } } 

使用纯注解式事务配置

不使用xml配置事务,就要在cn.maoritian.config包下新建一个事务管理配置类TransactionConfig,对其加上@EnableTransactionManagement注解以开启事务控制.

事务控制配置类TransactionConfig类的源码如下:

@Configuration @EnableTransactionManagement // 开启事务控制 public class TransactionConfig { 
    // 创建事务管理器对象 @Bean(name="transactionManager") public PlatformTransactionManager createTransactionManager(@Autowired DataSource dataSource){ 
    return new DataSourceTransactionManager(dataSource); } } 

JDBC配置类JdbcConfig类的源码如下:

@Configuration @PropertySource("classpath:jdbcConfig.properties") public class JdbcConfig { 
    @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; // 创建JdbcTemplate对象 @Bean(name="jdbcTemplate") @Scope("prototype") public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource){ 
    return new JdbcTemplate(dataSource); } // 创建数据源对象 @Bean(name="dataSource") public DataSource createDataSource(){ 
    DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } } 

Spring主配置类SpringConfig源代码如下:

@Configuration @ComponentScan("cn.maoritian") @Import({ 
   JdbcConfig.class, TransactionConfig.class}) public class SpringConfig { 
    } 

Spring编程式事务控制

Spring的编程时事务控制不能实现解耦,反而使耦合更加严重了,因此不推荐使用.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205394.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午6:07
下一篇 2026年3月19日 下午6:08


相关推荐

  • 微信公众号开发-超级简单[通俗易懂]

    微信公众号开发-超级简单[通俗易懂]1自动回复功能【图片模糊的双击图片,就清晰了】公众号注册网上一大把,搜下就可以了这个功能就是别人给公众号发什么消息,就返回指定内容关键词回复:输入关键词,返回指定内容收到消息回复:当你不是输入关键词时,自动发送当前消息,如果输入的是关键词,就返回关键词所指定的内容被关注回复:当公众号被关注时,自动给用户发的消息1案例,添加关键…

    2022年5月12日
    43
  • Spring batch教程 之 spring batch简介

    Spring batch教程 之 spring batch简介SpringBatch介绍在企业领域,有很多应用和系统需要在生产环境中使用批处理来执行大量的业务操作.批处理业务需要自动地对海量数据信息进行各种复杂的业务逻辑处理,同时具备极高的效率,不需要人工干预.执行这种操作通常根据时间事件(如月末统计,通知或信件),或者定期处理那些业务规则超级复杂,数据量非常庞大的业务,(如保险赔款确定,利率调整),也可能是从内部/外部系统抓取到的各种数据,通常需要

    2022年5月11日
    48
  • 7款最佳Andi替代方案:2025年理想的AI助手

    7款最佳Andi替代方案:2025年理想的AI助手

    2026年3月15日
    5
  • idea2022激活码-激活码分享

    (idea2022激活码)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlS32PGH0SQB-eyJsaWNlbnNlSW…

    2022年3月22日
    293
  • 华为三层交换机配置不同网段互通[通俗易懂]

    华为三层交换机配置不同网段互通[通俗易懂]CSDN上看到这样一个问题,回答后所以顺便整理一下,一般情况下,三层交换机不直接连终端端,太浪费了,可以连一个交换机然后再连终端

    2025年10月27日
    4
  • redhat配置yum源服务器_redhat7网络yum源配置

    redhat配置yum源服务器_redhat7网络yum源配置配置yum源redhat默认自带的yum源需要注册才能更新,可以更换成Centos的yum源.文章目录配置yum源前言一、yum源的工作原理?二、yum配置步骤1.查看系统已经安装的yum2.删除自带的yum包3.下载相关的安装包4.安装包5.下载配置文件6.更新前言Yum(全称为YellowdogUpdater,Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器。基于RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动

    2022年8月12日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号