概述
编程式事务控制
自己手动控制事务,就叫做编程式事务控制。
Jdbc代码:
Conn.setAutoCommite(false); // 设置手动控制事务
Hibernate代码:
Session.beginTransaction(); // 开启一个事务
【细粒度的事务控制: 可以对指定的方法、指定的方法的某几行添加事务控制】
(比较灵活,但开发起来比较繁琐: 每次都要开启、提交、回滚.)
spring提供的事务控制
声明式事务控制
Spring提供了对事务的管理, 这个就叫声明式事务管理。
Spring提供了对事务控制的实现。用户如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。这个实现了对事务 控制的最大程度的解耦。
Spring声明式事务管理,核心实现就是基于Aop。
【粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务。】
(因为aop拦截的是方法。)
Spring声明式事务管理器类:
Jdbc技术:DataSourceTransactionManager
Hibernate技术:HibernateTransactionManager
1 package cn.fuyi.a_tx; 2 3 public class Dept { 4 5 private Integer deptId; 6 private String dname; 7 public Integer getDeptId() { 8 return deptId; 9 } 10 public void setDeptId(Integer deptId) { 11 this.deptId = deptId; 12 } 13 public String getDname() { 14 return dname; 15 } 16 public void setDname(String dname) { 17 this.dname = dname; 18 } 19 20 } 21 22 package cn.fuyi.a_tx; 23 24 import org.springframework.jdbc.core.JdbcTemplate; 25 26 public class DeptDao { 27 28 private JdbcTemplate jdbcTemplate; 29 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 30 this.jdbcTemplate = jdbcTemplate; 31 } 32 33 public void save(Dept dept) { 34 String sql = "insert into dept(dname) values(?)"; 35 jdbcTemplate.update(sql, dept.getDname()); 36 } 37 } 38 39 package cn.fuyi.a_tx; 40 41 public class DeptService { 42 43 private DeptDao deptDao; 44 public void setDeptDao(DeptDao deptDao) { 45 this.deptDao = deptDao; 46 } 47 48 public void save(Dept dept) { 49 deptDao.save(dept); 50 int i = 1/0; 51 deptDao.save(dept); 52 53 } 54 } 55 56 <?xml version="1.0" encoding="UTF-8"?> 57 <beans xmlns="http://www.springframework.org/schema/beans" 58 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 59 xmlns:p="http://www.springframework.org/schema/p" 60 xmlns:context="http://www.springframework.org/schema/context" 61 xmlns:aop="http://www.springframework.org/schema/aop" 62 xmlns:tx="http://www.springframework.org/schema/tx" 63 xsi:schemaLocation=" 64 http://www.springframework.org/schema/beans 65 http://www.springframework.org/schema/beans/spring-beans.xsd 66 http://www.springframework.org/schema/context 67 http://www.springframework.org/schema/context/spring-context.xsd 68 http://www.springframework.org/schema/aop 69 http://www.springframework.org/schema/aop/spring-aop.xsd 70 http://www.springframework.org/schema/tx 71 http://www.springframework.org/schema/tx/spring-tx.xsd"> 72 73 <!-- dataSource配置 --> 74 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 75 <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 76 <property name="jdbcUrl" value="jdbc:mysql:///mydb"></property> 77 <property name="user" value="root"></property> 78 <property name="password" value="950613"></property> 79 <property name="initialPoolSize" value="4"></property> 80 <property name="maxPoolSize" value="9"></property> 81 <property name="maxStatements" value="100"></property> 82 <property name="acquireIncrement" value="2"></property> 83 </bean> 84 85 <!-- JdbcTemplate工具类实例 --> 86 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 87 <property name="dataSource" ref="dataSource"></property> 88 </bean> 89 90 <!-- dao实例 --> 91 <bean id="deptDao" class="cn.fuyi.a_tx.DeptDao"> 92 <property name="jdbcTemplate" ref="jdbcTemplate"></property> 93 </bean> 94 95 <!-- service实例 --> 96 <bean id="deptService" class="cn.fuyi.a_tx.DeptService"> 97 <property name="deptDao" ref="deptDao"></property> 98 </bean> 99 100 101 <!-- spring的声明式事务配置 --> 102 <!-- 1.配置事务管理器类 --> 103 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 104 <property name="dataSource" ref="dataSource"></property> 105 </bean> 106 107 <!-- 2.配置事务增强(如何管理事务?) --> 108 <tx:advice id="txAdvice" transaction-manager="transactionManager"> 109 <tx:attributes> 110 <tx:method name="*save*" read-only="false" isolation="DEFAULT" propagation="REQUIRED"/> 111 <tx:method name="*find*" read-only="true" isolation="DEFAULT" propagation="REQUIRED"/> 112 <tx:method name="*get*" read-only="true" isolation="DEFAULT" propagation="REQUIRED"/> 113 </tx:attributes> 114 </tx:advice> 115 116 <!-- 3.AOP配置:拦截哪些方法(切入点表达式 + 应用上面的事务增强配置) --> 117 <aop:config> 118 <aop:pointcut expression="execution(* cn.fuyi.a_tx.DeptService.*(..))" id="pt"/> 119 <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> 120 </aop:config> 121 122 </beans> 123 124 package cn.fuyi.a_tx; 125 126 import static org.junit.Assert.*; 127 128 import org.junit.Test; 129 import org.springframework.context.ApplicationContext; 130 import org.springframework.context.support.ClassPathXmlApplicationContext; 131 132 public class App { 133 134 private ApplicationContext ac = new ClassPathXmlApplicationContext("cn/fuyi/a_tx/beans.xml"); 135 136 @Test 137 public void testTx1() throws Exception { 138 DeptService ds = (DeptService) ac.getBean("deptService"); 139 Dept dept = new Dept(); 140 dept.setDname("哈2哈"); 141 ds.save(dept); 142 } 143 } 144 145 /**Output 146 147 数据库回滚 148 149 */
注解方式实现
使用注解实现Spring的声明式事务管理,更加简单!
步骤:
1) 必须引入Aop相关的jar文件
2) bean.xml中指定注解方式实现声明式事务管理以及应用的事务管理器类
3)在需要添加事务控制的地方,写上: @Transactional
@Transactional注解:
1)应用事务的注解
2)定义到方法上: 当前方法应用spring的声明式事务
3)定义到类上: 当前类的所有的方法都应用Spring声明式事务管理;
4)定义到父类上: 当执行父类的方法时候应用事务。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xsi:schemaLocation="http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd 12 http://www.springframework.org/schema/aop 13 http://www.springframework.org/schema/aop/spring-aop.xsd 14 http://www.springframework.org/schema/tx 15 http://www.springframework.org/schema/tx/spring-tx.xsd"> 16 17 18 <!-- 1. 数据源对象: C3P0连接池 --> 19 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 20 <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 21 <property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property> 22 <property name="user" value="root"></property> 23 <property name="password" value="root"></property> 24 <property name="initialPoolSize" value="3"></property> 25 <property name="maxPoolSize" value="10"></property> 26 <property name="maxStatements" value="100"></property> 27 <property name="acquireIncrement" value="2"></property> 28 </bean> 29 30 <!-- 2. JdbcTemplate工具类实例 --> 31 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 32 <property name="dataSource" ref="dataSource"></property> 33 </bean> 34 35 <!-- 事务管理器类 --> 36 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 37 <property name="dataSource" ref="dataSource"></property> 38 </bean> 39 40 <!-- 开启注解扫描 --> 41 <context:component-scan base-package="cn.itcast.b_anno"></context:component-scan> 42 43 <!-- 注解方式实现事务: 指定注解方式实现事务 --> 44 <tx:annotation-driven transaction-manager="txManager"/> 45 </beans>
事务属性
1 @Transactional( 2 readOnly = false, // 读写事务 3 timeout = -1, // 事务的超时时间不限制 4 noRollbackFor = ArithmeticException.class, // 遇到数学异常不回滚 5 isolation = Isolation.DEFAULT, // 事务的隔离级别,数据库的默认 6 propagation = Propagation.REQUIRED // 事务的传播行为 7 ) 8 public void save(Dept dept){ 9 deptDao.save(dept); 10 int i = 1/0; 11 deptDao.save(dept); 12 }
事务传播行为:
Propagation.REQUIRED
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务, 就会加入当前的事务;
Propagation.REQUIRED_NEW
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务: 事务会挂起; 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行。
举例:
1 1.Propagation.REQUIRED 2 Class Log{ 3 @Propagation.REQUIRED 4 insertLog(); 5 } 6 7 Class DeptDao{ 8 @Propagation.REQUIRED 9 Void saveDept(){ 10 insertLog(); // 加入当前事务 11 .. 出现异常, 会回滚 12 saveDept(); 13 } 14 } 15 16 2.Propagation.REQUIRED_NEW 17 Class Log{ 18 @Propagation.REQUIRED_NEW 19 insertLog(); 20 } 21 22 Class DeptDao{ 23 @Propagation.REQUIRED 24 Void saveDept(){ 25 insertLog(); // 始终开启事务 26 .. 异常, 日志不会回滚 27 saveDept(); 28 } 29 }
测试:
1 package cn.fuyi.a_tx; 2 3 import javax.annotation.Resource; 4 5 import org.springframework.jdbc.core.JdbcTemplate; 6 import org.springframework.stereotype.Repository; 7 import org.springframework.transaction.annotation.Propagation; 8 import org.springframework.transaction.annotation.Transactional; 9 10 @Repository 11 public class LogDao { 12 13 @Resource 14 private JdbcTemplate jdbcTemplate; 15 16 @Transactional(propagation=Propagation.REQUIRES_NEW) 17 public void insertLog() { 18 String sql = "insert into log_ values('在保存dept。。。')"; 19 jdbcTemplate.update(sql); 20 } 21 } 22 23 package cn.fuyi.a_tx; 24 25 import javax.annotation.Resource; 26 27 import org.springframework.stereotype.Service; 28 import org.springframework.transaction.annotation.Propagation; 29 import org.springframework.transaction.annotation.Transactional; 30 31 @Service("deptService") 32 public class DeptService { 33 34 @Resource(name="deptDao") 35 private DeptDao deptDao; 36 37 @Resource(name="logDao") 38 private LogDao logDao; 39 40 41 @Transactional( 42 propagation=Propagation.REQUIRED 43 ) 44 public void save(Dept dept) { 45 logDao.insertLog(); 46 int i = 1/0; 47 deptDao.save(dept); 48 } 49 }