sping(springboot)事务

一、事务ACID特性

特性说明
原子性(Automicity)事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(Consistency)一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
隔离性(Isolation)可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
持久性(Durability)一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

二、事务隔离级别

事务的隔离级别定义一个事务可能受其他并发务活动活动影响的程度,可以把事务的隔离级别想象为这个事务对于事物处理数据的自私程度

在一个典型的应用程序中,多个事务同时运行,经常会为了完成他们的工作而操作同一个数据。并发虽然是必需的,但是会导致以下问题:

问题说明
脏读(Dirty read)脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。
不可重复读(Nonrepeatable read不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。不可重复读重点在修改。
幻读(Phantom reads)幻读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。幻读重点在新增或删除。

2.1、数据库隔离级别

在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。我们的数据库锁,也是为了构建这些隔离级别存在的。

隔离级别脏读(Dirty Read)不可重复读(NonRepeatable Read)幻读(Phantom Read)
未提交读(Read uncommitted)可能可能可能
已提交读(Read committed)不可能可能可能
可重复读(Repeatable read)不可能不可能可能
可串行化(Serializable )不可能不可能不可能
  • 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
  • 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
  • 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
  • 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

事务隔离性的四种隔离级别和实现原理这里就不作阐述,感兴趣的可以看以下两篇博客:

2.2、spring事务隔离级别

在理想状态下,事务之间将完全隔离,从而可以防止这些问题发生。然而,完全隔离会影响性能,因为隔离经常涉及到锁定在数据库中的记录(甚至有时是锁表)。完全隔离要求事务相互等待来完成工作,会阻碍并发。因此,可以根据业务场景选择不同的隔离级别。TransactionDefinition 接口中定义了五个表示隔离级别的常量。

隔离级别说明
TransactionDefinition.ISOLATION_DEFAULT这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,该级别就是 TransactionDefinition.ISOLATION_READ_COMMITTED;
TransactionDefinition.ISOLATION_READ_UNCOMMITTED允许读取尚未提交的更改。该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据,该级别不能防止脏读和不可重复读,因此很少使用该隔离级别;
TransactionDefinition.ISOLATION_READ_COMMITTED(Oracle 默认级别)允许从已经提交的并发事务读取。该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ(MYSQL默认级别)对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是,这将严重影响程序的性能,通常情况下也不会用到该级别。

三、spring事务配置方式

在使用传统的事务编程策略时,程序代码必然和具体的事务操作代码耦合,如下所示:

// JDBC事务
Connection conn = getConnection();
conn.setAutoCommit(false);
...
// 业务实现
...
if 正常
    conn.commit();
if 失败
    conn.rollback();
// Hibernate事务
Session s = getSession();
Transaction tx = s.beginTransaction();
...
// 业务实现
...
if 正常
    tx.commit();
if 失败
    tx.rollback();

因此,当应用需要在不同的事务策略之间切换时,开发者必须手动修改程序代码。使用Spring事务管理策略,就可以避免这种尴尬。因为Spring的事务管理不需与任何特定的事务API耦合,并且其提供了两种事务管理方式:编程式事务管理和声明式事务管理。对不同的持久层访问技术,编程式事务提供一致的事务编程风格,通过模板化的操作一致性地管理事务;而声明式事务基于Spring AOP实现,却并不需要程序开发者成为AOP专家,亦可轻易使用Spring的声明式事务管理。
Spring支持编程式事务管理以及声明式事务管理两种方式。

3.1、Spring 事务管理 API

Spring 框架中,最重要的事务管理的 API 有三个:TransactionDefinitionPlatformTransactionManagerTransactionStatus
所谓事务管理,实质上就是按照给定的事务规则来执行提交或者回滚操作。其中,“给定的事务规则”是用 TransactionDefinition 表示的,“按照……来执行提交或者回滚操作”是用 PlatformTransactionManager 表示的,而 TransactionStatus 可以看作代表事务本身。

  • TransactionDefinition接口
    TransactionDefinition 接口用于定义一个事务的规则,它包含了事务的一些静态属性,比如:事务传播行为、超时时间等。同时,Spring 还为我们提供了一个默认的实现类:DefaultTransactionDefinition,该类适用于大多数情况。如果该类不能满足需求,可以通过实现 TransactionDefinition 接口来实现自己的事务定义。

    TransactionDefinition接口包含与事务属性相关的方法,如下所示:
    在这里插入图片描述
    默认的实现类DefaultTransactionDefinition相关属性方法如下所示:

在这里插入图片描述
TransactionDefinition 接口只提供了获取属性的方法,而没有提供相关设置属性的方法。因为,事务属性的设置完全是程序员控制的,因此程序员可以自定义任何设置属性的方法,而且保存属性的字段也没有任何要求。唯一的要求的是,Spring 进行事务操作的时候,通过调用以上接口提供的方法必须能够返回事务相关的属性取值。例如,TransactionDefinition 接口的默认的实现类 —— DefaultTransactionDefinition 就同时定义了一系列属性设置和获取方法。
  TransactionDefinition 接口定义的事务规则包括:事务隔离级别、事务传播行为、事务超时、事务的只读属性和事务的回滚规则

  • PlatformTransactionManager接口
    Spring事务策略是通过PlatformTransactionManager接口体现的,该接口是Spring事务策略的核心。该接口的源代码如下:
public interface PlatformTransactionManager {

    //平台无关的获得事务的方法
    //根据指定的传播行为返回当前活动的事务或创建一个新的事务
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

    //平台无关的事务提交方法
    void commit(TransactionStatus status) throws TransactionException;

    //平台无关的事务回滚方法
    void rollback(TransactionStatus status) throws TransactionException;
}

它的实现类如下图所示:
在这里插入图片描述

可以看出,PlatformTransactionManager是一个与任何事务策略分离的接口。PlatformTransactionManager接口有许多不同的实现类,应用程序面向与平台无关的接口编程,而对不同平台的底层支持由PlatformTransactionManager接口的实现类完成,故而应用程序无须与具体的事务API耦合。因此使用PlatformTransactionManager接口,可将代码从具体的事务API中解耦出来。

在PlatformTransactionManager接口内,包含一个getTransaction(TransactionDefinition definition)方法,该方法根据一个TransactionDefinition参数,返回一个TransactionStatus对象。TransactionStatus对象表示一个事务,该事务可能是一个新的事务,也可能是一个已经存在的事务对象,这由TransactionDefinition所定义的事务规则所决定。

  • TransactionStatus接口
    PlatformTransactionManager.getTransaction(@Nullable TransactionDefinition definition) 方法返回一个 TransactionStatus 对象,该对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。TransactionStatus 接口提供了一个简单的控制事务执行和查询事务状态的方法。该接口的源代码如下:
public interface TransactionStatus extends SavepointManager, Flushable {
	boolean isNewTransaction();
	boolean hasSavepoint();
	void setRollbackOnly();
	boolean isRollbackOnly();
	void flush();
	boolean isCompleted();
}

它的实现类如下图所示:
在这里插入图片描述

3.2、编程式事务管理

在 Spring 出现以前,编程式事务管理对基于 POJO 的应用来说是唯一选择。用过 Hibernate 的人都知道,我们需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。通过 Spring 提供的事务管理 API,我们可以在代码中灵活控制事务的执行。在底层,Spring 仍然将事务操作委托给底层的持久化框架来执行。

编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。

  • 1、基于底层 API 的编程式事务管理
    下面给出一个基于底层 API 的编程式事务管理的示例,
    基于PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三个核心接口,我们完全可以通过编程的方式来进行事务管理。
public class BankServiceImpl implements BankService {
    private BankDao bankDao;
    private TransactionDefinition txDefinition;
    private PlatformTransactionManager txManager;
    
    
    public boolean transfer(Long fromId, Long toId, double amount) {
        // 获取一个事务
        TransactionStatus txStatus = txManager.getTransaction(txDefinition);
        boolean result = false;
        try {
            result = bankDao.transfer(fromId, toId, amount);
            txManager.commit(txStatus);    // 事务提交
        } catch (Exception e) {
            result = false;
            txManager.rollback(txStatus);      // 事务回滚
            System.out.println("Transfer Error!");
        }
        return result;
    }
}

相应的配置文件如下所示:

<bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
    <property name="bankDao" ref="bankDao"/>
    <property name="txManager" ref="transactionManager"/>
    <property name="txDefinition">
    <bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
    </bean>
    </property>
</bean>

如上所示,我们在BankServiceImpl类中增加了两个属性:一个是 TransactionDefinition 类型的属性,它用于定义事务的规则;另一个是 PlatformTransactionManager 类型的属性,用于执行事务管理操作。如果一个业务方法需要添加事务,我们首先需要在方法开始执行前调用PlatformTransactionManager.getTransaction(…) 方法启动一个事务;创建并启动了事务之后,便可以开始编写业务逻辑代码,然后在适当的地方执行事务的提交或者回滚。

  • 2.基于 TransactionTemplate 的编程式事务管理
    当然,除了可以使用基于底层 API 的编程式事务外,还可以使用基于 TransactionTemplate 的编程式事务管理。通过上面的示例可以发现,上述事务管理的代码散落在业务逻辑代码中,破坏了原有代码的条理性,并且每一个业务方法都包含了类似的启动事务、提交/回滚事务的样板代码。Spring 也意识到了这些,并提供了简化的方法,这就是 Spring 在数据访问层非常常见的 模板回调模式
public class BankServiceImpl implements BankService {
    private BankDao bankDao;
    private TransactionTemplate transactionTemplate;
   
    public boolean transfer(final Long fromId, final Long toId, final double amount) {
        return (Boolean) transactionTemplate.execute(new TransactionCallback(){
            public Object doInTransaction(TransactionStatus status) {
                Object result;
                try {
                        result = bankDao.transfer(fromId, toId, amount);
                    } catch (Exception e) {
                        status.setRollbackOnly();
                        result = false;
                        System.out.println("Transfer Error!");
                }
                return result;
            }
        });
    }
}

相应的配置文件如下所示:

<bean id="bankService" class="footmark.spring.core.tx.programmatic.template.BankServiceImpl">
    <property name="bankDao" ref="bankDao"/>
    <property name="transactionTemplate" ref="transactionTemplate"/>
</bean>

TransactionTemplate 的 execute() 方法有一个 TransactionCallback 类型的参数,该接口中定义了一个 doInTransaction() 方法,通常我们以匿名内部类的方式实现 TransactionCallback 接口,并在其 doInTransaction() 方法中书写业务逻辑代码。这里可以使用默认的事务提交和回滚规则,这样在业务代码中就不需要显式调用任何事务管理的 API。doInTransaction() 方法有一个TransactionStatus 类型的参数,我们可以在方法的任何位置调用该参数的 setRollbackOnly() 方法将事务标识为回滚的,以执行事务回滚。

此外,TransactionCallback 接口有一个子接口 TransactionCallbackWithoutResult,该接口中定义了一个 doInTransactionWithoutResult() 方法,TransactionCallbackWithoutResult 接口主要用于事务过程中不需要返回值的情况。当然,对于不需要返回值的情况,我们仍然可以使用 TransactionCallback 接口,并在方法中返回任意值即可。

3.3、声明式事务管理

声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中作相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。总的来说,声明式事务得益于 Spring IoC容器 和 Spring AOP 机制的支持:IoC容器为声明式事务管理提供了基础设施,使得 Bean 对于 Spring 框架而言是可管理的;而由于事务管理本身就是一个典型的横切逻辑(正是 AOP 的用武之地),因此 Spring AOP 机制是声明式事务管理的直接实现者。

显然,声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要在XML文件中配置或者添加注解就可以获得完全的事务支持。因此,通常情况下,笔者强烈建议在开发中使用声明式事务,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极大方便后期的代码维护。
唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置

  • 1、基于 < tx > 命名空间的声明式事务管理

    Spring 2.x 引入了 命名空间,结合使用 命名空间,带给开发人员配置声明式事务的全新体验,配置变得更加简单和灵活。总的来说,开发者只需基于和命名空间在XML中进行简答配置便可实现声明式事务管理。下面基于使用Hibernate事务管理的配置文件:

<!-- 配置 DataSourece -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName">
        <value>com.mysql.jdbc.Driver</value>
    </property>
    <property name="url">
        <value>jdbc:mysql://localhost:3306/ssh</value>
    </property>
    <property name="username">
        <value>root</value>
    </property>
    <property name="password">
        <value>root</value>
    </property>
</bean>

<!-- 配置 sessionFactory -->
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <!-- 数据源的设置 -->
    <property name="dataSource" ref="dataSource" />
    <!-- 用于持久化的实体类类列表 -->
    <property name="annotatedClasses">
        <list>
            <value>cn.edu.tju.rico.model.entity.User</value>
            <value>cn.edu.tju.rico.model.entity.Log</value>
        </list>
    </property>
    <!-- Hibernate 的配置 -->
    <property name="hibernateProperties">
        <props>
            <!-- 方言设置   -->
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <!-- 显示sql -->
            <prop key="hibernate.show_sql">true</prop>
            <!-- 格式化sql -->
            <prop key="hibernate.format_sql">true</prop>
            <!-- 自动创建/更新数据表 -->
            <prop key="hibernate.hbm2ddl.auto">update</prop>
        </props>
    </property>
</bean>

<!-- 配置 TransactionManager -->
<bean id="txManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 配置事务增强处理的切入点,以保证其被恰当的织入 -->    
<aop:config>
    <!-- 切点 -->
    <aop:pointcut expression="execution(* cn.edu.tju.rico.service.impl.*.*(..))"
        id="bussinessService" />
    <!-- 声明式事务的切入 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="bussinessService" />
</aop:config>

<!-- 由txAdvice切面定义事务增强处理 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- get打头的方法为只读方法,因此将read-only设为 true -->
        <tx:method name="get*" read-only="true" />
        <!-- 其他方法为读写方法,因此将read-only设为 false -->
        <tx:method name="*" read-only="false" propagation="REQUIRED"
            isolation="DEFAULT" />
    </tx:attributes>
</tx:advice>

事实上,

Spring配置文件中关于事务的配置总是由三个部分组成,即:DataSource、TransactionManager和代理机制三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
其中,DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。

  • 2、基于 @Transactional 的声明式事务管理

除了基于命名空间的事务配置方式,Spring 还引入了基于 Annotation 的方式,具体主要涉及@Transactional 标注。@Transactional 可以作用于接口、接口方法、类以及类方法上:
当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性;
当作用于方法上时,该标注来覆盖类级别的定义。
如下所示:

@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) {
    return bankDao.transfer(fromId, toId, amount);
}

