Spring Boot+Druid下自定义@myTransactional注解的实现方案咨询
问题描述
当前手动事务管理代码如下:
public void exec(){ Connection conn = null; try{ Connection conn = getConnection(); save(user1,conn); update(school,conn); delete(home,conn); conn.commit() }catch(Exception e) { conn.rollback(); }finally{ conn.close(); } } getConnection(){ conn = dataSource.getConnection(); conn.setAutoCommit(false); } save(entity,conn){ String SQL = createSQL(entity); stmt = conn.prepareStatement(SQL,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); }
期望简化为如下形式:
@myTransactional public void exec(){ save(user1); update(school); delete(home); }
使用Spring Boot框架,仅依赖Druid,未使用JdbcTemplate。咨询以下问题:
- 在自定义
@myTransactional注解中如何获取Connection? - 如何执行提交或回滚操作?
- 是否有其他实现方案?
解决方案
一、自定义@myTransactional注解获取Connection的方式
核心是通过ThreadLocal绑定当前线程的Connection,结合Spring AOP实现方法拦截:
- 先定义自定义注解:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface myTransactional { }
- 编写AOP切面类,用ThreadLocal存储线程专属Connection:
@Component @Aspect public class TransactionAspect { @Autowired private DruidDataSource dataSource; // ThreadLocal确保同一事务内的所有方法共享同一个Connection private ThreadLocal<Connection> connThreadLocal = new ThreadLocal<>(); @Pointcut("@annotation(com.xxx.myTransactional)") public void transactionPointcut() {} @Around("transactionPointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Connection conn = null; try { // 从Druid获取连接并关闭自动提交 conn = dataSource.getConnection(); conn.setAutoCommit(false); // 将连接存入ThreadLocal connThreadLocal.set(conn); // 执行目标业务方法 Object result = joinPoint.proceed(); return result; } catch (Throwable e) { throw e; } finally { // 后续提交/回滚、关闭连接逻辑放在这里,下文详述 } } // 提供给save/update/delete方法获取当前线程Connection的入口 public Connection getCurrentConn() { return connThreadLocal.get(); } }
- 修改业务方法,从切面类获取Connection,无需手动传入:
@Autowired private TransactionAspect transactionAspect; public void save(User user) { Connection conn = transactionAspect.getCurrentConn(); String SQL = createSQL(user); try (PreparedStatement stmt = conn.prepareStatement(SQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) { stmt.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } }
二、提交/回滚操作的实现
在AOP切面的环绕通知中完成事务的提交与回滚:
@Around("transactionPointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Connection conn = null; try { conn = dataSource.getConnection(); conn.setAutoCommit(false); connThreadLocal.set(conn); Object result = joinPoint.proceed(); // 方法正常执行,提交事务 conn.commit(); return result; } catch (Throwable e) { // 方法抛出异常,回滚事务 if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } throw e; } finally { // 释放连接并清除ThreadLocal,避免内存泄漏 if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } connThreadLocal.remove(); } }
三、其他实现方案
方案1:直接使用Spring原生@Transactional
Spring原生事务管理完全支持Druid数据源,无需自定义注解:
- 配置Druid数据源(application.yml):
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://localhost:3306/test_db username: root password: your_password
- 在启动类或配置类上添加
@EnableTransactionManagement开启事务支持 - 直接在业务方法上标注
@Transactional,Spring会自动处理Connection绑定、事务提交/回滚:
@Transactional public void exec(){ save(user1); update(school); delete(home); }
方案2:使用Spring TransactionTemplate编程式事务
如果偏好编程式风格,可以用TransactionTemplate:
@Autowired private TransactionTemplate transactionTemplate; public void exec() { transactionTemplate.execute(status -> { save(user1); update(school); delete(home); return null; }); }
需提前配置TransactionTemplate:
@Bean public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) { return new TransactionTemplate(transactionManager); }
内容的提问来源于stack exchange,提问作者ighack
相关产品推荐
相关产品推荐

