Spring事务实践

事务四大特性

1. 原子性(Atomicity):事务不可再分,要么都执行,要么都不执行。

2. 一致性(Consistency):事务执行前后,数据的完整性保持一致,即修改前后数据总量是一样的大概。

3. 隔离性(Isolation):一个事务执行过程中,不会受到其他事务干扰。

4. 持久性(Durability):事务一旦结束,对数据库的影响是永久的。数据持久化到数据库中。

SQL标准定义的四种隔离级别

1. READ UNCOMMITTED(未提交读):

1. 在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为**脏读(Dirty Read)**。

2. 性能上并不占优势

2. READ COMMITTED(提交读):

1. 大多数数据库系统的默认隔离级别都是READ COMMITTED **(但MySQL不是)**。READ COMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。

2. 提交读有时也叫做**不可重复读(nonrepeatable read)**。

3. REPEATABLE READ(可重复读):

1. REPEATABLE READ解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。 但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。

2. 幻读:当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前事务再次读取该范围的记录时,会产生幻行(Phantom Row)。

3. InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC)解决幻读问题。

4. MySQL的默认事务隔离级别。

4. SERIALIZABLE(可串行化):

SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。

5. ANSI SQL隔离级别

隔离级别

脏读可能性

不可重复读可能性

幻读可能性

加锁读

READ UNCOMMITTED

Yes

Yes

Yes

No

READ COMMITTED

No

Yes

Yes

No

REPEATABLE READ

No

No

Yes

No

SERIALIZABLE

No

No

No

Yes

Spring事务的五个隔离级别

1. ISOLATION_DEFAULT(默认):使用数据库默认的事务隔离级别。

2. ISOLATION_READ_UNCOMMITTED:事务最低的隔离级别,允许一个事务可以读取另一个事务未提交的数据。 会产生脏读、不可重复读和幻读。

3. ISOLATION_READ_COMMITTED: 保证一个事务只能读取另一个事务修改并提交后的数据,不能读取未提交的数据。防止脏读。

4. ISOLATION_REPEATABLE_READ:保证一个事务不能更新另一个事务修改但尚未提交的数据。可以避免脏读和不可重复读。

5. ISOLATION_SERIALIZABLE:序列化执行所有事务。都避免了,但是效率极低。

Spring事务7个传播机制

1. 在同一个事务中:

1. PROPAGION_REQUIRED(默认):支持当前事务,不存在则创建一个新的事务。

2. PROPAGION_SUPPORTS:支持当前事务,不存在就以非事务方式运行。

3. PROPAGION_MANDATORY:支持当前事务,如果不存在,抛出异常。

2. 不同事务中:

1. PROPAGIN_REQUIRES_NEW:当前存在事务则挂起,创建一个新事务。

2. PROPAGION_SUPPORTS:当前存在事务则挂起,以非事务方式运行。

3. PROPAGION_NEVER:当前存在事务抛出异常,非事务方式运行。

4. PROPAGION_NESTED:当前存在事务则嵌套事务执行。

Spring事务传播机制demo

传播机制规律

1. 事务失效原因:

1. 对象没有被spring管理

2. 事务方法不是public修饰符修饰

3. 异常被捕获,举例: com.example.transaction.User2ServiceImpl.addRequiredAndException方法;

但是需要注意的是当外围方法跟调用方法不在同一类时,外围方法开启事务,内部方法加入外围方法事务,内部方法抛出异常回滚,即使方法被catch不被外围方法感知,整个事务依然回滚。举例:com.example.transaction.TransactionApplicationTests.transaction_required_required_exception_try

4. 发生自身调用,类中的一个没有启用事务的方法A() 调用了本类中的另一个启用事务的方法B();解决方案:获取当前类的代理对象,然后在A()中用代理对象调用B()。从Bean容器中获取当前实例,本质上还是Spring动态代理对象 UserServiceImpl self = applicationContext.getBean(UserServiceImpl.class);

2. 事务传播的规律

1. 外围方法未开启事务的情况下,

1. Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

2. Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

3. Propagation.NESTED和Propagation.REQUIRE作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。

4. MANDATORY修饰的内部方法会抛出异常

2. 外围方法开启事务的情况下,

1. Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。

2. Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。

3. Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务。

4. NEVER修饰的内部方法会抛出异常

创建表结构

CREATE TABLE `user1` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL DEFAULT '',
PRIMARY KEY(`id`)
)
ENGINE = InnoDB;``

``CREATE TABLE `user2` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL DEFAULT '',
PRIMARY KEY(`id`)
)
ENGINE = InnoDB;

