拿下 Spring 事务

手撕阿里 Spring 框架:AOP、IOC、注解、事务,带你统统拿下 前言 Spring 框架自 2002 年诞生以来一直备受开发者青睐,它包括 SpringMVC、SpringBoot、Spring Cloud、Spring Cloud Dataflow 等解决方案。有人亲切的称之为:Spring 全家桶。 很多研发人员把 spring 看作心目中最好的 java 项目,没有之一。所以这是重点也是难点,工作中必须会,面试时肯定考。那么,花费 10 分钟,由阿里一线架构师,带你梳理 Spring 框架相关知识。 以下所整理的 Spring 相关面试题(含答案)、Spr 阅读详情

什么是事务

事务是数据库操作的最基本单元,是逻辑上的一组操作,要么都成功,要么都失败。是一个不可分割的工作单元。

事务的使用

事务具有 4 个特性:原子性、一致性、隔离性】持久性,简称为 ACID 特性。

  • 原子性(Atomicity):一个事务是一个不可分割的工作单位,一个事务中包括的操作要么都成功要么都失败。
  • 一致性(Consistency):事务必须保证数据库从一个一致性状态变到另一个一致性状态。比如转账的总金额,不能转着转着总金额少了或者多了。大部分一致性的需求需要程序员写业务代码保证。
  • 隔离性(Isolation):一个事务的执行不能被其它事务干扰,即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能互相打扰。
  • 持久性(Durability):持久性也称为永久性,指一个事务一旦提交,它对数据库中数据的改变就是永久性的,后面的其它操作和故障都不应该对其有任何影响。

为什么要用事务

举例:银行转账。小明给小红转 100 元。小明需要减少余额 100,小红需要增加余额 100。这是两个操作,需要一起成功。如果在小明转账成功之后发生了异常,就会出现小明 减 100 余额,但是小红并没有加 100 余额。就会造成钱丢失的情况。这是绝对不允许的。伪代码如下:

public void accountMoney() {
    int money = 100;
    //小明 少 100
    userDao.reduceMoney(money);
    // 其他业务 发生异常
    int i = 1/0;
    //小红 多 100
    userDao.addMoney(money);
}

事务管理方式

Spring 支持 2 种事务管理方式。

  • 编程式事务管理 编程式事务管理是通过编写代码实现的事务管理。可以根据需求规定事务从哪里开始,到哪里结束,拥有很高的灵活性。但是这种方式,会使业务代码与事务规则高度耦合,难以维护,因此我们很少使用这种方式对事务进行管理。所以,本文给大家介绍的是如何使用声明式事务管理。
  • 声明式 声明式事务管理可以通过 2 种方式实现,分别是 XML和注解方式。Spring 在进行声明式事务管理时,底层使用了 AOP 。

事务管理器

Spring 并不会直接管理事务,而是通过事务管理器对事务进行管理的。

PlatformTransactionManager

Spring 提供了一个 PlatformTransactionManager 接口,这个接口被称为 Spring 的事务管理器,其源码如下:

public interface PlatformTransactionManager {
// 根据传入的 TransactionDefinition 对象获取一个事务状态对象
   TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;

// 提交事务
   void commit(TransactionStatus var1) throws TransactionException;
// 回滚事务
   void rollback(TransactionStatus var1) throws TransactionException;
}

该接口的源码很简单。这个接口针对不同的框架提供了不同的实现类,如下:

实现类说明
org.springframework.jdbc.datasource.DataSourceTransactionManager提供给 Spring JDBC 、MBatis 的事务管理器
org.springframework.orm.hibernate5.HibernateTransactionManager提供给 Hibernate 的事务管理器
org.springframework.orm.jpa.JpaTransactionManager提供给 JPA 的事务管理器
org.springframework.jdo.JdoTransactionManager提供给 Jdo 的事务管理器
org.springframework.transaction.jta.JtaTransactionManager提供给 JTA 的事务管理器

注意:这些实现类,需要导入对应的依赖才能看到。 该接口中还有两个对象,分别是 TransactionDefinition 和 TransactionStatus。

TransactionDefinition

  • TransactionDefinition:事务定义,定义了事务的名称,传播属性,事务隔离级别,是否只读,超时时间。
public interface TransactionDefinition {

    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;

    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;
    int ISOLATION_READ_COMMITTED = 2;
    int ISOLATION_REPEATABLE_READ = 4;
    int ISOLATION_SERIALIZABLE = 8;

