当前位置: 首页 > article >正文

Spring 事务注解原理

spring事务实现方式

1. 手动式事务

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JdbcTransactionExample {
    public static void main(String[] args) {
        Connection conn = null;
        try {
            // 1. 创建数据库连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");

            // 2. 开启事务
            conn.setAutoCommit(false);

            // 3. 执行数据库操作
            updateData(conn);
            insertData(conn);

            // 4. 提交事务
            conn.commit();

        } catch (SQLException e) {
            e.printStackTrace();
            // 5. 回滚事务
            try {
                if (conn != null) {
                    conn.rollback();
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            // 6. 关闭数据库连接
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static void updateData(Connection conn) throws SQLException {
        String sql = "UPDATE table_name SET column_name = ? WHERE id = ?";
        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, "new value");
            stmt.setInt(2, 1);
            stmt.executeUpdate();
        }
    }

    private static void insertData(Connection conn) throws SQLException {
        String sql = "INSERT INTO table_name (column1, column2) VALUES (?, ?)";
        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, "value1");
            stmt.setString(2, "value2");
            stmt.executeUpdate();
        }
    }
}

通过 JDBC 原生命令操作事务

2. 声明式事务

public class UserService {

private final DataSource dataSource;

@Autowired
public UserService(DataSource dataSource) {
    this.dataSource = dataSource;
}

@Transactional
public void performTransaction() {
    Connection conn = null;
    try {
        conn = dataSource.getConnection();

        // 3. 执行数据库操作
        updateData(conn);
        insertData(conn);

    } catch (SQLException e) {
        e.printStackTrace();
        throw new RuntimeException("Transaction failed.", e);
    } finally {
        // 6. 关闭数据库连接
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

private void updateData(Connection conn) throws SQLException {
    String sql = "UPDATE table_name SET column_name = ? WHERE id = ?";
    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
        stmt.setString(1, "new value");
        stmt.setInt(2, 1);
        stmt.executeUpdate();
    }
}

private void insertData(Connection conn) throws SQLException {
    String sql = "INSERT INTO table_name (column1, column2) VALUES (?, ?)";
    try (PreparedStatement stmt = conn.prepareStatement(sql)) {
        stmt.setString(1, "value1");
        stmt.setString(2, "value2");
        stmt.executeUpdate();
    }
}

如果有事务注解我们是如何使用的?

  1. 在方法上添加@Transactional注解即可。

Q:为什么事务注解这么方便,原理是什么(动态代理)?

  1. 声明式事务的书写方式就是在方法上加上一个@Transactional注解,这里涉及到了Spring的AOP思想,即:把当前方法作为一个切面,对其执行前后进行功能增强。
  2. AOP的原理就是动态代理,通过扫描对应的注解,创建相应的代理对象,并实现功能增强。简单看一下源码:
    在这里插入图片描述

在try块中代理对象调用了增强方法,catch块中对异常情况进行回滚处理,finally块中清理本次事务的信息。

Q:事务注解有哪些参数可以控制?

  1. 传播行为(Propagation):

    1. REQUIRED(0) : 默认的事务传播级别,它表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
    2. SUPPORTS(1):如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    3. MANDATORY(2):如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
    4. REQUIRES_NEW(3):表示创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰
    5. NOT_SUPPORTED(4):以非事务方式运行,如果当前存在事务,则把当前事务挂起。
    6. NEVER(5):以非事务方式运行,如果当前存在事务,则抛出异常。
    7. NESTED(6):如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED
  2. 回滚处理(RollbackFor):控制事务什么时候回滚,如果不配置,默认在遇到RuntimeException和Error时才回滚。

    1. @Transactional(rollbackFor = Exception.class) 配置之后,遇见非运行时异常时回滚。
  3. 隔离级别(Isolation):与数据库隔离级别一致,默认为Default,直接使用数据库的隔离级别。注意:如果Spring的隔离级别与MySQL不一致,则以Spring为准。

Q:事务失效的场景有哪些?

  1. 事务方法执行期间出现了异常,但是并未指定rollbackFor:Spring默认只会在遇到error和RunTimeException时才会回滚。
public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
}
  1. 事务方法执行期间出现了异常,但被方法本身捕获: 使用catch进行捕获之后,Spring无法感知到异常,无法回滚。
  2. 同一个类种方法互相调用: 因为Spring事务的本质是动态代理,通过生成代理对象去调用方法,并且在方法前后增加事务效果;同一类中的方法调用时,无法使用代理对象调用,使用的是this调用,因此无法实现动态代理效果。
  3. 方法不是public: Spring源码做了判断,如果不是Public会直接返回。
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
  return null;
}
  1. 方法是final或static: 考虑动态代理的实现原理,无论是基于JDK还是CGLib,都不允许final和static的修饰。

http://www.kler.cn/a/593983.html

相关文章:

  • [Xilinx]工具篇_Vivado自动安装
  • 计算机网络快速入门
  • 【C#知识点详解】ExcelDataReader介绍
  • 记一次性能调优-20250320
  • 【嵌入式硬件】 天线与距离问题
  • JVM常用概念之压缩引用
  • C语言的setjmp和longjmp:可以作异常处理
  • 【数据分享】2000—2024年我国乡镇的逐月归一化植被指数(NDVI)数据(Shp/Excel格式)
  • 微信小程序面试内容整理-请求优化
  • 经典面试题:C/C++中static关键字的三大核心作用与实战应用
  • 计算机四级 - 数据库原理(操作系统部分)- 第4章「并发与同步」
  • Numpy broadcasting规则
  • 3.20学习总结 java面向对象+string函数
  • New Friends(并查集)
  • Vue.js 组件开发全解析:从基础概念到实战应用
  • Compose Indication:点击效果设置
  • 碰一碰发视频后端源码技术,支持OEM
  • Python绘图技巧,主流绘图库
  • UI设计中的信息架构:组织内容的艺术
  • java项目之基于ssm的疫苗预约系统(源码+文档)