用例代码

@Service
public class User1ServiceImpl {

   @Autowired
   private User1Mapper user1Mapper;

    /**
     *
     * @param user user
     */
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void addRequired(User1 user){
        user1Mapper.insert(user);
    }

    /**
     *
     * @param user
     */
    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(User1 user){
        user1Mapper.insert(user);
    }

    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)
    public void addNested(User1 user){
        user1Mapper.insert(user);
    }

@Service
public class User2ServiceImpl {

    @Autowired
    private User2Mapper user2Mapper;

    /**
     *
     * @param user user
     */
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void addRequired(User2 user){
        user2Mapper.insert(user);
    }

    /**
     *
     * @param user user
     */
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void addRequiredException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }

    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void addRequiredAndException(){
        User2 user2=new User2();
        user2.setName("李四1233qq");
        addRequired(user2);

        User2 user2_1=new User2();
        user2_1.setName("李四45644qq");
        try{
            addRequiredException(user2_1);
        }catch (Exception e){
            System.out.println("同一类内事务异常被捕获,导致不会滚");
        }
    }

    /**
     *
     * @param user
     */
    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNew(User2 user){
        user2Mapper.insert(user);
    }

    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRES_NEW)
    public void addRequiresNewException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }

    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)
    public void addNested(User2 user){
        user2Mapper.insert(user);
    }

    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)
    public void addNestedException(User2 user){
        user2Mapper.insert(user);
        throw new RuntimeException();
    }

    public void interiorQuote(){
        User2 user2=new User2();
        user2.setName("李四qqqq");
        addRequired(user2);

        User2 user2_1=new User2();
        user2_1.setName("李四aaaaaa");
        addRequiredException(user2_1);
    }

    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void interiorQuote2(){
        User2 user2=new User2();
        user2.setName("李四cccccc");
        addRequired(user2);

        User2 user2_1=new User2();
        user2_1.setName("李四dddddd");
        addRequiredException(user2_1);
    }


}
    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NEVER)
    public void addSupports(){

        User1 user1=new User1();
        user1.setName("李四1233qqaaa");
        user1Mapper.insert(user1);
    }
}

@SpringBootTest
class TransactionApplicationTests {

    @Autowired
    private User1ServiceImpl user1Service;

    @Autowired
    private User2ServiceImpl user2Service;


    @Test
    void contextLoads() {
    }


    /*-------------通过以下两个方法证明在外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。-----*/

    /**
     * 外围方法无事务,调用方法事务传播机制REQUIRED,外围方法异常;不影响调用方法事务提交
     */
    @Test
    public void notransaction_exception_required_required(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addRequired(user2);

        throw new RuntimeException();
    }

    /**
     * 外围方法无事务,调用方法事务传播机制REQUIRED,调用方法user2Service.addRequiredException(),发生异常,user1Service.addRequired正常提交, user2Service.addRequiredExceptio回滚
     */
    @Test
    public void notransaction_required_required_exception(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addRequiredException(user2);
    }




    /*-------------通过以下外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。-----*/

    /**
     * 外围方法启用事务指定传播机制REQUIRED,调用方法事务传播机制REQUIRED,外围方法异常,调用方法也回滚
     */
    @Test
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void transaction_exception_required_required(){
        User1 user1=new User1();
        user1.setName("张三3");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四3");
        user2Service.addRequired(user2);

        throw new RuntimeException();
    }

    /**
     * 外围方法启用事务指定传播机制REQUIRED,调用方法事务传播机制REQUIRED,,调用方法user2Service.addRequiredException(),发生异常,user1Service.addRequired、 user2Service.addRequiredExceptio回滚
     */
    @Test
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void transaction_required_required_exception(){
        User1 user1=new User1();
        user1.setName("张三3");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四3");
        user2Service.addRequiredException(user2);
    }

    /**
     * 外围方法开启事务,内部方法加入外围方法事务,内部方法抛出异常回滚,即使方法被catch不被外围方法感知,整个事务依然回滚。
     */
    @Test
    @Transactional
    public void transaction_required_required_exception_try(){
        User1 user1=new User1();
        user1.setName("张三3");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四3");
        try {
            user2Service.addRequiredException(user2);
        } catch (Exception e) {
            System.out.println("方法回滚");
        }
    }

    /**
     * 如果在同一个类中,catch不回滚;spring声明式事务 同一类内方法调用事务失效
     */
    @Test
    public void addRequiredAndException(){
        user2Service.addRequiredAndException();
    }