    int TIMEOUT_DEFAULT = -1;

    default int getPropagationBehavior() {
        return 0;
    }

    default int getIsolationLevel() {
        return -1;
    }

    default int getTimeout() {
        return -1;
    }

    default boolean isReadOnly() {
        return false;
    }

    @Nullable
    default String getName() {
        return null;
    }

    static TransactionDefinition withDefaults() {
        return StaticTransactionDefinition.INSTANCE;
    }
}

  • PROPAGATION_** 0 ~ 7 代表的是事务传播行为

  • ISOLATION_** -1 ~ 8 代表的是事务的隔离级别

  • TIMEOUT_DEFAULT 默认的超时时间,-1,代表使用数据库的超时时间

  • getPropagationBehavior:获取事务的传播行为,默认为 PROPAGATION_REQUIRED

  • getIsolationLevel:获取事务的隔离级别,默认为所使用数据库的隔离级别

  • getTimeout:获取事务的超时时间

  • isReadOnly:事务是否只读

  • getName:获取事务的名称

TransactionStatus

  • TransactionStatus:事务状态,保存了事务执行过程中的状态。
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

    boolean hasSavepoint();

    @Override
    void flush();

}

方法说明如下:

名称说明
hasSavepoint事务内部是否带有保存点
flush刷新事务
  • TransactionExecution
public interface TransactionExecution {

   boolean isNewTransaction();

   void setRollbackOnly();

   boolean isRollbackOnly();

   boolean isCompleted();

}

方法说明如下:

名称说明
isNewTransaction当前事务是否是新的
setRollbackOnly设置事务回滚
isRollbackOnly事务是否已被标记为回滚
isCompleted事务是否完成,即是否已经提交或回滚
  • SavepointManager
public interface SavepointManager {

    Object createSavepoint() throws TransactionException;

    void rollbackToSavepoint(Object savepoint) throws TransactionException;

    void releaseSavepoint(Object savepoint) throws TransactionException;

}

方法说明如下:

名称说明
createSavepoint创建保存点
rollbackToSavepoint回滚到给定的保存点
releaseSavepoint释放给定的保存点

有一个默认的抽象实现 AbstractTransactionStatus,对 TransactionExecution、savepoint、SavepointManager 有具体的实现逻辑,代码有点多,就不贴了,但是非常好理解。 对 TransactionExecution、savepoint、SavepointManager 有具体的实现逻辑,代码有点多,就不贴了,但是非常好理解。 DefaultTransactionStatus 又继承了 AbstractTransactionStatus,继续进行扩充。

public class DefaultTransactionStatus extends AbstractTransactionStatus {

    // 具体事务对象
    @Nullable
    private final Object transaction;

    //是否是新开启的事务(=true时才会提交事务)
    private final boolean newTransaction;

// 是否是新建的同步器(=true时才会执行回调事件)
    private final boolean newSynchronization;

// 是否只读
    private final boolean readOnly;
// 是否已调试
    private final boolean debug;

// 挂起的资源信息(事务传播行为要求挂起当前事务时,挂起的事务暂存信息,执行完后用于恢复)
    @Nullable
    private final Object suspendedResources;

    //省略。。。。
}

事务传播行为

事务传播行为指的是,多事务方法之间进行调用时,这个过程中事务应该如何进行管理。例如,事务方法 A 在调用事务方法 B 时,B 方法是在调用者 A 方法的事务中运行呢,还是为自己开启一个新事务运行,这就是由事务方法 B 的事务传播行为决定的。

事务方法:能让数据库表数据发生改变的方法,例如新增、删除、修改数据的方法。

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);
}

行为说明
REQUIRED如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行
SUPPORTS如果有事务在运行,当前的方法就在这个事务内运行;如果当前没有事务,则以非事务的方式运行。
MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
REQUIRES_NEW当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起。
NEVER以非事务方式运行,如果当前存在事务,则抛出异常。
NESTED如果当前存在事务,则创建一个新事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED。

根据上面的描述,我们可以将行为分为三大类。

  • 不要事务:NEVER、NOT_SUPPORTED。
  • 如果有则用:SUPPORTS
  • 必须使用事务:REQUIRED、REQUIRES_NEW、NESTED、MANDATORY

