手写一个@Transactional注解,实现事务回滚
一,自定义注解
/**
* 自定义事务注解
* 空注解,用来标识
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTransactional {
}
二,自己的连接管理器
/**
* 自己的连接器
*/
@Component
public class MyConnectionManager {
/**
* 这个是最容易出错的点,连接要让每一个线程独立,不然当一个线程的操作,产生异常后,事务回滚,那就会把另一线程的操作也回滚掉,这就产生线程不安全问题
*/
private ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();
@Autowired
private DataSource dataSource;
public Connection getConnection() {
Connection connection = this.connectionThreadLocal.get();
if (null == connection) {
try {
Connection connection1 = dataSource.getConnection();
connectionThreadLocal.set(connection1);
} catch (SQLException e) {
e.printStackTrace();
}
}
return this.connectionThreadLocal.get();
}
}
三,自己的数据库操作模板类
/**
* 自己的数据库操作模板类
*/
@Component
public class MyJdbcTemplate {
@Autowired
private MyConnectionManager connectionManager;
// 要throws exception
public void execute(String sql) throws SQLException {
Connection connection = connectionManager.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.execute();
}
}
四,定义切面,实现注解核心逻辑
/**
* 将自定义的事务注解,使用AOP的方式,来增强它的功能
*/
@Aspect
@Component
public class MyTransactionalAspect {
@Autowired
private MyConnectionManager connectionManager;
/**
* 让标记着@myTransactional注解的方法,不直接调用,而是在该方法前后增加代码,增强该方法功能
* @param proceedingJoinPoint
* @param myTransactional 这个是增强点的定义,也就是说当遇到这个增强点标签(这里是@MyTransactional)时,就切入到它的代码中,@MyTransactional就是切入点
* 可以参考:https://www.cnblogs.com/ssslinppp/p/5845659.html
* @throws Throwable
*/
@Around("@annotation(myTransactional)")
public void doTransactional(ProceedingJoinPoint proceedingJoinPoint, MyTransactional myTransactional) throws Throwable {
Connection conn = null;
try {
/**
* 方法执行前增强
*/
System.out.println("setAutoCommit false");
conn = connectionManager.getConnection();
conn.setAutoCommit(false);
/**
* 调用实际的方法,即标注@myTransactional注解的方法
*/
proceedingJoinPoint.proceed();
/**
* 方法执行后增强
*/
conn.commit();
System.out.println("=====事务提交======");
} catch (Exception e) {
e.printStackTrace();
conn.rollback();
System.out.println("=====事务回滚=====");
throw new Exception();
}
}
}
五,测试
@MyTransactional // 要throws异常才能回滚,如果被try catch那就相当于没有异常,所以不会回滚
public void addUser(User user) throws Exception {
String sql = "insert into t_user(age, name, address) " +
"values(" + user.getAge() + ",'" + user.getName() + "','" + user.getAddress() + "')";
System.out.println(sql);
myJdbcTemplate.execute(sql);
String sql2 = "insert into t_log(content)values('增加用户,用户名为:"+user.getName()+"')";
myJdbcTemplate.execute(sql2);
System.out.println("==========Add user completed============");
//测试回滚功能
System.out.println("制造异常,测试回滚功能!");
throw new Exception();
}
作者:樊同学
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~