什么是spring的事物属性
描述物体特征的一系列值
事务属性:描述事务特征的一系列值
- 隔离属性
- 传播属性
- 只读属性
- 超时属性
- 异常属性
如何添加事务属性:
//对应的事物属性 1 2 3 4 5 5
@Transactional(isolation = ,propagation = , readOnly = ,timeout = , rollbackFor = ,noRollbackFor = )
隔离属性(ISOLATION)
隔离属性的概念
概念:描述了事务解决并发问题的特征
**1、**什么是并发?
多个事务,或者用户,在同一时间访问操作了统一数据(同一时间:并不是说必须分毫不差,可能差0.00几秒)
**2、**并发会产生哪些问题?
- 脏读
- 不可重复读
- 幻影读
**3、**并发问题如何解决?
通过隔离属性解决,隔离属性中设置不同的值,解决并发 处理过程中的问题
隔离属性
- ** 脏读**
一个事务,读取了另一个事务没有提交的数据,会产生数据不一致的问题。
解决办法:读已提交@Transactional(isolation = Isolation.READ_COMMITTED)
- ** 不可重复读 **
一个事务,多次读取相同的数据,但是读取结果不一致,会在本事务中产生数据不一致的问题。
注意:1 不是脏读 2 在一个事务中
解决方法:@Transactional(isolation = Isolation.REPEATABLE_READ)
本质:一把行锁
- ** 幻影读 **
一个事务中,多次对整表进行查询统计,但是结果不一样,会在 本事务中产生数据不一致的问题。
解决方法:@Transactional(isolation = Isolation.SERIALIZABLE)
本质:表锁
总结(安全与效率对比)
- 安全与效率对比
并发安全:SERIALIZABLE
> REPEATABLE_READ
> READ_COMMITTED
运行效率:READ_COMMITTE
> REPEATABLE_READ
> SERIALIZABLE
- 数据库对隔离属性的支持
隔离属性的值 | Mysql | Oracle |
---|---|---|
isolation = Isolation.READ_COMMITTED | 支持 | 支持 |
isolation = Isolation.REPEATABLE_READ | 支持 | 不支持 |
isolation = Isolation.SERIALIZABLE | 支持 | 支持 |
问:Oracle不支持REPEATABLE_READ值,如何解决不可重复读 ?
采用多版本的的方式,解决不可重复读的问题
- 默认隔离属性
Spring会指定为ISOLATION_DEFAULT
,调用不同数据库所设置的默认隔离属性
MYSQL默认:REPEATABLE_READ
,不可重复读。SELECT @@tx_isolation;
可查看隔离级别
Oracle默认:READ_COMMITTED
,读已提交,想查看比较麻烦,可以百度搜索
隔离属性在实战中的建议
1、推荐使用 Spring 默认指定的 ISOLATION_DEFAULT
2、在未来的实战中,并发访问的几率很低
3、如果真的遇到并发问题,推荐使用乐观锁
Hibernate(JPA):Version
Mybatis:通过拦截器自定义开发
传播属性(PROPAGATION)
概念:描述了事务解决嵌套问题的特征
什么是事务的嵌套:它指的是一个大的事务中,包含若干个小的事务
事务的嵌套产生的问题:大事务中融入了很多小的事务,他们彼此影响,最终会导致外部大的事务,丧失了事务的原子性
传播属性的概念
传播属性的值 | 外部不存在事务 | 外部存在事务 | 用法 | 备注 |
---|---|---|---|---|
REQUIRED | 开启新的事务 | 融合到外部事务中 | @Transactional(propagation = Propagation.REQUIRED) | 增、删、改方法 |
SUPPORTS | 不开启事务 | 融合到外部事务中 | @Transactional(propagation = Propagation.SUPPORTS) | 查询方法 |
REQUIRES_NEW | 开启新的事务 | 挂起外部事务,创建新的事务 | @Transactional(propagation = Propagation.REQUIRES_NEW) | 日志记录方法中 |
NOT_SUPPORTED | 不开启事务 | 挂起外部事务 | @Transactional(propagation = Propagation.NOT_SUPPORTED) | 极其不常用 |
NEVER | 不开启事务 | 抛出异常 | @Transactional(propagation = Propagation.NEVER) | 极其不常用 |
MANDATORY | 抛出异常 | 融合到外部事物中 | @Transactional(propagation = Propagation.MANDATORY) | 极其不常用 |
Spring中传播属性的默认值是:REQUIRED
推荐传播属性的使用方式:
1、增删改 方法:使用默认值:REQUIRED
2、查询 方法:显示指定传播属性的值为:SUPPORTS
传播属性的值及其用法
只读属性(readOnly)
针对于进行查询操作的方法,可以加入只读属性,提高运行效率。默认值为false
@Transactional(readOnly = true)
超时属性(timeout)
指定了事务等待的最长时间。
1、为什么事务会进行等待?
当前事务访问数据时,有可能访问的数据被其他数据获取锁后加锁了,当前事务就必须要等待释放锁。
2、其他和用法
@Transactional(timeout = 2)
超时属性的默认值:-1
-1 表示超时属性由对应的数据库来指定(一般不会主动指定,-1 即可)
异常属性(rollbackFor、noRollbackFor)
Spring事务处理过程中:
1、默认 对RuntimeException及其子类,采用的是回滚的机制
2、默认 对Exception及其子类,采用的是提交事务的机制
@Transactional(rollbackFor = java.lang.Exception.class, xxx, xxx)
@Transactional(noRollbackFor = java.lang.RuntimeException, xxx, xxx)
** 建议:实战中使用 RuntimeException 及其子类,使用事务异常属性的默认值。**
事务属性常见配置总结
1、隔离属性 默认值
2、传播属性 Required(默认值)增删改、Supports 查询操作
3、只读属性 readOnly=false 增删改,true 查询操作
4、超时属性 默认值 -1
5、异常属性 默认值
增删改操作:@Transactional
查询操作:@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
基于标签的事务配置方式
基于注解@Transaction的事务配置回顾:
<!-- 业务类注入到spring -->
<bean id="userService" class="com.bearjun.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
<!-- DataSourceTransactionManager 事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
// 业务操作的类和方法
@Transactional
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
// TODO 业务操作
}
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
但是随着spring的迭代,基于标签的事务配置也经常使用:
<!-- 1 注入业务类-->
<bean id="userService" class="com.bearjun.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
<!-- 2 配置DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 3 配置事务管理的方法 -->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<!-- 相当于
@Transactional(isolation="" propagation="")
public void register{}
-->
<tx:attributes>
<tx:method name="register" isolation="DEFAULT" propagation="REQUIRED"/>
<tx:method name="login" isolation="xxxxx" propagation="xxxxx"/>
</tx:attributes>
</tx:advice>
<!-- 4 配置aop信息 -->
<aop:config>
<!-- 配置切入点:表达意思->给register添加事务,属性是xxx-->
<aop:pointcut id="pc" expression="execution(* com.bearjun.service.UserServiceImpl.register(..))"/>
<!-- 事务和切入点组装 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
基于标签的事务配置在 实战 中的应用方式:
<!-- 1 注入业务类-->
<bean id="userService" class="com.bearjun.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
<!-- 2 配置DataSourceTransactionManager-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 正式开发中,这部分会越写越多,所以抽取固定的前缀 -->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="register"/>
<!-- 约束编程时候, service中负责进行增删改操作的方法都以modify 开头,查询操作 命名无所谓-->
<tx:method name="modify*"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 正式开发中,一般以包切入点 -->
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.bearjun.service..*.*(..))"/>
<!-- 约束应用的过程中, 将service都放到service包下 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
其实spring有很多约束大于配置的操作,这也是spring的常规操作。
评论区