Spring-事务
Spring-事务
核心概念
(1)Spring事务的实现方式和实现原理
(2)Spring事务的传播行为
(3)Spring事务的隔离机制
(4)Spring框架的事务管理的优缺点
(5)Spring中如何配置事务?结合业务场景说明
基本概念
事务:把一组业务当成一个业务来做;要么都成功,要么都失败,保证业务操作完整性的一种数据库机制
Spring Jdbc Template:在spring中为了更加方便的操作JDBC,在JDBC的基础之上定义了一个抽象层,此设计的目的是为不同类型的JDBC操作提供模板方法,每个模板方法都能控制整个过程,并允许覆盖过程中的特定任务,通过这种方式,可以尽可能保留灵活性,将数据库存取的工作量降到最低
事务分类
ACID 四大特性
- A 原子性:原子性指的是 在一组业务操作下 要么都成功 要么都失败;在一组增删改查的业务下 要么都提交 要么都回滚
- C 一致性:事务前后的数据要保证数据的一致性;在一组的查询业务下 必须要保证前后关联数据的一致性
- I 隔离性:在并发情况下 事物之间要相互隔离
- D 持久性:数据一旦保存就是持久性的
在事务控制方面,主要有两个分类:
编程式事务:在代码中直接加入处理事务的逻辑,需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法
connetion.autoCommit(false);
connction.commint()
catch(){
connction.rollback();
}
声明式事务:在方法的外部添加注解或者直接在配置文件中定义,将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。spring的AOP恰好可
以完成此功能:事务管理代码的固定模式作为一种横切关注点,通过AOP方法模块化,进而实现声明式事务
声明式事务
使用步骤
声明式事务的使用步骤
(1)配置事务管理器,开启声明式事务配置(此处省略xmlns的部分引入,只保留核心配置代码)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans">
<!-- 配置JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 使用druid数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/guigu"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- a.定义事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- d.基于注解的声明式事务管理(需要引入tx标签) -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
(2)代码逻辑中通过@Transactional控制
@Transactional 可以标记在类上面(当前类所有的方法都运用上了事务)
@Transactional 标记在方法则只是当前方法运用事务也可以类和方法上面同时都存在, 如果类和方法都存在@Transactional会以方法的为准。如果方法上面没有@Transactional会以类上面的为准
建议:@Transactional写在方法上面,控制粒度更细, 建议@Transactional写在业务逻辑层上,因为只有业务逻辑层才会有嵌套调用的情况
事务配置的属性
隔离级别
隔离级别 | 说明 |
---|---|
isolation | 设置事务的隔离级别(用于解决并发事务所产生的问题:同一时间多个线程同时请求的情况) |
propagation | 事务的传播行为 |
noRollbackFor | 哪些异常事务可以不回滚 |
noRollbackForClassName | 填写的参数是全类名 |
rollbackFor | 哪些异常事务需要回滚 |
rollbackForClassName | 填写的参数是全类名 |
readOnly | 设置事务是否为只读事务 |
timeout | 事务超出指定执行时长后自动终止并回滚,单位是秒 |
如果不设定事务默认的隔离级别,则其相应使用的是数据库默认的隔离级别:
- Oracle:read_commited、serializable
- MySQL:repeatableread
事务的传播特性(传播行为)
事务的传播行为是针对业务逻辑层的定义,当一个是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行?
希望如果外部存在事务就用外部的, 外部不存在就自己开启事务
@Transactional
trans(){
sub();
log(); // 记录流水 数据库操作
add();
}
@Transactional REQUIRES_NEW
log();
超时属性(timeout)
指定事务等待的最长时间(秒):当前事务访问数据时,有可能访问的数据被别的数据进行加锁的处理,那么此时事务就必须等待,如果等待时间过长给用户造成的体验感差
设置事务是否只读(readOnly)
readonly:只会设置在查询的业务方法中
connection.setReadOnly(true) 通知数据库,当前数据库操作是只读,数据库就会对当前只读做相应优化
当将事务设置只读,就必须要业务方法里面有增删改
如果一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;
如果一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持(如:设置不可重复度、幻影读级别)。
对于只读事务,可以指定事务类型为readonly,即只读事务。由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段
异常属性
设置当前事务出现的那些异常就进行回滚或者提交
默认对于RuntimeException 及其子类 采用的是回滚的策略
默认对于Exception 及其子类 采用的是提交的策略
- 设置哪些异常不回滚(noRollbackFor)
- 设置哪些异常回滚(rollbackFor )
@Transactional(timeout = 3,rollbackFor = {FileNotFoundException.class})
实战应用
如果当前业务方法是一组 增、改、删 可以这样设置事务:@Transactional
如果当前业务方法是一组 查询 可以这样设置事务:@Transactionl(readOnly=true)
如果当前业务方法是单个 查询 可以这样设置事务:@Transactionl(propagation=propagation.SUPPORTS ,readOnly=true)
声明式事务的优缺点
优点:不需要在业务逻辑代码中编写事务相关代码,只需要在配置文件配置或使用注解(@Transaction),这种方式没有侵入性
缺点:声明式事务的最细粒度作用于方法上,如果像代码块也有事务需求,只能变通下,将代码块变为方法