@Transactionall注解如图所示:
在这里插入图片描述
Spring 使用 BeanPostProcessor 来处理 Bean 中的标注,因此我们需要在配置文件中作如下声明来激活该后处理 Bean,如下所示:

<tx:annotation-driven transaction-manager="transactionManager"/>

与前面相似,transaction-manager、datasource 和 sessionFactory的配置不变,只需将基于< tx >和< aop >命名空间的配置更换为上述配置即可。

  • 3.声明式事务的常用配置
参 数 名 称功 能 描 述
readOnly该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)
rollbackFor该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称@Transactional(rollbackForClassName=”RuntimeException”)指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
noRollbackFor该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”)指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})
propagation该属性用于设置事务的传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
timeout该属性用于设置事务的超时秒数,默认值为-1表示永不超时

3.4、Spring 声明式事务的本质

就Spring 声明式事务而言,无论其基于 < tx > 命名空间的实现还是基于 @Transactional 的实现,其本质都是 Spring AOP 机制的应用:即通过以@Transactional的方式或者XML配置文件的方式向业务组件中的目标业务方法插入事务增强处理并生成相应的代理对象供应用程序(客户端)使用从而达到无污染地添加事务的目的。如下图所示:

在这里插入图片描述

四、spring事务传播机制

事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。TransactionDefinition 接口中定义了七个表示事务传播机制常量。

