Spring学习04:事务控制[TransactionManager]

  • Spring事务控制
    • 数据库事务的基础知识
    • Spring中事务控制的API
  • 使用Spring进行事务控制
    • Spring配置式事务控制
      • 项目准备
      • 使用xml配置事务控制
      • 使用半注解配置事务控制
      • 使用纯注解式事务配置
    • 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并导入约束

    <?xml version="1.0" encoding="UTF-8"?>
    <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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttp://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; }@Overridepublic 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 {@Autowiredprivate IAccountDao accountDao;@Overridepublic Account findAccountById(Integer accountId) {return accountDao.findAccountById(accountId);}@Overridepublic 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 {@Autowiredprivate JdbcTemplate jdbcTemplate;// 根据id查询账户@Overridepublic 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);}// 根据用户名查询账户@Overridepublic 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);}// 更新账户@Overridepublic void updateAccount(Account account) {jdbcTemplate.update("update account set name=?,money=? where id=?", account.getName(), account.getMoney(), account.getId());}
    }
    
  6. bean.xml中配置数据源以及要扫描的包

    <!--配置 创建Spring容器时要扫描的包-->
    <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>    <!--配置 JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property>
    </bean>
    

使用xml配置事务控制

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

  1. 配置事务管理器并注入数据源

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

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

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

    <tx:advice>标签内包含<tx:attributes>标签表示配置事务属性

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

    • 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:method>匹配得更精确,所以对所有查询方法,匹配第二个事务管理配置;对其他查询方法,匹配第一个事务管理配置--></tx:attributes>
    </tx:advice>
    
  3. 配置AOP并为事务管理器事务管理器指定切入点

    <!--配置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. 配置事务管理器并注入数据源

    <!--向Spring容器中注入一个事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入数据源--><property name="dataSource" ref="dataSource"></property>
    </bean>
    
  2. 在业务层使用@Transactional注解,其参数与<tx:method>的属性一致.

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

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

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

    // 业务层实现类,事务控制应在此层
    @Service("accountService")
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)    // 读写型事务配置
    public class AccountServiceImpl implements IAccountService {@Autowiredprivate IAccountDao accountDao;@Override@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) // 只读型事务配置,会覆盖上面对类的读写型事务配置 public Account findAccountById(Integer accountId) {return accountDao.findAccountById(accountId);}@Overridepublic 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的编程时事务控制不能实现解耦,反而使耦合更加严重了,因此不推荐使用.

Spring学习04:事务控制(TransactionManager)相关推荐

  1. Spring中的事务控制学习中

    Chapter 1. Spring中的事务控制(Transacion Management with Spring) Table of Contents 事务管理(Transaction Manage ...

  2. Spring中的事务控制

    Chapter 1. Spring中的事务控制(Transacion Management with Spring) Table of Contents 1.1. 有关事务(Transaction)的 ...

  3. java day60【 Spring 中的 JdbcTemplate[会用] 、Spring 中的事务控制 、Spring5 的新特性[了解] 】...

    第1章 Spring 中的 JdbcTemplate[会用] 1.1JdbcTemplate 概述 1.2JdbcTemplate 对象的创建 1.3spring 中配置数据源 1.3.1 环境搭建 ...

  4. Spring中的事务控制学习中(转)

    1.1. 有关事务(Transaction)的楔子 1.1.1. 认识事务本身 1.1.2. 初识事务家族成员 1.2. 群雄逐鹿下的Java事务管理 1.2.1. Java平台的局部事务支持 1.2 ...

  5. Spring MVC一事务控制问题

    在近期一个项目中用了Spring MVC作为控制层框架,但却出现了一个让人非常费解的问题:事务控制. Spring MVC的配置文件名称为:springMVC-servlet.xml,内容例如以下: ...

  6. Spring中的事务控制(Transacion Management with Spring)

    1.1. 有关事务(Transaction)的楔子 1.1.1. 认识事务本身1.1.2. 初识事务家族成员 1.2. 群雄逐鹿下的Java事务管理 1.2.1. Java平台的局部事务支持1.2.2 ...

  7. spring编程式事务控制

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  8. Spring学习:IOC控制反转

    一.Spring概述: Spring是一个开源框架,其存在的根本使命就是简化JAVA开发.为了降低JAVA开发的复杂性,Spring采取了以下四种关键策略: 基于POJO的最轻量级和最小侵入性编程: ...

  9. Spring学习8-Spring事务管理(AOP/声明式式事务管理)

    一.基础知识普及 声明式事务的事务属性: 一:传播行为 二:隔离级别 三:只读提示 四:事务超时间隔 五:异常:指定除去RuntimeException其他回滚异常.  传播行为: 所谓事务的传播行为 ...

  10. Spring学习8-Spring事务管理(注解式声明事务管理)

    步骤一.在spring配置文件中引入<tx:>命名空间 <beans xmlns="http://www.springframework.org/schema/beans& ...

最新文章

  1. 问题征集 | 跟计算机史学家对谈是一种怎样的感受
  2. 解决ReSharper自动删除换行
  3. 面试题: Vue中的 computed 和 watch的区别
  4. Nginx-----相关配置-详细介绍
  5. 对怀孕的人有害的食物。。。朋友们记住咯!(欢迎转载)
  6. 目标检测(一)--Objectness算法总体理解,整理及总结
  7. sql server 触发器
  8. 58. Attribute item() 方法
  9. js遍历对象去除空格
  10. HTML:网页设计案例3
  11. CATIA V6 二次开发—概述
  12. vue-cropper裁剪个人图像
  13. argmax() 函数
  14. java实现爬虫,爬取网易歌单信息
  15. 计算机卡死快捷键,必看!电脑运行卡或软件卡死无响应,怎么办?
  16. 独立窗口打开多个Excel
  17. Win10 22H2 19045.2670系统原版镜像
  18. Computer vision for solid waste sorting: A critical review of academic research 机器视觉垃圾分选综述翻译
  19. RuntimeError: Python 3.5 or later is required
  20. 电脑不能创建txt文件解决方法

热门文章

  1. iOS TestFlight 使用详解
  2. FFmpeg + SDL 的视频播放器的制作视频(雷神,雷霄骅)
  3. 索尼pha2 android手机,索尼PHA-2A便携式耳机放大器图文评测
  4. Codeblock一直卡在编译界面
  5. 爬虫实战 -- QQ空间自动点赞!太强了呀!
  6. Python身份证号码识别
  7. 0.99元用7天,金山云大米云主机给你这个机会!
  8. postman 测试excel下载_postman下载,postman下载excel
  9. Java 定时任务-最简单的3种实现方法
  10. ds6708 symbol 驱动_Symbol DS6708条码扫描器