一、首先创建一个使用单例模式的ConnectionContext类
public class ConnectionContext {
/**
* 构造方法私有化,将ConnectionContext设计成单例
*/
private ConnectionContext(){
}
//创建ConnectionContext实例对象
private static ConnectionContext connectionContext = new ConnectionContext();
/**
* @Field: connectionThreadLocal
* 使用ThreadLocal存储数据库连接对象
*/
private static ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>();
/**
* @Method: getInstance
* @Description:获取ConnectionContext实例对象
*
* @return
*/
public static ConnectionContext getInstance(){
return connectionContext;
}
/**
* @Method: bind
* @Description:利用ThreadLocal把获取数据库连接对象Connection和当前线程绑定
* @param connection
*/
public void bind(Connection connection){
connectionThreadLocal.set(connection);
}
/**
* @Method: getConnection
* @Description:从当前线程中取出Connection对象
*
* @return
*/
public Connection getConnection(){
return connectionThreadLocal.get();
}
/**
* @Method: remove
* @Description: 解除当前线程上绑定Connection
*
*/
public void remove(){
connectionThreadLocal.remove();
}
二、在Dao层的类StudentDaoImpl创建数据库操作方法加钱和减钱的方法,注意异常往上抛
public class StudentDaoImpl {
public void inMoney(Integer id,Integer money) throws Exception {
String sql = "update test set money=money+? where id=?";
QueryRunner queryRunner = new QueryRunner();
queryRunner.update(ConnectionContext.getInstance().getConnection(), sql,money,id);
}
public void outMoney(Integer id,Integer money) throws Exception {
String sql = "update test set money=money-? where id=?";
QueryRunner queryRunner = new QueryRunner();
queryRunner.update(ConnectionContext.getInstance().getConnection(), sql,money,id);
}
}
三、在Service层提供对Dao层的调用,注意异常往上抛
public class StudentServiceImpl {
private StudentDaoImpl studentDaoImpl = new StudentDaoImpl();
public void transfer() throws Exception {
studentDaoImpl.inMoney(1, 10);
int i=1/0;
studentDaoImpl.outMoney(2, 10);
}
}
四、在控制层Servlet类StudentServlet调用Service层的方法,注意抛出一个自定义的异常,这里的自定义异常为MyException
public class StudentServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private StudentServiceImpl studentServiceImpl = new StudentServiceImpl();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
try {
studentServiceImpl.transfer();
} catch (Exception e) {
throw new MyException();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
五、创建过滤器TransactionFilter,这里实现对事务的处理
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
//1、获取数据库连接对象Connection
Connection connection = JDBCUtils3.getConnection();
//2、利用ThreadLocal把获取数据库连接对象Connection和当前线程绑定
ConnectionContext.getInstance().bind(connection);
//3、开启事务
JDBCUtils3.startTransaction();
//4、把请求转发给目标Servlet
chain.doFilter(request, response);
//5、提交事务
JDBCUtils3.commit();
} catch (Exception e) {
e.printStackTrace();
//6、回滚事务
JDBCUtils3.rollback();
}finally{
//7、解除绑定
ConnectionContext.getInstance().remove();
//8、关闭数据库连接
JDBCUtils3.close();
}
}
六、总结
使用ThreadLocal+filter对JDBC事务处理的原理过程是创建的ConnectionContext类使用单例模式保证Dao层调用数据库连接的对象实例和过滤器TransactionFilter绑定和解绑数据库连接的对象实例是同一个实例。过滤器TransactionFilter首先执行获取数据库连接、绑定连接到ThreadLocal、开启事务操作,然后 chain.doFilter(request, response)放行让servlet执行,而当Dao、Service和Servlet层出现异常时,由于异常是层层往上抛,所以Servlet层抛出的异常会被TransactionFilter捕获,捕获后进入TransactionFilter的catch里执行事务回滚。