springboot中数据库事务

数据库事务具有以下4 个基本特征, 也就是著名的ACID 。

Atomic (原子性): 事务中包含的操作被看作一个整体的业务单元, 这个业务单元中的操作

要么全部成功,要么全部失败,不会出现部分失败、部分成功的场景。

Consistency (一致性):事务在完成时,必须使所有的数据都保持一致状态,在数据库中所

有的修改都基于事务,保证了数据的完整性。

Isolation (隔离性): 这是我们讨论的核心内容,正如上述,可能多个应用程序线程同时访问

同一数据,这样数据库同样的数据就会在各个不同的事务中被访问,这样会产生丢失更新。

为了压制丢失更新的产生,数据库定义了隔离级别的概念,通过它的选择,可以在不同程度

上压制丢失更新的发生。因为互联网的应用常常面对高并发的场景,所以隔离性是需要掌握

的重点内容。

Durability (持久性):事务结束后,所有的数据会固化到一个地方,如保存到磁盘当中,即

使断电重启后也可以提供给应用程序访问。

隔离级别

未提交读( read uncommitted )是最低的隔离级别,其含义是允许一个事务读取另外一个事务没

有提交的数据。未提交读是一种危险的隔离级别,所以一般在我们实际的开发中应用不广, 但是它

的优点在于并发能力高,适合那些对数据一致性没有要求而追求高并发的场景,它的最大坏处是出

现脏读。

读写提交( read committed )隔离级别, 是指一个事务只能读取另外一个事务已经提交的数据,

不能读取未提交的数据。

可重复读的目标是克服读写提交中出现的不可重复读的现象,因为在读写提交的时候,可能出

现一些值的变化, 影响当前事务的执行,如上述的库存是个变化的值,这个时候数据库提出了可重

复读的隔离级别。

串行化( Serializable)是数据库最高的隔离级别,它会要求所有的SQL 都会按照顺序执行,这

样就可以克服上述隔离级别出现的各种问题,所以它能够完全保证数据的一致性。

传播行为

在Spring 事务机制中对数据库存在7 种传播行为,它是通过枚举类Propagation 定义的

package org.springframework.transaction.annotation;

public enum Propagation {
/**
*需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务,
*否则新建一个事务运行子方法
/
    REQUIRED(0),
/**
*支持事务,如果当前存在事务,就沿用当前事务,
*如果不存在,则继续采用无事务的方式运行子方法
/
    SUPPORTS(1),
/**
*必须使用事务,如果当前没有事务,则会抛出异常,
*如果存在当前事务, 就沿用当前事务
/
    MANDATORY(2),
/**
*无论当前事务是否存在,都会创建新事务运行方法,
*否则新建一个事务运行子方法
/
    REQUIRES_NEW(3),
/**
*不支持事务,当前存在事务时,将挂起事务,运行方法
/
    NOT_SUPPORTED(4),
/**
*不支持事务,如果当前方法存在事务,则抛出异常,否则继续使用无事务机制运行
/
    NEVER(5),
/**
*在当前方法调用子方法时,如果子方法发生异常,
*只因滚子方法执行过的SQL ,而不回滚当前方法的事务
/
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

@Transactional 在某些场景下会失效(自调用失效)

Spring 数据库事务的约定, 其实现原理是AOP , 而AOP 的原理是动态代
理, 在自调用的过程中, 是类自身的调用,而不是代理对象去调用, 那么就不会产生AOP , 这样Spring
就不能把你的代码织入到约定的流程中, 于是就产生了现在看到的失败场景。为了克服这个问题,
用一个Service 去调用另一个Service , 这样就是代理对象的调用, Spring
才会将你的代码织入事务流程。当然也可以从Spring IoC 容器中获取代理对象去启用AOP

package com.example.springboot.service;

import com.example.springboot.dao.AppealDao;
import com.example.springboot.entity.Appeal;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class AppealServiceImpl implements AppealService,ApplicationContextAware {
    @Autowired
    private AppealDao appealDao;

    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,timeout = 1)
    public Appeal getAppeal(int id) {
        return appealDao.getAppeal(id);
    }

    /**
     *NESTED 在当前方法调用子方法时,如果子方法发生异常,只回滚子方法执行过的SQL,而不回滚当前方法的事务
     * REQUIRES_NEW 无论当前事务是否存在,都会创建新事务运行方法,这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,timeout = 1,propagation = Propagation.NESTED)
    public int insertAppeal(Appeal appeal) {
        return appealDao.insertAppeal(appeal);
    }

    /**
     * REQUIRED 需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务,否则新建一个事务运行子方法
     */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    public int insertAppeals(List<Appeal> appeals) {
        int count = 0;
        AppealService appealService = applicationContext.getBean(AppealService.class);
        for(Appeal appeal:appeals){
            count += appealService.insertAppeal(appeal);
        }
        return count;
    }


}

猜你喜欢

转载自blog.csdn.net/weixin_42218986/article/details/89515246