Spring事务的实现方式和实现原理;事务声明的方式,Spring的事务传播行为,spring事务的实现原理

Spring事务的实现方式和实现原理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。

什么是事务

数据库事务是指作为单个逻辑工作单元执行的一系列操作,这些操作要么一起成功,要么一起失败,是一个不可分割的工作单元。

在我们日常工作中,涉及到事务的场景非常多,一个 service 中往往需要调用不同的 dao 层方法,这些方法要么同时成功要么同时失败,我们需要在 service 层确保这一点

事务的四大特性:A:原子性 C:一致性 I:隔离性 D:持久性

Spring支持的事务管理类型, spring 事务实现方式有哪些?

Spring支持两种类型的事务管理:

编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。

声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。

Spring中事务的实现方式

1、编程式---实现事务

​ 在applicationContext.xml中配置好数据源,和事务管理器:

以上这种方式 不推荐使用,代码入侵太多。大量的处理事务的代码穿插到业务代码中

2、声明式---实现事务

(1)、声明式事务:xml形式 提前配置好数据源

  • 配置事务管理器
  • 配置通知,添加事务的切面
  • Aop的织入,将切面和切入点绑定起来

(2)、configration配置类的形式配置声明式事务

​ 1、配置好数据源信息 2、配置事务管理器 3、开启事务的注解支持

将该配置类添加到包扫描路径下,接来下就可以直接在service的方法或者类上使用@Transactional注解给方法添加事务

(3)、xml+注解方式配置声明式事务

配置完成后,只需要在想要开启注解的方法上加上@Transactional注解就可以了

说一下Spring的事务传播行为

spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。

① PROPAGATION_REQUIRED:默认的事务传播,如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

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

⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

在一个事务执行的过程中,调用另一个事务时候(比如一个service方法调用另一个service方法),这个事务将以何种状态存在,是两个事务共存呢,还是一个事务是另一个事务的子事务,还是一个事务加入另一个事务的子事务呢......利用事务的传播性来解决这个问题。

​ 1、REQUIRED : spring默认的事务的传播性

REQUIRED 表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

java 复制代码
@Service
public class AccountService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Transactional
    public void handle1() {
        jdbcTemplate.update("update user set money = ? where id=?;", 1, 2);
    }
}
@Service
public class AccountService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    AccountService accountService;
    public void handle2() {
        jdbcTemplate.update("update user set money = ? where username=?;", 1, "zhangsan");
        accountService.handle1();
    }
}

如果 handle2 方法本身是有事务的,则 handle1 方法就会加入到 handle2 方法所在的事务中,这样两个方法将处于同一个事务中,一起成功或者一起失败(不管是 handle2 还是 handle1 谁抛异常,都会导致整体回滚)。

如果 handle2 方法本身是没有事务的,则 handle1 方法就会自己开启一个新的事务。

2、REQUIRES_NEW

​ REQUIRES_NEW 表示创建一个新的事务,如果当前存在事务,则把当前事务挂起。换言之,不管外部方法是否有事务,REQUIRES_NEW 都会开启自己的事务。

3、NESTED

​ NESTED 表示如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED。

4、MANDATORY

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

5、SUPPORTS

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

6、NOT_SUPPORTED

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

7、NEVER

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

spring事务的实现原理

​ 底层是通过aop进行实现,@Transactional注解使用环绕通知,在进入方法前开启事务 。使用try catch包含目标方法,执行目标方法,执行完成后如果没有抛出异常,就提交事务 。如果抛出异常就进行回滚

代码实现:

定义注解:

java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface rkTransactional {
}

切面:

java 复制代码
@Aspect
@Component
@Slf4j
public class ExtrkThreadAop {
    @Autowired
    private RkTransaction rkTransaction;
 
    /**
     * 只要方法上有加上rkTransactional 走around()
     * 异常通知
     * @param joinPoint
     * @throws Throwable
     */
    @Around(value = "@annotation(com.rk.aop.rkTransactional)")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 在目标方法之前开启事务  底层实现:将事务状态保存在当前线程里面
        TransactionStatus transactionStatus = rkTransaction.begin();
        try {
            Object result = joinPoint.proceed();//目标方法
            log.info("目标方法之后执行");
            //提交事务
            rkTransaction.commit(transactionStatus);
            return result;
        } catch (Throwable throwable) {
            // 目标方法执行向外抛出异常之后 手动回滚
            rkTransaction.rollback(transactionStatus);
            return "fail";
        }
    }
}

注解类:

java 复制代码
@Component
public class RkTransaction {
 
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
 
    // 开启事务
    public TransactionStatus begin() {
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transaction;
    }
 
    // 提交事务
    public void commit(TransactionStatus transactionStatus) {
        dataSourceTransactionManager.commit(transactionStatus);
    }
 
    // 回滚事务
    public void rollback(TransactionStatus transactionStatus) {
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}

test: 测试

java 复制代码
    /**
     * 使用事务注解 事务到底在什么时候提交呢?该方法没有抛出异常的情况下就会自动提交事务
     * aop
     * @param name
     * @return
     */
    @GetMapping("/insertUser")
    @rkTransactional
    public String insertUser(String name) {
        int result = userMapper.insertUser(name);
        if ("rk".equals(name)) {
            int j = 1 / 0;
        }
        return result > 0 ? "ok" : "fail";
    }
}
相关推荐
小小寂寞的城34 分钟前
JAVA观察者模式demo【设计模式系列】
java·观察者模式·设计模式
探索java1 小时前
Java并发编程中的StampedLock详解:原理、实践与性能优化
java·stampedlock
界面开发小八哥1 小时前
「Java EE开发指南」如何用MyEclipse将Java项目转换为Web项目?
java·ide·java-ee·eclipse·开发工具·myeclipse
pobu1681 小时前
aksk前端签名实现
java·前端·javascript
一个天蝎座 白勺 程序猿2 小时前
飞算JavaAI进阶:重塑Java开发范式的AI革命
java·开发语言·人工智能
前端 贾公子2 小时前
tailwindCSS === 使用插件自动类名排序
java·开发语言
没有bug.的程序员2 小时前
JAVA面试宝典 -《Spring Boot 自动配置魔法解密》
java·spring boot·面试
hnlucky3 小时前
《Nginx + 双Tomcat实战:域名解析、静态服务与反向代理、负载均衡全指南》
java·linux·服务器·前端·nginx·tomcat·web
hnlucky3 小时前
同时部署两个不同版本的tomcat要如何配置环境变量
java·服务器·http·tomcat·web