隔离级别

事务有一个特性为隔离性,多事务操作之间不会产生影响。但如果不考虑隔离性,则会产生三个读问题:脏读、不可重复读、虚(幻)读。

  • 脏读:一个未提交事务读取到另一个未提交事务的数据

  • 不可重复读:一个未提交事务读取到另一个提交事务修改的数据

  • 虚(幻)读:一个未提交事务读取到另一提交事务添加的数据 那如何解决呢?可以通过设置事务隔离级别,解决读问题!Spring 中提供了以下隔离级别。

    级别说明
    DEFAULT使用所用的数据库的隔离级别
    READ_UNCOMMITTED(读未提交)可以读取到尚未提交的更改,可能导致脏读、幻读和不可重复读
    READ_COMMITTED(读已提交)Oracle 的默认级别,可以读取到已提交的更改的数据,防止脏读,可能出现幻读和不可重复读
    REPEATABLE_READ(可重复读)MySQL 的默认级别,同一条SQL多次执行,可以读取到已提交的新增的数据,防止脏读和不可重复读,可能出现幻读
    SERIALIZABLE可串行化,什么读问题都不会产生

加入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.6</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

xml方式

我们先来看看不使用事务会发生什么情况。创建名为 aopxml的包。

提供数据库脚本

CREATE TABLE `tx_test` (
  `id` int(11) NOT NULL,
  `name` varchar(64) DEFAULT NULL,
  `money` decimal(10,0) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `test`.`tx_test`(`id`, `name`, `money`) VALUES (1, '张三', 1000);
INSERT INTO `test`.`tx_test`(`id`, `name`, `money`) VALUES (2, '李四', 1000);

开发代码

新建 dao 包

在类中提供两个方法,一个张三增加金额,一个李四减金额。

@Repository
public class TXDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 给张三增加金额
     */
    public void add(){
        String sql = "update `tx_test`  set money = money + 100 where id = 1;";
        jdbcTemplate.update(sql);
    }

    /**
     * 给李四减金额
     */
    public void reduce(){
        String sql = "update `tx_test`  set money = money - 100 where id = 2;";
        jdbcTemplate.update(sql);
    }
}

新建 entity

public class TxTest {

    private Integer id;

    private String name;

    private BigDecimal money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getMoney() {
        return money;
    }

    public void setMoney(BigDecimal money) {
        this.money = money;
    }
}

新建 service 包

@Service
public class TXServiceImpl {

    @Autowired
    private TXDao tx;

    public void transfer(){
        tx.add();
        int i = 1/0;
        tx.reduce();
    }
}

项目结构如下:

测试

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("tx.xml");
    TXServiceImpl bean = context.getBean(TXServiceImpl.class);
    bean.transfer();
}

控制台出现异常

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at cn.cxyxj.txannon.service.TestServiceImpl.transfer(TestServiceImpl.java:20)
    at cn.cxyxj.txannon.AppMain.main(AppMain.java:20)

再来查看数据库数据,可以发现张三的金额增加了,但是李四的金额没有减。银行哭死!!! 所以我们需要引入 Spring 事务,解决上述出现的问题。

引入 tx 命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx" 
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

注意: 上面说过 Spring 提供的声明式事务管理是依赖于 Spring AOP 实现的,因此还需要添加 aop 命名空间配置。当然我还额外引入了 spring-context 命名空间。

配置事务管理器以及 JdbcTemplate

<!--引入 jdbc.properties 中的配置-->
    <context:property-placeholder location="classpath:jdbc.properties">
    </context:property-placeholder>

    <!--配置数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!--数据库连接地址-->
        <property name="url" value="${jdbc.url}"/>
        <!--数据库的用户名-->
        <property name="username" value="${jdbc.username}"/>
        <!--数据库的密码-->
        <property name="password" value="${jdbc.password}"/>
        <!--数据库驱动-->
        <property name="driverClassName" value="${jdbc.driver}"/>
    </bean>

    <!--定义 JdbcTemplate Bean-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--将数据源的 Bean 注入到 JdbcTemplate 中-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务管理器,以 JDBC 为例-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

配置的事务管理器实现为 DataSourceTransactionManager,是 JDBC 和 MBatis 的PlatformTransactionManager 接口实现。

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.50.172/tx_test?useSSL=false
jdbc.username=Yj19980402
jdbc.password=root

