是?我们只管写dao方法,service方法,不需要关心在哪边commit哪边rollback何时connection,spring的声明式事务会帮我们负责,这种风格我们称为“优雅”,各层间耦合度极大程度上的降低,封装性好。
因此,我们可以总结出下面这些好处:
- Service层的方法只管开启事务(如果讲究点的还会设一个Transaction);
- 在该Service层中的所有dao使用该service方法中开启的事务(即connection);
- Dao中每次只管getCurrentConnection(获取当前的connection),与进行数据处理
- Dao层中如果发生错误就抛回Service层
- Service层中接到exception,在catch{}中rollback,在try{}未尾commit,在finally块中关闭整个connection。
这。。。就是我们所说的ThreadLocal。
举个更实际的例子再次来说明ThreadLocal:
我们有3个用户访问同一个service方法,该service方法内有3个dao方法为一个完整事务,那么整个web容器内只因该有3个connection,并且每个connection之间的状态,彼此“隔离”。
我们下面一起来看我们如何用代码实现类似于Spring的这种做法。
首先,根据我们的ThreadLocal的概念,我们先声明一个ConnectionManager的类。
2.4 利用ThreadLocal制作ConnectionManager
public class ConnectionManager {
private static ThreadLocal tl = new ThreadLocal();
private static Connection conn = null;
public static void BeginTrans(boolean beginTrans) throws Exception {
if (tl.get() == null || ((Connection) tl.get()).isClosed()) {
conn = SingletonDBConnection.getInstance().getConnection();
conn = new ConnectionSpy(conn);
if (beginTrans) {
conn.setAutoCommit(false);
}
tl.set(conn);
}
}
public static Connection getConnection() throws Exception {
return (Connection) tl.get();
}
public static void close() throws SQLException {
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
((Connection) tl.get()).close();
tl.set(null);
}
public static void commit() throws SQLException {
try {
((Connection) tl.get()).commit();
} catch (Exception e) {
}
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
}
public static void rollback() throws SQLException {
try {
((Connection) tl.get()).rollback();
} catch (Exception e) {
}
try {
((Connection) tl.get()).setAutoCommit(true);
} catch (Exception e) {
}
}
}
2.5 利用ThreadLocal改造Service与Dao层
Service层(注意红色标粗-好粗yeah,的地方)
package sky.org.service.impl;
public class StudentServiceImpl implements StudentService {
public void addStudent(Student std) throws Exception {
StudentDAO studentDAO = new StudentDAOImpl();
ClassRoomDAO classRoomDAO = new ClassRoomDAOImpl();
try {
ConnectionManager.BeginTrans(true);
studentDAO.addStudent(std);
classRoomDAO
.addStudentClassRoom(std.getClassRoomId(), std.getsNo());
ConnectionManager.commit();
} catch (Exception e) {
try {
ConnectionManager.rollback();
} catch (Exception de) {
}
throw new Exception(e);
}finally {
try {
ConnectionManager.close();
} catch (Exception e) {
}
}
}
}
Look,如果我把上述标粗(没有加红色)的地方,全部用AOP的方式从这块代码的外部“切”进去。。。是不是一个Spring里的Service方法就诞生了?
下面来看一个完整的例子
2.6 使用ThreadLocal分离Service、DAO层
先来看表结构:
T_Student表
T_ClassRoom表
T_Student_ClassRoom表
需求:
很简单,T_ClassRoom表里已经有值了,在插入T_Student表的数据时同时要给这个学生分配一个班级并且插入T_Student_ClassRoom表,这就是一个事务,这两步中有任何一步出错,事务必须回滚。
看来工程的结构吧:
下面开始放出所有源代码:
2.6.1 ConnectionManager类
package sky.org.util.db;
import java.sql.*;
public class ConnectionManager {
private static ThreadLocal tl = new ThreadLocal();
private static Connection conn = null;
public static void BeginTrans(boolean beginTrans) throws Exception {