12.Spring声明式事务
本文最后更新于2023.11.30-17:53
,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥。
1.什么是事务?
一组数据库的增删改查,创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作,要么同时成功,要么同时失败,作为一个整体来考虑,我们把他叫做事务。
2.事务的4大特性 ACID
A:原子性:
C:一致性:
在一个事务中 ,数据库操作(查询) 应该保持查询结果一致
I:隔离性
多个事务之间,相互隔离互不干扰。
D:持久性
数据一旦保存就是持久的(提交之后)
3.事务的隔离规则
最低的隔离级别:Read-uncommited(读未提交)
db补充:
脏读
概念:当一个事务 正在访问数据,并且对数据进行了修改,而这种修改还没有被提交到数据库,这是另外一个事务也访问了这个数据,然后使用了这个数据.
1老马 原工资是 1000块,财务人员将老马的工资 改为了 8000 (开启了事务,但是并未提交)
2老马 查询工资 发现自己要发的工资是8000快。很开心。
3 而财务发现 这个操作有问题 要进行回滚,工资其实还是1000.
4.老马读到了一条没有提交的数据8000 所谓的脏读。
处理方法:设置数据库的隔离级别,只允许访问 提交了的数据
设置 隔离级别:READ-COMMITTED
不可重复读
概念:指在同一个事务A内,多次读取了同一个数据,A还没有结束,事务B也访问了同一个数据,那么在事务A读取的两次数据中,由于事务B对 数据进行了修改,导致一个事务A读取的 数据是不一样的,那么这种在一个事务内读到两次不同数据的,叫做 不可重复读
1.事务A 老马读取了自己的工资 8000块,操作并没有完成
2.事务B 把老马的工资 改成了1000快,并进行了提交(committed)
3.事务A 老马挑了一个8000块的电脑,付款时,再次查询了工资 结果只有1000快
进行了一次不可重复的读
解决办法; 原理(行锁) 只有事务A 完全提交了数据,事务B 才能去访问以及读取 老马的工资
设置隔离级别:repeatable read
幻读
概念:幻读当事务 不是独立执行时 发生的一种现象: 例如 第一个事务 对一个表中的 数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也在修改这个表中的数据,这种修改时 向表中插入一条数据。,那么发生第一个事务的用户就会发现,表中还有没有修改的数据行,称之为幻读。
1.目标工资为1000的员工有 10个人
2事务1 :查询所有工资为1000的员工, 得到的结果为10
3事务2 插入了一条数据 新员工 工资也为1000
4 事务1 再去读取工资为1000的员工 结果是11条
解决办法:(锁表) 在事务的操作执行完之前,其他事务都不可以添加新数据
设置 隔离级别;serializable
- read-uncommitted:会有 脏读 不可重复度 幻读的问题
- read-committed :解决了 脏读 但仍然有 不可重复读 和幻读
- repeatable-read :解决了 脏读 不可重复读的问题 幻读没有解决
- serializable :解决了 一切,但是性能底下。
传播行为
- REQUIRED:表示当前这个方法 的执行必须要在 事务中,如果已经处于事务环境,则直接使用当前事务,如果没有处于 则开启一个新的事务。
method1() method2()
method1 调用了 method2 那么在1已经开启事务的情况下 那么method2 会直接和1 公用一个事务。
- SUPPORTS:表示当前方法如果 已经开启事务 那么就使用 同一个事务,如果没开启事务,那么就不开启新事务。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- MANDATORY:表示当前方法的执行,必须要处于事物环境,否则抛出异常。
- REQUIRES_NEW:表示当前方的执行 必须开启一个新的事务,如果已经存在事务,那么原先的事务暂停。
- NEVER:该方法的执行 必须不在事务里。
- NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。
事务超时
timeout:设置 一个事务允许执行的最长时间,超时候 会整个回滚。
结论
在实战中事务的使用方式
如果当前业务方法是一组 增、改、删 可以这样设置事务 @Transactional
如果当前业务方法是一组 查询 可以这样设置事务 @Transactionl(readOnly=true)
如果当前业务方法是单个 查询 可以这样设置事务 @Transactionl(propagation=propagation.SUPPORTS ,readOnly=true)
使用注解完成声明式事务
1.配置 事务管理器 开启事务注解:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!--启用注解 -->
<tx:annotation-driven transaction-manager="transactionManager" ></tx:annotation-driven>
2.可以在对应的接口 类 方法上面加上注解,可以配置隔离级别,传播方式,超时时间,以及哪些异常回滚,哪些异常不回滚。
package com.rimi.service.impl;
import com.rimi.bean.User;
import com.rimi.dao.UserMapper;
import com.rimi.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public User selectUser(int userid) {
return userMapper.selectUserById(userid);
}
@Transactional(propagation = Propagation.SUPPORTS)
public boolean transformMoney(int payId, int getId, int money) {
int result1 = 0;
int result2 = 0;
result1 = userMapper.updateUser(payId,0-money);
System.out.println(5/0);
result2 = userMapper.updateUser(getId,money);
return result1+result2==2;
}
}
基于配置的
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 传播行为 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 切面 -->
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.gxzh.dsmng.*.service.*.*(..))" /> </aop:config>
作者:涛哥
链接:https://ltbk.net/back/spring_family/spring/article/406.html
文章版权归作者所有,未经允许请勿转载。