手写一个@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();
    }
posted @ 2022-03-03 16:08  你樊不樊  阅读(161)  评论(0编辑  收藏  举报