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,项目中有详细的代码讲解与应用案例。