李琪的技术专栏 System Research

Spring事务

2020-04-13
Clear Li

阅读:


在记录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

事务的传播行为,应该是防止脏读不可重复读那一套,回头再整理吧。。。


下一篇 希尔排序