欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > Spring 事务注解原理

Spring 事务注解原理

2025/3/21 20:03:25 来源:https://blog.csdn.net/dream410/article/details/146397655  浏览:    关键词: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的修饰。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词