配置事务通知

配置事务通知,指定所需要使用的事务管理器以及指定事务作用的方法和该事务属性。

 <!--配置通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务参数-->
        <tx:attributes>
            <!--指定哪个方法上面添加事务-->
            <tx:method name="transfer*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="10"/>
            <!--可以配置多个方法 <tx:method name="account*"/>-->
        </tx:attributes>
    </tx:advice>

transaction-manage参数的默认值就是 transactionManager,如果事务管理器 id 与其一致,则可以不用指定。 <tx:method>元素包含多个属性参数,可以为某个或某些方法(name 属性指定的方法)定义事务属性,如下表所示:

事务属性说明
propagation指定事务的传播行为,默认为 REQUIRED
isolation指定事务的隔离级别,默认为所使用数据库的隔离级别
read-only指定是否为只读事务,默认为 false
timeout表示超时时间,单位为“秒”。事务在指定的超时时间后,自动回滚。避免事务长时间不提交导致数据库资源占用。默认为 -1,代表不超时
rollback-for指定出现哪些异常进行事务回滚
no-rollback-for指定出现哪些异常不进行事务回滚

配置切入点和切面

<aop:config>
    <!--配置切入点-->
    <aop:pointcut id="pt" expression="execution(*
com.cxyxj.aopxml.service.TXServiceImpl.*(..))"/>
    <!--配置切面-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>

如上写法就对 transfer 方法进行了事务管理。就不会出现小明减少余额,而小红没有增加余额的情况,发生了异常就进行回滚。

注解方式

使用注解方式就不会有上面如此琐碎的配置了。再重新创建名为 txannon包,将 xml 方式使用到的 entity、dao、service 相关代码 copy 过来。

开启事务

使用 EnableTransactionManagement注解开启事务。

@ComponentScan(basePackages = "com.cxyxj.txannon")
@EnableTransactionManagement //开启事务
public class AppMain {

}

相当于 tx:annotation-driven 标签。

创建配置类

@Configuration
@PropertySource("jdbc.properties")
public class TxConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Value("${jdbc.driver}")
    private String driverClassName;

    //创建数据库连接池
    @Bean
    public DriverManagerDataSource getDruidDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    //创建 JdbcTemplate 对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        //到 ioc 容器中根据类型找到 dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入 dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager
    getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new
                DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

}

可以不需要在配置切入点和切面了。

添加事务注解

在需要添加事务的方法上添加 @Transactional注解,表明该方法需要进行事务管理。

@Service
public class TXServiceImpl {

    @Autowired
    private TXDao tx;

    @Transactional
    public void transfer(){
        tx.add();
        int i = 1/0;
        tx.reduce();
    }
}

@Transactional这个注解可以添加到类上面,也可以添加方法上面。如果把这个注解添加到类上面,这个类里面所有的方法都添加事务,如果把这个注解添加方法上面,则是为这个方法添加事务。

@Transactional

Transactional 这个注解里面可以配置很多事务相关参数。

public @interface Transactional {

    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

事务属性说明
value指定不同的事务管理器。
transactionManager跟 value 一致。
propagation指定事务的传播行为,默认为 REQUIRED
isolation指定事务的隔离级别,默认为所使用数据库的隔离级别
read-only指定是否为只读事务,默认为 false
timeout表示超时时间,单位为“秒”。事务在指定的超时时间后,自动回滚。避免事务长时间不提交导致数据库资源占用。默认为 -1,代表不超时
rollbackFor指定出现哪些异常进行事务回滚
rollbackForClassName指定异常类名称,进行事务回滚
noRollbackFor指定出现哪些异常不进行事务回滚
noRollbackForClassName指定出现哪些异常类名称不进行事务回滚

基本用法会了,现在就来看看事务的传播行为,这是Spring事务中难以理解的一块,因为它的场景很多。

事务传播行为详解

REQUIRED

如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行

  • 如果 transfer 方法没有事务,则 reduce 方法会创建一个事务。
  • 两个方法的事务的传播行为都为 Propagation.REQUIRED。所以 transfer方法会先开启一个事务,而 reduce会加入到 transfer方法的事务中,这两个方法用的是同一个事务,所以不论是在哪个方法中抛出异常,所有操作都会回滚。

REQUIRES_NEW

当前方法必须启动新事务,并在它自己的事务内运行。如果有事务正在运行,应该将它挂起。

reduce 方法行为修改为 Propagation.REQUIRES_NEW。transfer 方法创建新事务,然后调用 reduce 方法,reduce 方法会将 transfer 方法的事务挂起,并创建属于 reduce 方法的事务。所以在该例子中会创建两个事务。由于有两个事务,那事务的回滚就出现了几种情况。

