动力节点旗下在线教育品牌  |  咨询热线:400-8080-105 学Java全栈,上蛙课网
首页 > 文章

Apache_DBUtils框架教程学习笔记(五)_Filter 处理事务

07-12 11:39 1256浏览
举报 T字号
  • 大字
  • 中字
  • 小字

4.4、ThreadLocal + Filter 处理事务

  上面介绍了JDBC开发中事务处理的3种方式,下面再介绍的一种使用ThreadLocal + Filter进行统一的事务处理,这种方式主要是使用过滤器进行统一的事务处理,如下图所示

 

1、编写一个事务过滤器TransactionFilter

代码如下:

 

package wkcto.com.web.filter;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import wkcto.com.util.JdbcUtils;

/**
* @ClassName: TransactionFilter
* @Description:ThreadLocal + Filter 统一处理数据库事务
*
*/ 
public class TransactionFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        Connection connection = null;
        try {
            //1、获取数据库连接对象Connection
            connection = JdbcUtils.getConnection();
            //2、开启事务
            connection.setAutoCommit(false);
            //3、利用ThreadLocal把获取数据库连接对象Connection和当前线程绑定
            ConnectionContext.getInstance().bind(connection);
            //4、把请求转发给目标Servlet
            chain.doFilter(request, response);
            //5、提交事务
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //6、回滚事务
            try {
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            //req.setAttribute("errMsg", e.getMessage());
            //req.getRequestDispatcher("/error.jsp").forward(req, res);
            //出现异常之后跳转到错误页面
            res.sendRedirect(req.getContextPath()+"/error.jsp");
        }finally{
            //7、解除绑定
            ConnectionContext.getInstance().remove();
            //8、关闭数据库连接
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void destroy() {

    }
}

 

 

我们在TransactionFilter中把获取到的数据库连接使用ThreadLocal绑定到当前线程之后,在DAO层还需要从ThreadLocal中取出数据库连接来操作数据库,因此需要编写一个ConnectionContext类来存储ThreadLocal,ConnectionContext类的代码如下:

 

package wkcto.com.web.filter;

import java.sql.Connection;

/**
* @ClassName: ConnectionContext
* @Description:数据库连接上下文
*
*/ 
public class ConnectionContext {

    /**
     * 构造方法私有化,将ConnectionContext设计成单例
     */
    private ConnectionContext(){
        
    }
    //创建ConnectionContext实例对象
    private static ConnectionContext connectionContext = new ConnectionContext();
    
    /**
    * @Method: getInstance
    * @Description:获取ConnectionContext实例对象
    *
    * @return
    */ 
    public static ConnectionContext getInstance(){
        return connectionContext;
    }
    
    /**
    * @Field: connectionThreadLocal
    *         使用ThreadLocal存储数据库连接对象
    */ 
    private ThreadLocal connectionThreadLocal = new ThreadLocal();
    
    /**
    * @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层想获取数据库连接时,就可以使用ConnectionContext.getInstance().getConnection()来获取,如下所示:

 

package wkcto.com.dao;

import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import wkcto.com.web.filter.ConnectionContext;
import wkcto.com.domain.Account;

/*
create table account(
    id int primary key auto_increment,
    name varchar(40),
    money float
)character set utf8 collate utf8_general_ci;

insert into account(name,money) values('A',1000);
insert into account(name,money) values('B',1000);
insert into account(name,money) values('C',1000);

*/

/**
* @ClassName: AccountDao
* @Description: 针对Account对象的CRUD
*
*/ 
public class AccountDao3 {

    public void update(Account account) throws SQLException{
        
        QueryRunner qr = new QueryRunner();
        String sql = "update account set name=?,money=? where id=?";
        Object params[] = {account.getName(),account.getMoney(),account.getId()};
        //ConnectionContext.getInstance().getConnection()获取当前线程中的Connection对象
        qr.update(ConnectionContext.getInstance().getConnection(),sql, params);
        
    }
    
    public Account find(int id) throws SQLException{
        QueryRunner qr = new QueryRunner();
        String sql = "select * from account where id=?";
        //ConnectionContext.getInstance().getConnection()获取当前线程中的Connection对象
        return (Account) qr.query(ConnectionContext.getInstance().getConnection(),sql, id, new BeanHandler(Account.class));
    }
}

 

 

businessService层也不用处理事务和数据库连接问题了,这些统一在TransactionFilter中统一管理了,businessService层只需要专注业务逻辑的处理即可,如下所示:

 

package wkcto.com.service;

import java.sql.SQLException;
import wkcto.com.dao.AccountDao3;
import wkcto.com.domain.Account;

public class AccountService3 {
    
    /**
    * @Method: transfer
    * @Description:在业务层处理两个账户之间的转账问题
    * @param sourceid
    * @param tartgetid
    * @param money
    * @throws SQLException
    */ 
    public void transfer(int sourceid, int tartgetid, float money)
            throws SQLException {
        AccountDao3 dao = new AccountDao3();
        Account source = dao.find(sourceid);
        Account target = dao.find(tartgetid);
        source.setMoney(source.getMoney() - money);
        target.setMoney(target.getMoney() + money);
        dao.update(source);
        // 模拟程序出现异常让事务回滚
        int x = 1 / 0;
        dao.update(target);
    }
}

 

 

Web层的Servlet调用businessService层的业务方法处理用户请求,需要注意的是:调用businessService层的方法出异常之后,继续将异常抛出,这样在TransactionFilter就能捕获到抛出的异常,继而执行事务回滚操作,如下所示:

 

package wkcto.com.web.controller;

import java.io.IOException;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import wkcto.com.service.AccountService3;

public class AccountServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        AccountService3 service = new AccountService3();
        try {
            service.transfer(1, 2, 100);
        } catch (SQLException e) {
            e.printStackTrace();
            //注意:调用service层的方法出异常之后,继续将异常抛出,这样在TransactionFilter就能捕获到抛出的异常,继而执行事务回滚操作
            throw new RuntimeException(e);
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

 

1人推荐
共同学习,写下你的评论
0条评论
HelloWorld
程序员HelloWorld

11篇文章贡献50393字

作者相关文章更多>

推荐相关文章更多>

MySQL开发规范

达芬奇09-04 11:38

必须掌握的30种SQL语句优化

不写代码你养我啊08-23 11:30

ES(Elasticsearch)支持PB级全文搜索引擎入门教程

HelloWorld09-29 17:28

Java面试中的hadoop面试题及答案整理

兔子06-15 17:15

数据库分类之速成篇

呵呵呀06-17 18:54

发评论

举报

0/150

取消