目录
(4)@Transactional开启事务(前提条件XML已配置)
一、前言
本章是在上一节SM工程项目中,通过@Transactional进行学习事务传播特性。
Spring声明注解式事务管理建立在AOP基础之上,是一个典型的横切关注点,通过环绕增强来实现,其原理是对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完毕之后根据执行情况提交或回滚事务。
二、Spring两种事务处理方式
(1)注解式的事务:使用@Transactional注解完成事务控制,此注解可添加到类上,则对类中所有方法执行事务的设定。此注解可添加到方法上,是对此方法执行事务的处理。
(2)声明式事务:在配置文件中添加一次,整个项目遵循事务的设定。
在SSM的开发中,多使用注解方式实现事务的处理。
三、Spring中事务五大隔离级别
(1)未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。
(2)提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)。
(3)可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读。
(4)串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
(5)使用数据库默认的隔离级别isolation = Isolation.DEFAULT
A.MySQL:mysql默认的事务处理级别是'REPEATABLE-READ',也就是可重复读
B.Oracle:oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。默认系统事务隔离级别是READ COMMITTED,也就是读已提交
四、Spring事务的传播特性
传播特性总结:多个事务之间的合并,互斥等都可以通过设置事务的传播特性来解决。
常用5个:全部共7个
(1)PROPAGATION_REQUIRED:必被包含事务(增删改必用)
(2)PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
(3)PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
(4) PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
(5)PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
不常用2个:
(1)PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
(2)PROPAGATION_NESTED:嵌套事务
五、非事务应景
(1)当前表数据
(2)更改业务接口实现——accounts
说明:以accounts账户表为例,在没有使用事务控制条件下执行操作过程中数据的不安全性。
@Service //交给Spring去创建对象
public class AccountsServiceImpl implements AccountsService {
//在所有的业务逻辑层中一定会有数据访问层的对象
@Autowired
private AccountsMapper accountsMapper;
@Override
public int addAccounts(Accounts accounts) {
int num = 0;
num = accountsMapper.addAccounts(accounts);
System.out.println("增加帐户成功!");
//手工抛出异常
System.out.println(1 / 0);
return num;
}
}
说明:以上代码addAccounts()方法中,先添加成功数据,之后此方法发生异常。从数据安全性和业务严谨性考虑应当为整个addAccounts()方法体不发生任何错误或异常时,新增数据才能正常处理。这里代码先添加数据再发生异常,就在accounts表中已经有了数据,所以我们需要使用事务来控制整个业务对数据操作进行细节处理。
六、事务应景——>注解式
说明:以accounts账户表为例,在使用事务控制条件下执行操作过程中保障数据安全性。————>业务接口实现同上
(1)更改Spring配置service
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--导入applicationContext_mapper.xml文件-->
<import resource="applicationContext_mapper.xml"/>
<!-- SM是基于注解的开发,所以添加包扫描 -->
<context:component-scan base-package="com.dhrj.java.zsitking.service.impl"/>
<!-- 事务处理 -->
<!-- 1.将MyBatis数据源替换为Druid数据源的Spring单例bean对象加载到Spring事务管理器中 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2.将Spring事务管理器单例bean对象加载到Spring事务注解驱动中
使用@Transaction注解以及它的各类事务属性值,
再通过代理模式进行底层事务拦截控制,当业务合法后正常添加数据,完成业务实现
proxy-target-class="false":
当该值是false时,使用JDK实现动态代理,这也是默认的实现方式
当该值是true时,使用cglib的实现方式动态代理
-->
<tx:annotation-driven proxy-target-class="false" transaction-manager="transactionManager"/>
</beans>
说明:既然是基于注解控制SQL事务,那就必须先完成Spring事务管理器和注册事务驱动的注解模式。
思考:为什么要添加Spring事务管理器?
答:
JDBC: Connection连接对象 con.commit()事务提交 con.rollback()事务回滚
MyBatis: SqlSession连接对象 sqlSession.commit()事务提交 sqlSession.rollback()事务回滚
Hibernate: Session连接对象 session.commit()事务提交 session.rollback()事务回滚
可以得出不同的框架访问技术,完成事务提交和回滚的连接对象不一样(Connection、SqlSession、Session),而在用Spring框架时,Spring底层并没有这些连接对象的信息,没有此对象信息是无法使用Spring事务控制管理机制来控制SQL事务的。重点!!!既然Spring底层与这些数据持久化框架没有技术关联,那么就要使用Spring事务管理器用来生成相应技术的连接+执行语句的对象关——>怎么关联呢?
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>Spring+MyBatis的事务管理器配置,必须使用DataSourceTransactionManager类完成处理。
Spring+ Hibernate的事务管理器配置,必须使用HibernateTransactionManager类+<property name="sessionFactory" ref="sessionFactory" /> + proxy-target-class="true"(CGLib)
(2)@Transactional
说明:业务中需要进行事务管理的类或方法都必须是公开Public修饰的,并且必须使用Spring内置@Transactional注解标注该类或方法,因为已经在Spring配置文件中添加了事务管理器和注解驱动,那么被@Transactional注解标注该类或方法在程序运行过程中会被Spring管理,程序员只需配置相应的事务控制参数,交给Spring容器中的代理模式(JDK+CGLib)进行SQL事务的处理。若不使用@Transactional注解标注该类或方法,那么代理模式无法访问,就不能代理业务。
不能被类内部方法调用。还是因为代理的原因,类内部自调用,不会经过代理类,所以
@Transactional不会生效。
——不使用@Transactional注解时
AccountsService accountsService = (AccountsService) ac.getBean("accountsServiceImpl");
System.out.println("accountsService单例bean对象的类型"+accountsService.getClass());
说明:最后的对象类型是账户接口实现类本身,即验证Spring的JDK动态代理+事务控制没有生效。
——使用@Transactional注解时
(3)@Transactional参数值详解
说明:从上往下介绍常用重点属性,value()、transactionManager()、lable()只是注解的别名标签,知道是什么就行,这里不详细介绍。
@Transactional(propagation = Propagation.REQUIRED,//事务的传播特性(如果当前存在事务,就加入该事务,如果当前没有事务,就创建一个新的事务)
noRollbackForClassName = "ArithmeticException", //指定发生什么异常不回滚,使用的是异常的名称(发生ArithmeticException异常时不回滚)
noRollbackFor = ArithmeticException.class,//指定发生什么异常不回滚,使用的是异常的类型(发生ArithmeticException异常时不回滚)
rollbackForClassName = "",//指定发生什么异常必须回滚(发生ArithmeticException异常时回滚)
rollbackFor = ArithmeticException.class,//指定发生什么异常必须回滚(发生ArithmeticException异常时回滚)
timeout = -1, //连接超时设置,默认值是-1,表示永不超时
readOnly = false, //默认是false,如果是查询操作,必须设置为true.
isolation = Isolation.DEFAULT//使用数据库默认的隔离级别
)//事务处于开启状态,生效
(4)@Transactional开启事务(前提条件XML已配置)
注意:真正意义的开启事务,是在Spring配置文件中,将Spring事务管理器注入到bean工厂且完成注册了事务注解。否则@Transactional开启事务是不准确的。
@Transactional(propagation = Propagation.REQUIRED)//事务处于开启状态,生效
@Service //交给Spring去创建对象
public class AccountsServiceImpl implements AccountsService {
//在所有的业务逻辑层中一定会有数据访问层的对象
@Autowired
private AccountsMapper accountsMapper;
@Override
public int addAccounts(Accounts accounts) {
int num = 0;
num = accountsMapper.addAccounts(accounts);
System.out.println("增加帐户成功!");
//手工抛出异常
System.out.println(1 / 0);
return num;
}
}
@Test
public void testAccounts() {
//2.取出UsersServiceImpl
AccountsService accountsService = (AccountsService) ac.getBean("accountsServiceImpl");
System.out.println("accountsService单例bean对象的类型"+accountsService.getClass());
int num = accountsService.addAccounts(new Accounts(666,"张松的账户666","账户余额666"));
System.out.println(num);
}
结论:Spring事务的传播特性在业务开发中,如果某个方法在执行过程中发生了异常的同时已经将数据插入到数据库中,那么这些数据是无效的,需要进行撤销,因此添加事务@Transactional,利用环绕通知的后切功能进行事务回滚,完成数据撤销功能,这样便可以保障数据安全的原子性、一致性、隔离性、持久性。
(5)发生异常不回滚
@Transactional(
propagation = Propagation.REQUIRED, //支持当前事务(如果当前存在事务,就加入该事务,如果当前没有事务,就创建一个新的事务)
noRollbackForClassName = {"ArithmeticException"} //发生ArithmeticException异常时不回滚
)//事务处于开启状态,生效
@Service //交给Spring去创建对象
public class AccountsServiceImpl implements AccountsService {
//在所有的业务逻辑层中一定会有数据访问层的对象
@Autowired
private AccountsMapper accountsMapper;
@Override
public int addAccounts(Accounts accounts) {
int num = 0;
num = accountsMapper.addAccounts(accounts);
System.out.println("增加帐户成功!");
//手工抛出异常
System.out.println(1 / 0);
return num;
}
}
七、总结
仅自己学习记录,如有错误,敬请谅解~,谢谢~~~