在记录Spring事务之前先把事务的性质记录一下吧。。。。
事务的性质
(1)原子性
这个是最容易理解的,原子性是指事务包含的所有操作要么全部成功,要么全部失败。
(2)一致性
一致性是指事务必须从数据库的一个状态变换到另一个一致性状态。就是说一个事务执行之前和之后都必须处于一致性状态。。比如两个人转账,无论怎么转账,钱的总量不能变化。
(3)隔离性
隔离性是指当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰。做到这一点需要数据库的隔离级别来实现,之后再写吧。
(4)持久性
持久性是指,一旦事务被提交,对数据库的数据改变就是永久性的,数据库系统遇到故障的情况下也不会丢失提交事务的操作。这个我的理解就是,当提交事务时,只有真正保存数据后,数据库才会将事务成功提交返回给用户。否则返回事务提交失败。
手动写事务
如下代码,很简单。
@Component
public class Transaction {
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
/**
* 无建议(默认)
* @description 开启一个事务<br>
*
* @author ClearLi
* @date 2020/4/13 20:06
* @param
* @return void
*/
public TransactionStatus begin(){
//获取到事务的状态
return dataSourceTransactionManager
.getTransaction(new DefaultTransactionAttribute());
}
/**
* 无建议(默认)
* @description 提交事务
* @author ClearLi
* @date 2020/4/13 20:09
* @param
* @return void
*/
public void commit(TransactionStatus status){
dataSourceTransactionManager.commit(status);
}
/**
* 无建议(默认)
* @description 回滚 发生异常的时候会回滚
* @author ClearLi
* @date 2020/4/13 20:11
* @param status
* @return void
*/
public void rollback(TransactionStatus status){
dataSourceTransactionManager.rollback(status);
}
}
写入开启事务,提交事务以及事务回滚就可以完成一个简单的事务。下面是使用的例子
@Autowired
private Transaction transaction;
public void addUser(String username ,String password){
TransactionStatus begin= transaction.begin();
try {
userDao.addUser(username,password);
userDao.addUser("username","password");
transaction.commit(begin);
}catch (Exception e){
transaction.rollback(begin);
}
}
手动try catch一下,这样做的话比较灵活一些
Spring事务Xml配置
xml配置,配置事务,配置事务增强(对应的增删改查不同的处理模式)Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置
<!-- 配置事物 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice transaction-manager="dataSourceTransactionManager" id="txAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.clear.service.*.*(..))"
id="pt" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
</aop:config>
Spring事务注解配置
xml配置
<!-- 配置事物 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
首先配置事务配置,打开事务注解即可
在要开启事务的方法或者类上注解@Transactional
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addLog(){
logDao.addLog();
System.out.println("插入日志成功");
}
事务的传播行为
事务的传播行为属实有点多,可谓是面面俱到了。有以下的事务传播行为
- PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。
重点
PROPAGATION_REQUIRED,这个是默认的,假如有事务嵌套的情况,会以一个事务为准,不会开两个事务的。
PROPAGATION_REQUIRES_NEW,创建新事物,假如有事务嵌套的情况那么就各自用各自的事务。下面看例子
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUser(String username,String password){
userDao.addUser(username,password);
logService.addLog();//其中也开启了@Transactional(propagation = Propagation.REQUIRES_NEW)
//int a = 1/0;
}
上面的用户添加和日志添加都会使用自己的事务,假如说添加用户报出了异常那么仅仅回滚添加用户这个事务,而添加日志则会正常插入到数据库中。
事务的其他配置
@Transactional(
readOnly = false, // 读写事务
timeout = -1, // 事务的超时时间不限制
noRollbackFor = ArithmeticException.class, // 遇到数学异常不回滚
isolation = Isolation.DEFAULT, // 事务的隔离级别,数据库的默认
propagation = Propagation.REQUIRES_NEW // 事务的传播行为
)
readOnly
设置是否可以写入数据。这个配置,也是为了防止意外的插入数据或删除数据。
timeout
事务的超时时间,总不可能在线程死锁后,其他的事务都不执行了吧。当超过设置时间后将会爆出运行时异常错误,进行事务回滚。。
noRollbackFor
设置一个异常类,当发生该异常时不会滚数据。例如上面例子的数学异常
propagation
事务的传播行为,应该是防止脏读不可重复读那一套,回头再整理吧。。。