Spring事务终极版...
1.什么是事务
事务(TRANSACTION) 是作为单个逻辑工作单元执行的一系列操作。
多个操作作为一个整体向系统提交,要么都执行,要么都不执行
事务是一个无可再分的逻辑单元
2.事务的特性
四个属性,简称ACDI属性
原子性(Atomicity)
事务是一个完整的操作,事务的各个操作都是不可再分的,要么都执行,要么都不执行
一致性(Consistency)
当事务完成后,数据必须处于一致性
隔离性(Isolation)
并发事务之间相互隔离,独立,他不应以任何形式依赖于或影响其他事务
持久性(Durability)
事务完成后,他对数据的修改是永久性的;
3.隔离问题
脏读
一个事务读到另一个事务没有提交的数据
不可重复读
一个事务读到另一个事务已经提交的数据(正常现象,主要发生在update)
幻读(虚读)
一个事务读到另一个事务已经提交的数据(正常现象,主要发生在insert)
4.隔离级别
read uncommitted:
读未提交。存在3个问题(脏读,可重复读,虚读)
read committed:
读已提交。解决脏读,存在2个问题(可重复读,虚读)
repeatable read:
可重复读。解决:脏读、不可重复读,存在1个问题。(虚读)
serializable :
串行化。都解决,单事务。
注意:这里是以级别从低到高排列的,不过他们的效率却是以高到低的,(一般使用第二、三个)
5.保存点
适用于多个事务都需要提交,不过有必须提交的事务,有的事务可提交
需求:AB(必须),CD(可选)
Connection conn = null;
Savepoint savepoint = null; //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
try{
//1 获得连接
conn = ...;
//2 开启事务
conn.setAutoCommit(false);
A
B
//设置一个保存点
savepoint = conn.setSavepoint();
C
D
//3 提交事务
conn.commit();
} catche(){
if(savepoint != null){ //CD异常
// 回滚到CD之前
conn.rollback(savepoint);
// 提交AB
conn.commit();
} else{ //AB异常
// 回滚AB
conn.rollback();
}
}
6.转账案例:(使用事务的代理工厂)
需求:
A账户给B账户转账,A账户减多少钱,B账户就需要增加多少钱
若出现异常,事务回滚到事务发生之前的状态
数据库表:
导入依赖:(这里的Spring-jdbc以来中包含jdbcTemplate)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency>
创建entity
public class Student { private Integer stuid; private String stuname; private Integer age; public Integer getStuid() { return stuid; } public void setStuid(Integer stuid) { this.stuid = stuid; } public String getStuname() { return stuname; } public void setStuname(String stuname) { this.stuname = stuname; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; }
创建Dao
public interface IStudentdao { //加年龄 public int jiaage(Integer sum); //减年龄 public int jianage(Integer sum); }
创建Daoimpl(这里我使用的是注释实现jdbcTemplate)
@Repository public class IStudentdaoimpl implements IStudentdao { @Resource private JdbcTemplate jdbcTemplate; @Override public int jiaage(Integer sum) { String sql="update student set age=age+? where stuid=1"; int update = this.jdbcTemplate.update(sql, sum); return update; } @Override public int jianage(Integer sum) { String sql="update student set age=age-? where stuid=2"; int update = this.jdbcTemplate.update(sql, sum); return update; } }
创建Service(因为加钱减钱是一步操作,所以在这里整合为一个方法)
public interface IStudentservice { //转账 public int zhuanzhang(); }
创建Serviceimpl
@Service("iStudentservice") public class IStudentserviceimpl implements IStudentservice { @Resource private IStudentdao iStudentdao; public IStudentdao getiStudentdao() { return iStudentdao; } public void setiStudentdao(IStudentdao iStudentdao) { this.iStudentdao = iStudentdao; } Propagation.REQUIRES_NEW) @Override public int zhuanzhang() { int jiaage = iStudentdao.jiaage(10); //模拟一个异常 //int asd=5/0; int jianage = iStudentdao.jianage(10); return jianage+jiaage; } }
数据源文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/student?useUniCode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull jdbc.username=root jdbc.password=123
大配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/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"> <!--扫描注解--> <context:component-scan base-package="com.JdbcTemplate"/> <!--记载配置文件--> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--DateSource模板 DriverManagerDataSource:Spring默认的数据源 数据源还有:c3p0 dbcp --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--植入JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置Spring的事务管理器,默认在发生异常的情况下回滚,否则提交--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--植入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--Spring事务的代理工厂--> <bean id="transactionFactory" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!--指定事务管理器--> <property name="transactionManager" ref="transactionManager"></property> <!--目标对象--> <property name="target" ref="iStudentservice"></property> <!--设置方法--> <property name="transactionAttributes"> <props>
<!--指定隔离级别和传播行为--> <prop key="zhuanzhang">ISOLATION_READ_COMMITTED,PROPAGATION_REQUIRES_NEW</prop> </props> </property> </bean> </beans>
测试
//工厂 @Test public void shi(){ IStudentservice stu = (IStudentservice)atc.getBean("transactionFactory"); int zhuanzhang = stu.zhuanzhang(); System.out.println("转账成功!"); }
修改Serviceimpl(模拟一个异常)
@Service("iStudentservice") public class IStudentserviceimpl implements IStudentservice { @Resource private IStudentdao iStudentdao; public IStudentdao getiStudentdao() { return iStudentdao; } public void setiStudentdao(IStudentdao iStudentdao) { this.iStudentdao = iStudentdao; } @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRES_NEW) @Override public int zhuanzhang() { int jiaage = iStudentdao.jiaage(10); //模拟一个异常 int asd=5/0; int jianage = iStudentdao.jianage(10); return jianage+jiaage; } }
继续测试
7.使用Aop事务
修改大配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/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"> <!--扫描注解--> <context:component-scan base-package="com.JdbcTemplate"/> <!--记载配置文件--> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--DateSource模板 DriverManagerDataSource:Spring默认的数据源 数据源还有:c3p0 dbcp --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--植入JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
<!--配置Spring的事务管理器,默认在发生异常的情况下回滚,否则提交--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--植入数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
<!--AOP管理事务-->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<!--实现事务的方法-->
<tx:attributes>
<tx:method name="zhuan*" isolation="READ_COMMITTED" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* *..service.*.*(..))"/>
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试
//aop @Test public void aopshi(){ IStudentservice stu = (IStudentservice)atc.getBean("iStudentservice"); int zhuanzhang = stu.zhuanzhang(); System.out.println("转账成功!"); }
模拟一个异常后
8.使用注释实现事务
修改大配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/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"> <!--扫描注解--> <context:component-scan base-package="com.JdbcTemplate"/> <!--记载配置文件--> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--DateSource模板 DriverManagerDataSource:Spring默认的数据源 数据源还有:c3p0 dbcp --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--植入JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置Spring的事务管理器,默认在发生异常的情况下回滚,否则提交--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--植入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--注解--> <tx:annotation-driven/> </beans>
修改Serviceimpl文件
@Service("iStudentservice") public class IStudentserviceimpl implements IStudentservice { @Resource private IStudentdao iStudentdao; public IStudentdao getiStudentdao() { return iStudentdao; } public void setiStudentdao(IStudentdao iStudentdao) { this.iStudentdao = iStudentdao; } @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRES_NEW) @Override public int zhuanzhang() { int jiaage = iStudentdao.jiaage(10); //模拟一个异常 int asd=5/0; int jianage = iStudentdao.jianage(10); return jianage+jiaage; } }
测试
//注解 @Test public void zhujie(){ IStudentservice stu = (IStudentservice)atc.getBean("iStudentservice"); int zhuanzhang = stu.zhuanzhang(); System.out.println("转账成功!"); }