06-Spring之基于AOP的事务管理(针对数据库操作)
作者:互联网
事务管理(针对数据库操作)
注意spring中的事务控制是基于AOP的
事务控制应该针对Service层进行配置(比如银行转账的一个案例,A转账给B 100元,这里涉及到了两个操作,A账户的资金减少100元以及B账户资金增加100元,如果不添加事务操作,B账户资金增加时发生错误,增加失败,而A操作正常发生,这时A的账户会减少100元,但是B账户没有增加100元,这明显是不可取的。因此需要引入事务控制,只有当两个操作都成功时,才真正地进行操作,任意一个操作失败时,发生回滚,即两个操作都撤回)
简介
定义:事务管理是针对于一系列对数据库操作的管理(这是数据库的概念),一个事务包含一条或多条SQL语句
假设增加一条数据需要进行的操作:查询数据库查看是否已经存在这条数据,再将这条数据插入(注意这里是假设).这样子的一个操作可以看做事务(单纯的查找数据或是插入数据也可以看做一个事务).
我们在实际业务场景中,经常会遇到数据频繁修改读取的问题。在同一时刻,不同的业务逻辑对同一个表数据进行修改,这种冲突很可能造成数据不可挽回的错乱,所以我们需要用事务来对数据进行管理.
关键属性ACID:
原子性(atomicity):事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的.
一致性(consistency):表明应用系统从一个正确的状态到另一个正确的状态,比如转账中,我们在数据库中约束余额不能为负,因此转账前后账户的余额都不为负(注意这是数据库约束的),否则会产生回滚。
隔离性(isolation):可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏.
持久性(durability):一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除.
一个事务的简单流程
1--开始事务
2--执行一系列的SQL语句
3--如果操作都成功,则执行提交操作,否则回滚所有操作.
事务类型
编程式事务--通过编码方式实现事务,即类似于JDBC编程实现事务管理
声明式事务--管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务.
数据库并发的问题
- 脏读(dirty reads):当事务读取还未被提交的数据时,就会发生这种事件。举例来说:Transaction1 修改了一行数据,然后Transaction2在Transaction1还未提交修改操作之前读取了被修改的行。如果Transaction1回滚了修改操作,那么Transaction2读取的数据就可以看作是从未存在过的。
- 不可重复的读(non-repeatable reads):当事务两次读取同一行数据,但每次得到的数据都不一样时,就会发生这种事件。举例来说:Transaction1读取一行数据,然后Transaction2修改或删除该行并提交修改操作。当Transaction1试图重新读取该行时,它就会得到不同的数据值(如果该行被更新)或发现该行不再存在(如果该行被删除)。
- 虚读(phantom read):如果符合搜索条件的一行数据在后面的读取操作中出现,但该行数据却不属于最初的数据,就会发生这种事件。举例来说:Transaction1读取满足某种搜索条件的一些行,然后Transaction2插入了符合Transaction1的搜索条件的一个新行。如果Transaction1重新执行产生原来那些行的查询,就会得到不同的行。
事务的隔离级别
- ISOLATION_DEFAULT
- 默认级别,归属下列某一种
- ISOLATION_READ_UNCOMMITTED
- 表明可以发生误读、不可重复读和虚读。(可以读取未提交的数据)
- ISOLATION_READ_COMMITTED
- 表明能够阻止误读;可以发生不可重复读和虚读。(只能读取已提交的数据,解决脏读问题(Oracle默认级别))
- ISOLATION_REPEATABLE_READ
- 表明能够阻止误读和不可重复读;可以发生虚读。(是否读取其他事务提交后的数据,解决不可重复读的问题(MYSQL默认级别))
- ISOLATION_SERIALIZABLE
- 表明能够阻止误读、不可重复读和虚读。(是否读取其他事务提交添加后的数据,解决幻影读问题)
事务的传播行为
1、REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
// do something
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
// do something
}
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择----单独调用methodB()时,因为不存在事务,因此会开启一个新的事务,调用methodA()时,开启一个新的事务,而执行到methodB()时,由于已经存在一个事务了,因此会加入到这个事务中
2、SUPPORTS
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
// do something
}
// 事务属性为SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
// do something
}
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行----单独调用methodB()时,不存在事务,但methodB()是可以非事务执行的,因此以非事务执行,调用methodA()时,会开启一个新的事务,而执行到methodB()时,会加入到methodA()的事务
3、MANDATORY
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
// do something
}
// 事务属性为MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
// do something
}
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。单独调用methodB()时,因为当前没有事务,会抛出异常,调用methodA()不会发生错误
4、REQUIRES_NEW
新建事务,如果当前存在事务,把当前事务挂起。
5、NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
7、NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。
超时时间和是否是只读事务
超时时间:默认为-1,没有超时限制。如果有,以秒为单位进行设置。
是否是只读事务:建议查询时设置为只读。
基于xml的声明式事务控制
注意:如果不进行事务配置,那么一个操作是一个sqlSession(即一个mapper.function就是一个sqlSession)
<!--Spring中基于xml的声明式事务控制
1.配置事务管理器
2.配置事务通知
<tx:advice>标签配置事务通知
属性:
id:给事务通知提供一个唯一标志
transaction-manager:给事务通知提供一个事务管理器引用
3.配置AOP中的通用切入点表达式
4.建立事务通知和切入点表达式的对应关系
5.配置事务属性
isolation属性:用于指定事务隔离级别,默认值DEFAULT,表示使用数据库的默认级别。
propagation属性:用于指定事务的传播行为,默认值REQUIRED,表示一定会有事务,增删改的选择,查询可选SUPPORTS。
read-only属性:设置事务是否只读,只用查询方法才能设置为true,默认值时false便是读写。
timeout属性:用于指定事务的超时时间,默认值-1,如果制定了时间以s为单位。
rollback-for属性:用于指定一个异常,当产生该异常时事务回滚,产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚。
no-rollback-for属性:用于指定一个异常,当产生该异常时事务不回滚,产生其他异常时,事务回滚。没有默认值,表示任何异常都回滚。
-->
需要的命名空间:
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<!--1.配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2.配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--5.配置事务属性-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<!--优先级高-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--配置AOP-->
<aop:config>
<!--3.配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.whether.service.impl.*.*(..))"></aop:pointcut>
<!--4.建立事务通知和切入点表达式的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
基于注解的声明式事务控制(重点)
<!--Spring中基于注解的声明式事务控制
1.配置事务管理器
2.开启Spring对注解事务的支持
3.在需要事务支持的地方使用@Transactional注解
-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启Spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)//只读型事务配置
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
@Override
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer....");
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney() - money);
//2.4转入账户加钱
target.setMoney(target.getMoney() + money);
//2.5更新转出账户
accountDao.updateAccount(source);
int i = 1 / 0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
}
标签:事务管理,事务,06,读取,Spring,methodB,操作,数据,public 来源: https://www.cnblogs.com/whether/p/14166422.html