  • 场景一

 transfer 方法进行的操作会回滚,reduce 方法的操作不会回滚。
  • 场景二

 两个方法的操作都会回滚。这是由于 reduce 方法的异常会向 transfer 方法传递。
  • 场景三

transfer 方法进行的操作不会回滚,reduce 方法的操作会回滚。

  • 如果 transfer 方法没有事务,则 reduce 方法会创建一个事务。
  • 如果 transfer 方法有事务,则 reduce 方法会将 transfer 方法的事务挂起,并创建属于 reduce 方法的事务。如果此时 transfer 方法发生了异常,则 transfer 方法操作会回滚,但不会导致 reduce 方法回滚。如果 reduce 方法发生了异常,则 reduce 方法操作会回滚,如果 transfer 方法没有捕获 reduce 方法的异常,那 transfer 方法也会回滚。

NESTED

如果当前存在事务(主事务),则创建一个新事务作为当前事务的嵌套事务(子事务)来运行;如果当前没有事务,则该取值等价于 REQUIRED。

  • 如果 transfer 方法没有事务,则 reduce 方法会创建一个事务。
  • 如果 transfer 方法有事务,则 reduce 方法会创建一个新事务,作为 transfer 方法事务的嵌套事务来运行。那会有什么场景呢?
  • 场景一

transfer 方法发生异常并回滚,会导致 reduce 方法 同时回滚。

