侧边栏壁纸
  • 累计撰写 98 篇文章
  • 累计创建 85 个标签
  • 累计收到 9 条评论

spring系列笔记 - 第二十一章 Spring事务属性

bearjun
2021-07-20 / 0 评论 / 0 点赞 / 644 阅读 / 5,930 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-07-20,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

什么是spring的事物属性

描述物体特征的一系列值

事务属性:描述事务特征的一系列值

  1. 隔离属性
  2. 传播属性
  3. 只读属性
  4. 超时属性
  5. 异常属性

如何添加事务属性:

//对应的事物属性     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

  • 数据库对隔离属性的支持
隔离属性的值MysqlOracle
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的常规操作。

0

评论区