您当前的位置: 首页 > 

梁云亮

暂无认证

  • 1浏览

    0关注

    1211博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【精品】利用动态代理实现事务统一管理 二

梁云亮 发布时间:2022-04-22 09:00:13 ,浏览量:1

DAO层
  • 接口
public interface DeptDao{
	int insert(Dept dept);
}
public interface EmpDao{
	int insert(Emp emp);
}
  • 实现类
public class DeptDaoImpl implements DeptDao{
	@Override
	public int insert(Dept dept){
		QueryRunner queryRunner = new QueryRunner(TransactionManager.getDataSource());
		// ....... 具体操作数据库实现插入数据到数据库表的操作
	}
}
public class EmpDaoImpl implements EmpDao{
	@Override
	public int insert(Emp emp){
		QueryRunner queryRunner = new QueryRunner(TransactionManager.getDataSource());
		// ....... 具体操作数据库实现插入数据到数据库表的操作
	}
}
Service层
  • 接口
public interface EmpService{
	/**
	  * 同时添加员工,以及该员工所在的部门
	  */
	int insert(Emp emp,Dept dept);
}
  • 实现类
public class EmpServiceImpl{
	private DeptDao dpetDao = new DeptDaoImpl();
	private EmpDao empDao = new EmpDaoImpl();
	/**
	  * 同时添加员工,以及该员工所在的部门
	  */
	int insert(Emp emp,Dept dept){
		deptDao.insert(conn,dept);
		System.out.println(3/0);
		empDao.insert(conn,emp);
	}
}
事务管理器
public class TransactionManager {
    private static DataSource dataSource;
    //用来给本地线程放置Connection代理对象
    private static ThreadLocal proxyConnLocal = new ThreadLocal();
    //用来给本地线程放置真正的Connection对象
    private static ThreadLocal realConnLocal = new ThreadLocal();

    static {
        Properties properties = new Properties();
        String propertiesFile = "/mysql.properties";
        try {
            InputStream is = TransactionManager.class.getResourceAsStream(propertiesFile);
            properties.load(is);
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private TransactionManager() {
    }

    /**
     * 获取数据源DataSource
     *
     * @return 如果需要开启事务的话,返回datasource的代理,否则返回原生的datasource
     */
  public static DataSource getDataSource() {
      DataSource ds = (DataSource) Proxy.newProxyInstance(dataSource.getClass().getClassLoader(), dataSource.getClass().getInterfaces(), new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              if ("getConnection".equals(method.getName())) {
                  return proxyConnLocal.get();
              } else {
                  return method.invoke(proxy, args);
              }
          }
      });
      return ds;
  }

    /**
     * 开启事务
     *
     * @throws SQLException
     */
    public static void startTransaction() throws SQLException {
        Connection conn = dataSource.getConnection();
        //设置事务不自动提交
        conn.setAutoCommit(false);
        //将真正的Connection对象放到本地线程中
        realConnLocal.set(conn);
        //开启事务
        isTransactionEnabled.set(true);

        //设置Connection对象不自动关闭
        Connection proxyConn = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("close".equals(method.getName())) { //
                    return null;
                } else {
                    return method.invoke(conn, args);
                }
            }
        });
        //将Connection代理对象放到本地线程中
        proxyConnLocal.set(proxyConn);
    }

    /**
     * 提交事务
     */
    public static void commitTransaction() {
        DbUtils.commitAndCloseQuietly(proxyConnLocal.get());
    }

    /**
     * 回滚事务
     */
    public static void rollbackTransaction() {
        DbUtils.rollbackAndCloseQuietly(proxyConnLocal.get());
    }

    public static void release() {
        DbUtils.closeQuietly(realConnLocal.get());
        realConnLocal.remove();
        proxyConnLocal.remove();
        isTransactionEnabled.remove();
    }
    
}
动态代理事务增强
public class ServiceProxy implements InvocationHandler {
    private Object service;

    public ServiceProxy(Object service) {
        this.service = service;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        //方法上面是否标有事务
        try {
            TransactionManager.startTransaction();
            //业务方法
            res = method.invoke(service, args);
            TransactionManager.commitTransaction();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionManager.rollbackTransaction();
        } finally {
            TransactionManager.release();
        }
        return res;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), this);
    }

}

测试代码
public static void main(String[] args) throws SQLException {
	Dept dept = new Dept(20,"ACCOUNTING","NEWYORK");
	Emp emp = new Emp(7369, "SMITH", "CLERK", 7902,LocalDate.of(1980, 12, 17), 800D, null, 20);
	EmpService empService = EmpServiceImpl();
    ServiceProxy serviceProxy = new ServiceProxy(empService);
    EmpService proxy = (EmpService) serviceProxy .getProxy();

    boolean addRes = proxy.add(emp,dept);
    System.out.println(addRes);
}
存在问题

采用本博客介绍的技术基本上就解决了事务的统一管理。但上面代码还存在一个小问题,就是这样一来所有执行DeptDao的insert()和EmpDao的insert()方法都会有事务支持,具体解决方案就是在True中添加一个本地变量来标识是否需要返回支持事务的DataSource就可以了。有关这一块的讲解,请参看代码:https://gitee.com/hcshow/credits,项目中有详细的代码讲解与应用案例。

关注
打赏
1665409997
查看更多评论
立即登录/注册

微信扫码登录

0.0442s