  • 场景二

transfer 方法进行的操作不会回滚,reduce 方法的操作会回滚。注意:transfer 方法需要进行 catch,不然 transfer 方法也会回滚。

主事务方法异常回滚时,会同时回滚子事务。而子事务可以单独异常回滚,可以不影响主事务和其他子事务(前提是需要处理掉子事务的异常)

MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

由于 transfer 方法没有事务,在启动时就会抛出异常,如下:

No existing transaction found for transaction marked with propagation 'mandatory'

SUPPORTS

如果有事务在运行,当前的方法就在这个事务内运行;如果当前没有事务,则以非事务的方式运行。

由于 transfer 方法没有事务,所以 reduce 方法也不会创建事务,发生了异常也不会进行回滚。

NOT_SUPPORTED

以非事务方式运行,如果当前存在事务,则把当前事务挂起。

transfer 方法有事务,但 reduce 方法传播行为是 NOT_SUPPORTED,所以会将 transfer 方法事务挂起,reduce 方法以非事务的方式运行。

所以图片例子会出现 transfer 方法进行的操作会回滚,reduce 方法的操作不会回滚。

NEVER

以非事务方式运行,如果当前存在事务,则抛出异常。

由于 transfer 方法有事务,在启动时就会抛出异常,如下:

Existing transaction found for transaction marked with propagation 'never'

回滚规则

上面一直在说遇到异常就回滚,那是遇到所有异常都会回滚吗?不是的,默认情况下,Spring 事务只有遇到 RuntimeException 以及 Error 时才会回滚,在遇到检查型异常时是不会回滚的,比如 IOException、TimeoutException。

那如果想在发生检查型异常时也进行回滚呢,可以使用 rollbackFor 属性进行如下配置:

那同理,如果遇到某个异常,不想进行回滚,使用 noRollbackFor 属性配置如下:

Spring 本地事务 Spring 本地事务、Spring 事务的传播行为、本地事务失效分析 阅读详情

相关推荐

Spring事务的实现方式

spring事务

Mr.xing的博客 1405

Spring 事务之@Transactional

学习Spring事务之@Transactional万字源码分析,从事务创建-->myabtis连接获取-->事务结束 比较完整的流程分析

weixin_68366781的博客 2446

Spring 事务 - 基本使用、传播机制、失效场景、嵌套事务多回滚解决

带你一次学懂:编程式事务、声明式事务、事务的传播机制!

CYK_byte的博客 5335

Spring挖掘: (事务篇)

Spring的事务为我们提供了非常便携的事务功能,只需要你添加@Transactional注解即可. 但需要注意的是,Spring的事务是需要数据库本身具有事务功能的支撑,才能完成事务的功能,也就是他是基于数据库的事务功能进行的.没有数据库事务的支持,Spring是无法提供事务功能的.我们就从这个注解入手,来看看Spring是怎么完成事务的功能的设计的.

m0_65013257的博客 1174

Spring事务操作示例(四种方式)

Spring事务操作示例(四种方式),包含完整代码和数据库文件(基于MySQL,在项目sql文件夹中),可运行,学习Spring事务详见博客:http://blog.youkuaiyun.com/daijin888888/article/details/51822257

钙钛矿太阳能电池稳定性测试:ISOS协议详解与标准化实践指南

在光伏器件研发中,稳定性测试是评估材料与器件长期可靠性的核心环节,其关键在于实现测试条件的标准化与数据的可比性。通过控制变量和模拟真实环境应力,标准化测试协议能够系统揭示材料衰减机理与器件失效模式,为技术迭代提供可靠依据。在钙钛矿太阳能电池领域,ISOS协议作为国际公认的测试框架,通过分级体系(如ISOS-L系列光照测试与ISOS-D系列暗态测试)和关键参数定义(如光源光谱匹配、MPP跟踪、结温控制),解决了不同实验室数据无法横向对比的痛点。该协议不仅规范了测试流程,还通过硬件系统搭建、自动化数据采集与归一

weixin_33600816的博客 319

【Spring】Spring事务和事务传播机制

Spring事务和事务传播机制

2403_87459748的博客 2828

Unity FPS射击控制系统:数据驱动的武器行为解耦方案

在Unity第一人称射击(FPS)开发中,射击系统常因输入、状态、行为耦合导致连发异常、换弹冲突与网络同步失准。其本质是缺乏对‘武器行为’(如后坐力曲线、弹药逻辑)与‘射击控制’(如输入合法性校验、冷却调度)的正交分离。本方案基于ScriptableObject实现WeaponData数据资产化,配合ShootingController状态机服务,达成输入解耦、运行时参数可调、协程受控、网络预测友好等工程目标。适用于中大型FPS项目架构升级与数据驱动转型,尤其解决Unity InputSystem与物理反馈

weixin_30536861的博客 609

Spring事务

一、什么是事务 1.1 事务的概念 事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元); 通俗的讲,就是为了达到某个目的,而做的一系列的操作,要么一起成功(事务提交),要么一起失败(事务回滚)。 最常见的例子就是转账: 小明给如花转账: 开启事务------- 从小明的账户扣除1000块 给如花的账户增加1000块 事务提交------- 上面例子的任何步骤

Jak的博客 7619

Astah 9.0升级步骤(Widnows/Mac OS)

Astah Professional是一款全新的轻量级UML建模工具。软件集思维导图和UML建模于一体,采用100%纯JAVA构建, 兼容性强,不仅能够实现分布式建模、项目合并,还能够实现和其它建模工具的交互。

tequiero777的博客 2824

TPM 2.0实例探索2 —— LUKS磁盘加密(1)

本文对LUKS行了系统介绍

phmatthaus的专栏 1699

Spring 事务详解

上一章我们介绍了Spring AOP,今天来讲解Spring事务什么是事务?为什么需要事务?Spring 中事务的实现1.编程式事务2.声明式事务@Transactional详解1. REQUIRED(默认值)3.NEVER4.Nested如果各位对文章的内容感兴趣的话,请点点小赞,关注一手不迷路,讲解的内容我会搭配我的理解用我自己的话去解释如果有什么问题的话,欢迎各位评论纠正 🤞🤞🤞N_0050-优快云博客java SE_N_0050的博客-优快云博客。

N_0050的博客 2824

ncode控件使用总结

数据格式txt转s3t #HEADER #TITLES Absolute_Time,COG_Ax,COG_Ay,COG_Az,LF_SPINDLE_AZ #UNITS ,g,g,g,g, #DATATYPES Double,Float,Float,Float,Float #DATA 2. 导入s3t数据 3. 输入数据控件 直接拖入数据源或者添加控件数据 4.

技术联盟 2006

Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现

