一、声明式事务概述 1、事务回顾 ① 事务和 SQL 语句的关系 一个事务中包含多条 SQL 语句。
② 事务的 ACID 属性 原子性:一个事务中包含的多个数据库操作,缺一不可。 一致性:事务执行之前,整个数据库中的所有数据处于“一致”状态(正确状态);事务执行之后,数据仍然处于“一致”状态。为了做到这一点,我们会让事务中的所有操作,要么全部成功一起提交,要么在任何一个操作失败后整体回滚。 持久性:事务一旦提交,那么就永久保存到数据库中。 隔离性:多个事务可以并发执行,参照隔离级别,决定它们相互之间是否会有彼此干扰。 ③ 事务执行过程中的并发问题
脏读:初始状态:数据库中 age 字段数据的值是 20 T1 把 age 修改为了 30 T2 读取了 age 现在的值:30 T1 回滚了自己的操作,age 恢复为了原来的 20 此时 T2 读取到的 30 就是一个不存在的“脏”的数据 不可重复读:T1 第一次读取 age 是 20 T2 修改 age 为 30 并提交事务,此时 age 确定修改为了 30 T1 第二次读取 age 得到的是 30 幻读:T1 第一次执行 count(*)返回 500 T2 执行了 insert 操作 T1 第二次执行 count(*)返回 501,感觉像是出现了幻觉 ④ 事务的隔离级别 读未提交:存在脏读、不可重复读、幻读这些所有问题 读已提交:能够解决脏读问题,不可重复读、幻读问题还存在 可重复读:能够解决脏读、不可重复读问题,幻读问题还存在 串行化:锁定整个表,让对整个表的操作全部排队串行执行。能解决所有并发问题,安全性最好,但是性能极差 2、IOC 容器和后面这些内容的关系 在 Spring 中干活,所有的东西肯定都必须在 IOC 容器中。如果某个对象不在 IOC 容器中,那么它就不归 Spring 管。
3、编程式事务 事务的相关操作完全由开发人员通过编码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 try { connection.setAutoCommit(false ); empDao.updateXxx(); connection.commit(); }catch (Exception e){ connection.rollBack(); }finally { connection.close(); }
4、声明式事务 事务的控制交给 Spring 框架来管理,开发人员只需要在 Spring 框架的配置文件中声明 你需要的功能即可。Spring 框架底层基于 AOP 实现了声明式事务。
二、声明式事务具体操作 1、搭建环境 ① 导入 jar 包 com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar commons-logging-1.1.3.jar druid-1.1.9.jar hamcrest-core-1.3.jar junit-4.12.jar mysql-connector-java-5.1.37-bin.jar spring-aop-4.0.0.RELEASE.jar spring-aspects-4.0.0.RELEASE.jar spring-beans-4.0.0.RELEASE.jar spring-context-4.0.0.RELEASE.jar spring-core-4.0.0.RELEASE.jar spring-expression-4.0.0.RELEASE.jar spring-jdbc-4.0.0.RELEASE.jar spring-orm-4.0.0.RELEASE.jar spring-test-4.0.0.RELEASE.jar spring-tx-4.0.0.RELEASE.jar
② 准备配置文件 [1]Spring 的配置文件 applicationContext.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <context:component-scan base-package ="com.atguigu.tx.component" /> <context:property-placeholder location ="classpath:jdbc.properties" /> <bean class ="com.alibaba.druid.pool.DruidDataSource" id ="druidDataSource" > <property name ="url" value ="${wechat.dev.url}" /> <property name ="driverClassName" value ="${wechat.dev.driver}" /> <property name ="username" value ="${wechat.dev.username}" /> <property name ="password" value ="${wechat.dev.password}" /> </bean > <bean class ="org.springframework.jdbc.core.JdbcTemplate" id ="jdbcTemplate" > <property name ="dataSource" ref ="druidDataSource" /> </bean >
[2]jdbc.properties 1 2 3 4 wechat.dev.driver =com.mysql.jdbc.Driver wechat.dev.url =jdbc:mysql://localhost:3306/mybatis0223 wechat.dev.username =root wechat.dev.password =root
③ 其他组件
④junit 测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(value = {"classpath:applicationContext.xml"}) public class TxTest { @Autowired private DataSource dataSource; @Autowired private EmpService empService; @Test public void testConnection () throws SQLException { Connection connection = dataSource.getConnection(); System.out.println("connection = " + connection); }
2、加入基于注解的声明式事务(三步) ① 配置事务管理器
1 2 3 4 5 6 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean >
② 开启声明式事务的注解驱动
注意:导入名称空间的时候,不要使用了错误的名称空间。
1 2 3 4 <tx:annotation-driven transaction-manager ="transactionManager" />
③ 在需要事务的方法上使用注解@Transactional 3、准备测试场景并测试声明式事务
①EmpDao 接口 1 2 3 4 5 6 7 package com.atguigu.tx.component.dao.api;public interface EmpDao { void updateEmpName (Integer empId, String empName) ; }
②EmpDaoImpl 实现类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Repository public class EmpDaoImpl implements EmpDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public void updateEmpName (Integer empId, String empName) { if (empId == 8 ) { throw new RuntimeException("抛出异常!!!" ); } String sql = "update t_emp set emp_name=? where emp_id=?" ; jdbcTemplate.update(sql, empName, empId); } }
③EmpService 接口 1 2 3 4 5 6 7 8 package com.atguigu.tx.component.service.api;public interface EmpService { void updateTwice () ; }
④EmpService 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Service @Transactional(readOnly = true) public class EmpServiceImpl implements EmpService { @Autowired private EmpDao empDao; @Override @Transactional(readOnly = false) public void updateTwice () { Integer empId = 7 ; String empName = "AAA" ; empDao.updateEmpName(empId, empName); empId = 8 ; empName = "BBB" ; empDao.updateEmpName(empId, empName); } }
⑤junit 测试 1 2 3 4 @Test public void testTx () { empService.updateTwice(); }
4、查看事务管理器源码 org.springframework.jdbc.datasource.DataSourceTransactionManager
① 提交操作 1 2 3 4 5 6 7 8 9 10 11 12 13 protected void doCommit (DefaultTransactionStatus status) { DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { this .logger.debug("Committing JDBC transaction on Connection [" + con + "]" ); } try { con.commit(); } catch (SQLException var5) { throw new TransactionSystemException("Could not commit JDBC transaction" , var5); } }
② 回滚操作 1 2 3 4 5 6 7 8 9 10 11 12 13 protected void doRollback (DefaultTransactionStatus status) { DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { this .logger.debug("Rolling back JDBC transaction on Connection [" + con + "]" ); } try { con.rollback(); } catch (SQLException var5) { throw new TransactionSystemException("Could not roll back JDBC transaction" , var5); } }
5、@Transactional 注解写在类上 ① 写法 1 2 3 @Service @Transactional public class EmpServiceImpl implements EmpService {
② 效果 @Transactional 注解写在类上之后相当于给类中的每一个方法都加了这个注解。包括注解中设置的属性,也会一起被作用到类中的方法。
③ 特殊情况 如果类上@Transactional 注解设置的属性,对具体的某个方法来说不合适,那么就可以在具体的这个方法上再声明一个@Transactional 注解,设置自己需要的属性。此处遵循就近原则,离方法近的设置会覆盖离得远的设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Service @Transactional(readOnly = true) public class EmpServiceImpl implements EmpService { @Autowired private EmpDao empDao; @Override @Transactional(readOnly = false) public void updateTwice () { Integer empId = 5 ; String empName = "AAA~~~" ; empDao.updateEmpName(empId, empName); empId = 6 ; empName = "BBB~~~" ; empDao.updateEmpName(empId, empName); } @Override public List<Employee> getAll () { return empDao.selectAll(); } }
在上面的例子中,类上的@Transactional(readOnly = true)注解对 getAll()方法有效,对 updateTwice()方法无效。
updateTwice()方法应用的是它自己的@Transactional(readOnly = false)。