【学习笔记】Spring声明式事务管理

108 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

Spring声明式事务管理属性

@Transactional注解属性

事务传播行为【Propagation】

  • 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。

  • Propagation常用属性:

    REQUIRED传播行为:

    当 bookService 的 purchase()方法被另一个事务方法 checkout()调用时,它默认会在现有的事务内运行。因此在 checkout()方法的开始和终止边界内只有一个事务。

    如果余额不足,就一本书也买不了。

    REQUIRES_NEW传播行为:

    表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。

事务隔离级别【Isolation】

假设现在有两个并发的事务:Transaction01和Transaction02。

脏读【读取到了未提交的事务】:

①Transaction01 将某条记录的 AGE 值从 20 修改为 30,但是没有提交。

②Transaction02 读取了 Transaction01 更新后的值:30。

③随后Transaction01 回滚,AGE 值恢复到了 20。

④Transaction02 读取到的 30 就是一个无效的值。

不可重复读 【多次从一个字段中读取到的数据不相同】

①Transaction01 读取了 AGE 值为 20。

②Transaction02 将 AGE 值修改为 30,并且提交数据。

③Transaction01 再次读取 AGE 值为 30,和第一次读取不一致。

幻读【多次从一个表中读取的行不相同】

①Transaction01 读取了 STUDENT 表中的一部分数据。

②Transaction02 向 STUDENT 表中插入了新的行。

③Transaction01 读取了 STUDENT 表时,多出了一些行。

数据库系统必须具有隔离并发运行各个事务的能力,使它们不会互相影响。

隔离级别越高,数据一致性就越好,但并发性越弱。一个事务与其他事务之间的隔离等级【1,2,4,8】

隔离等级

  • 读未提交【1】:READ UNCOMMITED

    • 事务1可以读取事务2未提交的数据。存在问题:脏读。
  • 读已提交【2】:READ COMMITTED

    • 存在问题:可能出现不可重复读
  • 可重读【4】:REPEATABLE READ(字段锁)

    • 存在问题:可能出现幻读
  • 串行化【8】SERIALIZABLE(表的锁)

其他属性

  • 事务超时【timeout】

类型:int,单位:second。

默认值:-1【未设置强制回滚】

设置超时时间,到达指定时间后,会强制事务回滚。

  • 事务只读【readonly】

    一般事务方法中,只有查询操作时,才将事务设置为只读。

  • 事务回滚【rollbackFor/noRollbackFor】

    遇见回滚/不回滚的异常类。

示例代码:

//当前事务传播行为是自己属于一个事务。
    @Transactional(propagation = Propagation.REQUIRES_NEW,
                    timeout = 1,
                    noRollbackFor = ArithmeticException.class)
    public void purchase(String username, String isbn) {
        //查询book价格
        Integer price = bookshopDao.findBookPriceByIsbn(isbn);
​
        //测试事务超时
//        try {
//            Thread.sleep(5000);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
​
        //修改库存
        bookshopDao.updateBookStock(isbn);
        //修改余额
        bookshopDao.updateUserAccount(username, price);
​
        //演示遇到算数异常时,事务不回滚
        int i = 1/0;
    }

基于XML方式配置声明式事务管理

<!-- 配置事务切面 --> 
<aop:config>
<aop:pointcut expression="execution(* com.atguigu.tx.component.service.BookShopServiceImpl.purchase(..))" id="txPointCut"/> <!-- 将切入点表达式和事务属性配置关联到一起 --> 
<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/> 
</aop:config> 
<!-- 配置基于 XML 的声明式事务 --> 
<tx:advice id="myTx" transaction-manager="transactionManager"> 
    <tx:attributes> 
    <!-- 设置具体方法的事务属性 --> 
    <tx:method name="find*" read-only="true"/> 
    <tx:method name="get*" read-only="true"/> 
    <tx:method name="purchase" 
               isolation="READ_COMMITTED" 
               no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException" propagation="REQUIRES_NEW" 
               read-only="false" 
               timeout="10"/> 
    </tx:attributes> 
</tx:advice>

Spring5新特性

添加了新注解

  • 以@Nullable 为例

    位置:可以书写在方法&属性&参数前面。

    作用:表示当前方法或属性可以为空,消除了空指针异常。

Spring5整合Log4j2

  • 导入jar包

    <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.11.2</version>
        <scope>test</scope>
    </dependency>
    
  • 编写配置文件(不需要自己编写)

    • log4j2.xml

      日志级别以及优先级排序:OFF > FATAL > WHAR > INFO > DEBUG > TRACE > ALL

      高级别会打印低级别的内容。

Spring5整合Junit5

  • 导入jar包【注意:将Junit4的jar包删除】

    <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.7.2</version>
        <scope>test</scope>
    </dependency><dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.3.1</version>
            </dependency>
    
  • 使用注解进行整合。

    //方式一:
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    @ExtendWith(SpringExtension.class)
    //方式二:
    @SpringJUnitConfig(locations = "classpath:applicationContext.xml")