点击蓝色“程序员DMZ ”关注我哟 好看记得加个“星标”哈! 前言 本专题大纲如下: 事务专题大纲 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用中的问题,这两天想了想还是单独写一篇并作为事务专题的收尾篇,也是我Spring源码专题的收尾篇。 本文大纲如下: Spring事务应用大纲 在看这篇文章,以及下篇源码分析的文章我希望你对Spring AOP以及有充分的了解,不然一些细节问题你可能看不明白,关于Spring AOP如果

做一个认真的程序员 3069

基于卷积神经网络的球罐结构损伤识别

球罐结构损伤识别方法研究 摘要:本研究针对球形储罐(球罐)结构健康监测需求,提出基于卷积神经网络(CNN)的损伤识别方法。通过ANSYS有限元仿真构建包含5种裂纹深度的500组振动信号数据集,设计包含卷积层、池化层和全连接层的CNN网络架构。实验结果表明,该方法在训练集和测试集上分别达到98.4%和96.0%的准确率,验证了其在球罐焊缝损伤识别中的有效性。研究提供了完整的MATLAB实现代码,为球罐结构智能化监测提供了技术参考。 关键词:球罐结构;卷积神经网络;损伤识别;振动信号;深度学习

AI的内部世界的博客 120

SpringCloud详细教程(下)

SpringCloud详细教程,springcloud完全教程。Springcloud微服务教程(上)的姊妹篇:介绍了其它SpringCloud组件。

鸟哥哥的专栏 4万+

【Spring框架】Spring事务的原理

本文将详细讲解Spring事务管理地底层原理,让你彻底搞懂Spring事务的运行机制。

小七的博客 3294

SSH 远程登录报错:kex_exchange_identification: Connection closed.....

不知道为啥会出现这样的情形,最近这段时间登录都是正常的,不知道哪里抽风了,就提示这个。错误信息 kex_exchange_identification: Connection closed by remote host 出现在尝试使用 SSH 连接到远程服务器过程中,表明在密钥交换阶段的一开始,远程主机就关闭了连接。服务器的sshd_config文件中配置错误,可能是不允许的加密算法、修订文件中的语法错误,或者未正确指定用于SSH的HostKey。如果当前已有太多活动连接,新的连接尝试可能会被拒绝。

飞越蓝天的专栏 8297

Spring事务和事务传播机制

事务是⼀组操作的集合,是一个不可分割的操作。事务会把所有的操作作为⼀个整体,一起向数据库提交或者是撤销操作请求。所以这组操作要么同时成功,要么同时失败。事务传播机制就是: 多个事务方法存在调用关系时,事务是如何在这些方法间进行传播的。比有两个方法A,B都被 @Transactional 修饰,A方法调用B方法A方法运行时,会开启⼀个事务。当A调用B时,B方法本身也有事务,此时B方法运行时,是加入A的事务,还是创建⼀个新的事务呢?取决于事物的传播机制如何设置。这个就涉及到了事务的传播机制。

MissedID_的博客 1601

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

【Spring】事务总结

事务是逻辑上的一组操作,要么都执行,要么都不执行。Guide 哥:大家应该都能背上面这句话了,下面我结合我们日常的真实开发来谈一谈。我们系统的每个业务方法可能包括了多个原子性的数据库操作,比如下面的方法中就有两个原子性的数据库操作。这些原子性的数据库操作是有依赖的,它们要么都执行,要不就都不执行。事务能否生效数据库引擎是否支持事务是关键。比如常用的 MySQL 数据库默认使用支持事务的innodb引擎。但是,如果把数据库引擎变为myisam,那么程序也就不再支持事务了!

2301_79456272的博客 1265

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Spring事务详解

我们的系统中,每个业务方法都可能包括了多个原子性的数据库操作,就比如下面的addUser()方法中,就有两个原子性的数据库操作。这些原子性的数据库操作是有依赖的,要么都执行,要么都不执行。玩意这两个操作之间突然出现错误,比如银行系统崩溃或者网络故障,导致小明的余额减少,但是小红的余额没有增加,这样就不对了。事务就是要保证这两个关键操作要么都成功,要么都失败。另外,数据库事务的ACID四大特性是事务的基础。

xg_960的博客 1334

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Spring 事务

spring 事务类型、事务模型(隔离级别)、常见数据库并发问题和解决、事务编程以及事务失效场景

code_nn的博客 1319
上一篇: HotSpot JVM 类加载、链接和初始化、Java Object Layout
下一篇: 数据结构之数组详解
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值