自定义事务管理器TransactionManager对象

自定义事务管理器TransactionManager对象

以aop思想,实现事务管理切面

1. DataSource注册容器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启包扫描注解-->
    <context:component-scan base-package="com.hang"/>

    <!--导入jdbc.properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置druid数据库连接池-->
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
    </bean>

    <!--开启aop注解-->
    <aop:aspectj-autoproxy/>
</beans>

2. 定义事务管理器MyTransactionManager对象

定义事务管理器MyTransactionManager对象,注册进spring容器。(依赖Druid连接池,获取连接,开始事务,提交事务,回滚事务,关闭连接)

@Component
public class MyTransactionManager {
    @Autowired
    private DataSource dataSource;

    // 获取连接
    public Connection getConnection(){
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            ConnUtil.setConn(conn);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    // 开始事务
    public void openTransaction(){
        try {
            ConnUtil.getConn().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 提交事务
    public void openCommit(){
        try {
            ConnUtil.getConn().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    // 回滚事务
    public void openRollback(){
        try {
            ConnUtil.getConn().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 关闭连接
    public void closeConnection(){
        Connection conn = ConnUtil.getConn();
        try {
            if (conn!=null){
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

3. 定义dao接口

定义dao接口,使用jdbc定义dao实现,不要使用mybatis,把dao实现注册spring容器(curd)

public interface SysUserDao {

    SysUser queryById(Integer id) throws Exception;
    
    Integer insert(SysUser sysUser) throws Exception;
    
    int update(SysUser sysUser);

    int deleteById(Long id);
}

@Repository
public class SysUserDaoImpl implements SysUserDao {
    private Connection conn = null;
    private ResultSet rs = null;
    private PreparedStatement ps = null;

    @Override
    public SysUser queryById(Integer id) throws Exception {
        SysUser sysUser = null;
        conn = ConnUtil.getConn();
        ps = conn.prepareStatement("select * from db_stage_three.sys_user where  id = ?");
        ps.setInt(1, id);
        rs = ps.executeQuery();
        if (rs.next()) {
            sysUser = new SysUser();
            sysUser.setUname("hello");
        }
        return sysUser;
    }

    @Override
    public Integer insert(SysUser sysUser) throws Exception {
        Integer result = null;
        conn = ConnUtil.getConn();
        ps = conn.prepareStatement("insert into db_stage_three.sys_user ( uname, uphone, upwd, uwechat, uemail, role_id, dept_id) values (?,?,?,?,?,?,?)");
        ps.setString(1, sysUser.getUname());
        ps.setString(2, sysUser.getUphone());
        ps.setString(3, sysUser.getUpwd());
        ps.setString(4, sysUser.getUwechat());
        ps.setString(5, sysUser.getUemail());
        ps.setInt(6, sysUser.getRoleId());
        ps.setInt(7, sysUser.getDeptId());
        result = ps.executeUpdate();
        return result;
    }

    @Override
    public int update(SysUser sysUser) {
        return 0;
    }

    @Override
    public int deleteById(Long id) {
        return 0;
    }

}

4. 定义service接口

定义service接口,定义service实现,注册容器(curd)。

public interface SysUserService {
    
    SysUser queryById(Integer id);

    SysUser insert(SysUser sysUser);
}
@Service()
public class SysUserServiceImpl implements SysUserService {
    @Resource
    private SysUserDao sysUserDao;

    @Override
    public SysUser queryById(Integer id) {
        Connection conn = null;
        // 织入获取连接,开始事务,提交事务,回滚事务,关闭连接
        SysUser sysUser = null;
        try {
            sysUser = this.sysUserDao.queryById(id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sysUser;
    }

    @Transaction // 使用基于自定义注解方式定义切入点表达式
    @Override
    public SysUser insert(SysUser sysUser) {
        try {
            this.sysUserDao.insert(sysUser);
        } catch (Exception e) {
            e.printStackTrace();
        }
        int i = 1/0;
        return sysUser;
    }

}

自定义注解代码如下:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Transaction {
}

5. 定义事务切面(依赖MyTransactionManag)

定义事务切面(依赖MyTransactionManag),定义环绕通知,织入到service层的数据库操作方法,执行方法之前打开事务,异常回滚,方法返回提交事务,最终关闭连接。定义 ServiceTransAspect 类作为切面

@Component
@Aspect
public class ServiceTransAspect {

    @Autowired
    private MyTransactionManager transactionManager;

    // 基于 AspectJ 的切入点表达式
    @Pointcut("execution(* com.hang.service.impl.SysUserServiceImpl.*(..))")
    public void servicePointCut() {
    }

    // 后置通知关闭数据库连接对象
    @After("servicePointCut()")
    public void closeConnection(){
        Connection conn = ConnUtil.getConn();
        if (conn!=null){
            try {
                conn.close();
                ConnUtil.clear();
                conn = null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    // 基于自定义注解形式
    @Pointcut("@annotation(com.hang.aspect.Transaction)")
    public void annotationPointCut() {
    }

    // 环绕通知 实现事务的开启、回滚、提交和连接对象的关闭
    @Around(value = "annotationPointCut()")
    public Object serviceManage(ProceedingJoinPoint pjp) {
        Object result = null;
        try {
            transactionManager.getConnection();
            transactionManager.openTransaction();
            result = pjp.proceed();
            transactionManager.openCommit();
        } catch (Throwable throwable) {
            transactionManager.openRollback();
            throwable.printStackTrace();
        } finally {
            transactionManager.closeConnection();
        }
        return result;
    }

}

6. 自定义工具类

在并发环境下,如果没使用ThreadLocal类维护线程内部的局部变量Connection(单Connection对象),两个线程共同访问一个公共变量。例如A用户获取数据库连接对象,并将Connection对象占为己有,对数据进行操作时,B用户也获取到数据库连接对象,获取Connection对象,此时A不具有Connection对象,B用户完成事务提交关闭连接,此时Connection对象为null,A用户数据则会提交失败。

ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。

对ThreadLocal作用的总结:

  1. 线程并发: 在多线程并发的场景下
  2. 传递数据: 我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
  3. 线程隔离: 每个线程的变量都是独立的,不会互相影响
@Component
public class ConnUtil {
    private static ThreadLocal<Connection> threadLocalConn  = new ThreadLocal<>();
    @Resource
    private MyTransactionManager tm;

    private static MyTransactionManager manager;

    public static void clear() {
        threadLocalConn.remove();
    }

    @PostConstruct
    public void setTransactionManager(){
        manager = tm;
    }

    public static Connection getConn() {
        Connection connection = threadLocalConn.get();
        if (connection!=null){
            return  connection;
        }else {
            return manager.getConnection();
        }
    }
    public static void setConn(Connection conn) {
        threadLocalConn.set(conn);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值