Filter - 编码和事务管理

编码

Filter可以用于提前设置请求的编码

若写定了UTF-8则略显僵硬,可以配置initParams,然后在init()中先读取编码方式,交给成员变量,再让方法直接拿来用

@WebFilter(urlPatterns = {"*.do"},initParams = {
        @WebInitParam(name="encoding",value="UTF-8")
})
public class EncodingFilter implements Filter {
    private String encoding = "UTF-8";  //默认为utf-8
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String encodingStr = filterConfig.getInitParameter("encoding");
        if(!StringUtils.isEmpty(encodingStr)) {
            encoding = encodingStr;
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ((HttpServletRequest)servletRequest).setCharacterEncoding(encoding);
        filterChain.doFilter(servletRequest,servletResponse);  //放行
    }

    @Override
    public void destroy() {

    }
}

如此一来,就实现了在DS拦截请求之前,先由Filter负责对请求进行编码的过程,保证了信息存入数据库的正确性

保证事务原子性

当前体系下,一个Service(服务)中包含了多个DAO操作,而这个服务本身也应该是要求原子性的,所以,对于事务的管理应该放到Service层去

所以,代码结构应该是:

try{
    conn.setAutoCommit(false);   //取消自动提交
    Service();
    conn.commit();
}catch(Exception e){
    conn.rollback();
}

将这个操作也放到过滤器去执行,代码结构为:

try{
    conn.setAutoCommit(false);
    filterChain.doFilter(req, res);  //放行,捕捉内部所有Exception
    conn.commit();
}catch(Exception e){
    conn.rollback();
}

那么,这个过滤器就是OpenSessionInViewFilter

首先,确认所有DAO中都没有对conn进行close(),避免事务执行完毕前被close

接下来,为了方便对conn进行commit、rollback等控制,在第一篇文章就建立的TransactionManager中封装要对conn进行的操作:

public class TransactionManager {
    public static void beginTrans() throws SQLException {  //开始事务
        ConnUtil.getConn().setAutoCommit(false);
    }
    public static void commitTrans() throws SQLException {  //提交事务
        ConnUtil.getConn().commit();  //提交事务
        ConnUtil.close();  //关闭连接
    }
    public static void rollbackTrans() throws SQLException {  //回滚事务
        ConnUtil.getConn().rollback();
        ConnUtil.close();
    }
}

然后,在OpenSessionInViewFilter类的doFilter()方法中按照上文提到的代码结构来编写事务操作:

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException {
    try{  //捕获事务进行中发生的Exception
        //开始事务
        TransactionManager.beginTrans();
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        //一切正常,提交
        TransactionManager.commitTrans();
    } catch (Exception e) {  //注意这里是捕获所有类型的异常
    //捕获到错误,打印信息后回滚
        e.printStackTrace();
        try {
            TransactionManager.rollbackTrans();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
}

要保证事务的原子性,实际上就是让处于Filter中的try{}结构捕获到异常,这样一来就能执行回滚了

但这里存在一个问题:当DAO、Service、Controller、DS中已经存在try{}catch{}结构但未向外抛出异常,则异常不会被最顶层的Filter捕获,而是在下层中就被处理掉

因此,还需要对每一层的Exception进行处理并抛出的操作,层层上抛,最后得以被Filter捕获。为了便于提示、区分出错层级,可以新建一个异常类,以DAO层为例:

public class DAOException extends RuntimeException {
    public DAOException(String mes) {
        throw super(mes);  //构造一个RuntimeException并将之抛出
    }
}

在BaseDAO中,若catch到了异常,则在打印错误信息后向上抛出该错误

try{
    //...
}catch(Exception e){
    e.printStackTrace();
    throw new DAOException("DAO层出错");
}

在处理完BaseDAO之后,原本需要catch的DAO层不再需要catch了,删去它们的try{}catch{}结构即可

从下向上再看一遍,Service层没有catch过,Controller层也没有;DS层存在catch,因此再次进行抛出,代码略,最终,抛出的错误能够回到FIlter中被捕获,最后执行回滚

测试

删去DAOImpl中方法执行的SQL语句中的一部分,使得SQL语句错误,观察异常是否能被FIlter捕获并执行回滚:

错误的SQL语句

 此为“登录事务”的第二个调用方法

启动Tomcat,点击登录后,输出如下图:

 证明了事务并未进行提交,而是进行了回滚

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值