    @Test
    public void interiorQuote(){
        user2Service.interiorQuote();
    }

    @Test
    public void interiorQuote2(){
        user2Service.interiorQuote2();
    }

    /*--------通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。-----*/

    /**
     * 外围方法没有事务,调用方法事务传播机制REQUIRES_NEW,两个事务独立运行,外围发生异常不会回滚
     */
    @Test
    public void notransaction_exception_requiresNew_requiresNew(){
        User1 user1=new User1();
        user1.setName("张三111");
        user1Service.addRequiresNew(user1);

        User2 user2=new User2();
        user2.setName("李四111");
        user2Service.addRequiresNew(user2);
        throw new RuntimeException();

    }

    /**
     * 外围方法没有事务,,调用方法事务传播机制REQUIRES_NEW,两个事务独立运行,其中一个方法异常回滚,其他事务不受影响
     */
    @Test
    public void notransaction_requiresNew_requiresNew_exception(){
        User1 user1=new User1();
        user1.setName("张三");
        user1Service.addRequiresNew(user1);

        User2 user2=new User2();
        user2.setName("李四");
        user2Service.addRequiresNewException(user2);
    }

    /*--在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰.---*/

    /**
     * 外围方法事务机制REQUIRED,调用方法事务机制为REQUIRED和REQUIRES_NEW,外围方法异常,同为REQUIRED的事务会归位同一事务,一起回滚;REQUIRES_NEW为独立事务不受影响
     */
    @Test
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void transaction_exception_required_requiresNew_requiresNew(){
        User1 user1=new User1();
        user1.setName("张三aaa");
        user1Service.addRequired(user1);

        User1 user2=new User1();
        user2.setName("李四aaa");
        user1Service.addRequiresNew(user2);

        User2 user3=new User2();
        user3.setName("王五aaa");
        user2Service.addRequiresNew(user3);
        throw new RuntimeException();
    }

    /**
     * 外围方法事务机制REQUIRED,调用方法事务机制为REQUIRED和REQUIRES_NEW,调用方法发生异常回滚,REQUIRED发生回滚,REQUIRES_NEW不受影响
     */
    @Test
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception(){
        User1 user1=new User1();
        user1.setName("张三bbb");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四bbb");
        user2Service.addRequiresNew(user2);

        User2 user3=new User2();
        user3.setName("王五bbb");
        user2Service.addRequiresNewException(user3);
    }

    /**
     * 外围方法事务机制REQUIRED,调用方法事务机制为REQUIRED和REQUIRES_NEW,调用方法发生即使被捕获,异常方法和REQUIRED发生回滚,REQUIRES_NEW不受影响
     */
    @Test
    @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception_try(){
        User1 user1=new User1();
        user1.setName("张三ccc");
        user1Service.addRequired(user1);

        User2 user2=new User2();
        user2.setName("李四ccc");
        user2Service.addRequiresNew(user2);
        User2 user3=new User2();
        user3.setName("王五ccc");
        try {
            user2Service.addRequiresNewException(user3);
        } catch (Exception e) {
            System.out.println("回滚");
        }
    }

    /*-- 通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.NESTED和Propagation.REQUIRE作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。 --*/
    @Test
    public void notransaction_exception_nested_nested(){
        User1 user1=new User1();
        user1.setName("张三ddd");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四ddd");
        user2Service.addNested(user2);
        throw new RuntimeException();
    }

    @Test
    public void notransaction_nested_nested_exception(){
        User1 user1=new User1();
        user1.setName("张三eee");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四eee");
        user2Service.addNestedException(user2);
    }

    @Test
    @Transactional
    public void addSupports(){
        user1Service.addSupports();
    }


    /*-- 外围方法开启事务的情况下Propagation.NESTED修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务--*/
    @Test
    @Transactional
    public void transaction_exception_nested_nested(){
        User1 user1=new User1();
        user1.setName("张三fff");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四fff");
        user2Service.addNested(user2);
        throw new RuntimeException();
    }

    @Test
    @Transactional
    public void transaction_nested_nested_exception(){
        User1 user1=new User1();
        user1.setName("张三ggg");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四ggg");
        user2Service.addNestedException(user2);
    }

    @Test
    @Transactional
    public void transaction_nested_nested_exception_try(){
        User1 user1=new User1();
        user1.setName("张三hhh");
        user1Service.addNested(user1);

        User2 user2=new User2();
        user2.setName("李四hhh");
        try {
            user2Service.addNestedException(user2);
        } catch (Exception e) {
            System.out.println("方法回滚");
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值