乐趣区

关于spring:Spring事务

一、什么是事务的流传?

简略的了解就是多个事务办法互相调用时, 事务如何在这些办法间流传。

二、Spring 事务流传类型 Propagation 介绍

在 Spring 中对于事务的流传行为定义了七种类型别离是:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED
现有两个办法 A 和 B,办法 A 执行会在数据库 ATable 插入一条数据,办法 B 执行会在数据库 BTable 插入一条数据,伪代码如下:

// 将传入参数 a 存入 ATable pubilc void A(a){insertIntoATable(a);    
}
// 将传入参数 b 存入 BTable public void B(b){insertIntoBTable(b);
}

无事务

public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB}

public void testB(){B(b1);  // 调用 B 入参 b1
    throw Exception;     // 产生异样抛出
    B(b2);  // 调用 B 入参 b2
}

a1 数据胜利存入 ATable 表,b1 数据胜利存入 BTable 表,而在抛出异样后 b2 数据存储就不会执行,也就是 b2 数据不会存入数据库,这就是没有事务的场景。

REQUIRED(Spring 默认的事务流传类型)

如果以后没有事务,则本人新建一个事务,如果以后存在事务,则退出这个事务

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB}
@Transactional(propagation = Propagation.REQUIRED)
public void testB(){B(b1);  // 调用 B 入参 b1
    throw Exception;     // 产生异样抛出
    B(b2);  // 调用 B 入参 b2
}

后果:
数据库没有插入新的数据,数据库还是放弃着执行 testMain 办法之前的状态,没有产生扭转。testMain 上申明了事务,在执行 testB 办法时就退出了 testMain 的事务(以后存在事务,则退出这个事务),在执行 testB 办法抛出异样后事务会产生回滚,又 testMain 和 testB 应用的同一个事务,所以事务回滚后 testMain 和 testB 中的操作都会回滚,也就使得数据库依然放弃初始状态

public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB}
@Transactional(propagation = Propagation.REQUIRED)
public void testB(){B(b1);  // 调用 B 入参 b1
    throw Exception;     // 产生异样抛出
    B(b2);  // 调用 B 入参 b2
}

后果:
数据 a1 存储胜利,数据 b1 和 b2 没有存储。因为 testMain 没有申明事务,testB 有申明事务且流传行为是 REQUIRED,所以在执行 testB 时会本人新建一个事务(如果以后没有事务,则本人新建一个事务),testB 抛出异样则只有 testB 中的操作产生了回滚,也就是 b1 的存储会产生回滚,但 a1 数据不会回滚,所以最终 a1 数据存储胜利,b1 和 b2 数据没有存储

SUPPORTS

以后存在事务,则退出以后事务,如果以后没有事务,就以非事务办法执行

public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB}
@Transactional(propagation = Propagation.SUPPORTS)
public void testB(){B(b1);  // 调用 B 入参 b1
    throw Exception;     // 产生异样抛出
    B(b2);  // 调用 B 入参 b2
}

后果:
a1,b1 存入数据库,b2 没有存入数据库。因为 testMain 没有申明事务,且 testB 的事务流传行为是 SUPPORTS,所以执行 testB 时就是没有事务的(如果以后没有事务,就以非事务办法执行),则在 testB 抛出异样时也不会产生回滚,所以最终后果就是 a1 和 b1 存储胜利,b2 没有存储。

MANDATORY

以后存在事务,则退出以后事务,如果以后事务不存在,则抛出异样。

public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB}
@Transactional(propagation = Propagation.MANDATORY)
public void testB(){B(b1);  // 调用 B 入参 b1
    throw Exception;     // 产生异样抛出
    B(b2);  // 调用 B 入参 b2
}

后果:
a1 存储胜利,而 b1 和 b2 没有存储。b1 和 b2 没有存储,并不是事务回滚的起因,而是因为 testMain 办法没有申明事务,在去执行 testB 办法时就间接抛出事务要求的异样(如果以后事务不存在,则抛出异样),所以 testB 办法里的内容就没有执行。

REQUIRES_NEW

创立一个新事务,如果存在以后事务,则挂起该事务,即在执行时,不管以后是否存在事务,总是会新建一个事务,所谓挂起就是以后事务要等到新事务执行完能力继续执行,两个事务是独立的。

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB
    throw Exception;     // 产生异样抛出
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testB(){B(b1);  // 调用 B 入参 b1
    B(b2);  // 调用 B 入参 b2
}

