OpenSessionInViewFilter [全名:org.springframework.orm.hibernate3.support.OpenSessionInViewFilter]是Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个Session,直到这个请求结束,具体是通过一个Filter来实现的。
MyBatis是不是也可以有这样一个类,当Web容器接到请求就为这个请求创建一个SqlSession,在之后的所有操作都由这个SqlSession来执行,当请求完毕时我们来关闭这个SqlSession。我们先来想一个问题我们怎样监控一个请求开始到结束并在请求处理前和处理后都能做些事情,很容易我们就想到了ServletFilter过滤器。OK,过滤器满足我们操作的要求,那么我么怎样才能让这个请求共享SqlSession呢?我们知道当请求到达web容器时就会为他创建一个线程,直到请求完成线程关闭;那我们就可以把这个SqlSession放到当前线程中这样整个线程就都能访问到SqlSession了。
在此之前我们先来了解一下ThreadLocal,它将帮助我们我SqlSession放进线程中并只在当前线程共享。ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。ThreadLocal为我们提供一些方法来操作它,这里我们只看它的get/set方法就可以了。
我们先来写一个MyBatis的工具类,它将帮助我们实例化SqlSessionFactory、创建SqlSession并放到ThreadLocal中、返回SqlSession、关闭SqlSession。
ublic class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal();
static{
try {
InputStream is = Resources.getResourceAsStream("Mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从当前线程共享变量中获取SqlSession
* @return SqlSession
*/
public static SqlSession getSqlSession(){
//判断当前线程中是否有SqlSession
if(threadLocal.get() == null){
//创建一个SqlSession并放到当前线程共享变量中
threadLocal.set(sqlSessionFactory.openSession());
}
//从当前线程共享变量中获取SqlSession
return threadLocal.get();
}
/**
* 关闭当前线程共享变量中SqlSession
*/
public static void closeSqlSession(){
//从当前线程共享变量中获取SqlSession
SqlSession sqlSession = threadLocal.get();
if(sqlSession != null){
//关闭SqlSession
sqlSession.close();
}
//从当前线程共享变量中移除SqlSession
threadLocal.set(null);
}
}
接下来我们将实现请求处理前创建SqlSession并在当前线程共享,在处理结束时关闭SqlSession。
@WebFilter("/*")
public class OpenSessionInViewFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//创建SqlSession并在当前线程中共享
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
filterChain.doFilter(servletRequest, servletResponse);
//提交事务
sqlSession.commit();
} catch (IOException e) {
//回滚事务
sqlSession.rollback();
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
} finally {
//关闭当前线程共享SqlSession
MyBatisUtil.closeSqlSession();
}
}
@Override
public void destroy() {
}
}
我们来看一下整个处理的流程:通过过滤器我们将请求拦截—>然后创建了当前线程可共享的SqlSession—>创建完成后放行请求—>请求处理完成后—>提交/回滚事务,关闭当前线程共享的SqlSession。