传播机制说明
TransactionDefinition.PROPAGATION_REQUIREDSpring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
TransactionDefinition.PROPAGATION_REQUIRES_NEW该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
TransactionDefinition.PROPAGATION_SUPPORT如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
TransactionDefinition.PROPAGATION_NOT_SUPPORT该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
TransactionDefinition.PROPAGATION_NEVER该传播机制不支持外层事务,即如果外层有事务就抛出异常
TransactionDefinition.PROPAGATION_MANDATORY与NEVER相反,如果外层没有事务,则抛出异常
TransactionDefinition.PROPAGATION_NESTED该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。

传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。

五、spring事务只读

事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition接口中,以 boolean 类型来表示该事务是否只读。

如果一个事务只对数据库执行读操作,那么该数据库就可能利用那个事务的只读特性,采取某些优化措施。通过把一个事务声明为只读,可以给后端数据库一个机会来应用那些它认为合适的优化措施。由于只读的优化措施是在一个事务启动时由后端数据库实施的, 因此,只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法来说,将事务声明为只读才有意义。

六、spring事务超时

通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。

为了使一个应用程序很好地执行,它的事务不能运行太长时间。因此,声明式事务的下一个特性就是它的超时。
假设事务的运行时间变得格外的长,由于事务可能涉及对数据库的锁定,所以长时间运行的事务会不必要地占用数据库资源。这时就可以声明一个事务在特定秒数后自动回滚,不必等它自己结束。
由于超时时钟在一个事务启动的时候开始的,因此,只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED)的方法来说,声明事务超时才有意义。

七、spring事务回滚规则

在默认设置下,事务只在出现运行时异常(runtime exception)时回滚,而在出现受检查异常(checked exception)时不回滚(这一行为和EJB中的回滚行为是一致的)。
不过,可以声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。

检查型异常和非检查型异常,请参考:https://www.cnblogs.com/xh_chiang/p/6850847.html

rollbackFor:该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。

  • 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
  • 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

相关文章

1.有关Spring事务,看这一篇就足够了https://www.cnblogs.com/mseddl/p/11577846.html
2.Spring 事务管理机制概述https://blog.csdn.net/justloveyou_/article/details/73733278
3.SpringBoot事务详解https://blog.csdn.net/weixin_33656548/article/details/80447460
4.Innodb中的事务隔离级别实现原理:https://blog.csdn.net/matt8/article/details/53096405

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值