后果:
这种情景的执行后果就是 a1 没有存储,而 b1 和 b2 存储胜利,因为 testB 的事务流传设置为 REQUIRES_NEW, 所以在执行 testB 时会开启一个新的事务,testMain 中产生的异样时在 testMain 所开启的事务中,所以这个异样不会影响 testB 的事务提交,testMain 中的事务会产生回滚,所以最终 a1 就没有存储,而 b1 和 b2 就存储胜利了。

NOT_SUPPORTED

始终以非事务形式执行, 如果以后存在事务,则挂起以后事务

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB}
@Transactional(propagation =Propagation.NOT_SUPPORTED)
public void testB(){B(b1);  // 调用 B 入参 b1
    throw Exception;     // 产生异样抛出
    B(b2);  // 调用 B 入参 b2
}

后果:
a1 和 b2 没有存储,而 b1 存储胜利。testMain 有事务,而 testB 不应用事务,所以执行中 testB 的存储 b1 胜利,而后抛出异样,此时 testMain 检测到异样事务产生回滚,然而因为 testB 不在事务中,所以只有 testMain 的存储 a1 产生了回滚,最终只有 b1 存储胜利,而 a1 和 b1 都没有存储

NEVER

不应用事务,如果以后事务存在,则抛出异样

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB}
@Transactional(propagation = Propagation.NEVER)
public void testB(){B(b1);  // 调用 B 入参 b1
    B(b2);  // 调用 B 入参 b2
}

后果:
该场景执行,间接抛出事务异样,且不会有数据存储到数据库。因为 testMain 事务流传类型为 REQUIRED,所以 testMain 是运行在事务中,而 testB 事务流传类型为 NEVER,所以 testB 不会执行而是间接抛出事务异样,此时 testMain 检测到异样就产生了回滚,所以最终数据库不会有数据存入。

NESTED

如果以后事务存在,则在嵌套事务中执行,NESTED 是以后存在事务时(咱们把以后事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。
在 NESTED 状况下父事务回滚时,子事务也会回滚
子事务回滚,父事务不受影响

@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){A(a1);  // 调用 A 入参 a1
    testB();    // 调用 testB
    throw Exception;     // 产生异样抛出
}
@Transactional(propagation = Propagation.NESTED)
public void testB(){B(b1);  // 调用 B 入参 b1
    B(b2);  // 调用 B 入参 b2
}

后果:
所有数据都不会存入数据库,因为在 testMain 产生异样时,父事务回滚则子事务也跟着回滚了

面试题

spring 事务的原理?
spring 事务的实质其实就是数据库对事务的反对,没有数据库的事务反对,spring 是无奈提供事务性能的。
那么,咱们个别应用 JDBC 操作事务的时候,代码如下
(1) 获取连贯 Connection con = DriverManager.getConnection()
(2)开启事务 con.setAutoCommit(true/false);
(3)执行 CRUD
(4)提交事务 / 回滚事务 con.commit() / con.rollback();
(5)敞开连贯 conn.close();
应用 spring 事务管理后,咱们能够省略步骤 (2) 和步骤(4),就是让 AOP 帮你去做这些工作。

spring 什么状况下进行事务回滚?
1. 事务只会对 Error 与 RuntimeException 及其子类这些异样,做出回滚。个别的 Exception 这些 Checked 异样不会产生回滚
2. 当所拦挡的办法有指定异样抛出,事务才会主动进行回滚,异样被捕捉则不会回滚

spring 事务什么时候生效?
1. 数据库引擎不反对事务
2. 没有被 Spring 治理
3. 办法不是 public 的
4. 本身调用问题
5. 异样被捕捉
6. 异样类型谬误

spring 的事务隔离和数据库的事务隔离是一个概念么?
是的,如果 spring 定义的隔离级别和数据库的不一样,则以 spring 定义的为准

spring 事务管制放在 service 层,在 service 办法中一个办法调用 service 中的另一个办法,默认开启几个事务?
考验 spring 的事务 PROPAGATION_REQUIRED 级别的流传行为,如果外层有事务,则以后事务退出到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行。

参考:
带你读懂 Spring 事务——事务的流传机制
面试官:来,讲讲 spring 事务有哪些坑?

退出移动版