一 提出问题:为什么要优化代码
首先,先看不使用过滤器Filter,直接在方法里面创建SqlSessionFactory工厂
public class StudentService {
public int insStudent(Student student) {
InputStream iStream = null;
int index = 0;
try {
iStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(iStream);
SqlSession session = factory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class); //接口绑定
index = mapper.insStudent(student);
if(index > 0) {
session.commit(); // 提交
session.close();
}else {
session.rollback(); // 插入失败,回滚
session.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return index;
}
}
问题:每创建一个类或者写一个方法,就要重新写创建SqlSession的代码,这样代码就冗余了。而且创建工厂是一个比较消耗性能的过程,我们不能为了制造一支笔,而去创建一个笔厂,所以最好保证有且只有一个factory。
二 分析问题
- 怎么才能简化代码,保证只有一个工厂呢?
- 怎么才能在加载servlet之前就创建好SqlSession对象呢?
- 答案是利用过滤器Filter
三 解决问题
首先,先封装一个MyBatisUtil工具类,方便以后复用
public class MyBatisUtil {
private static SqlSessionFactory factory;
/**
* ThreadLocal线程容器,java的一个类,给线程绑定一个 Object 内容后,只要线程不变,可以随时取出。
* 通过get()方法获取设置的对象,set(Object value)设置对象到线程容器里
*
* SqlSession对象绑定在线程里,这样就保证了过滤器Filter和Servlet里的SqlSession是同一个
*/
private static ThreadLocal<SqlSession> tLocal = new ThreadLocal<>();
static {
InputStream is;
try {
is = Resources.getResourceAsStream("mybatis.xml"); //加载全局配置文件
factory = new SqlSessionFactoryBuilder().build(is); //创建工厂
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession的方法
* @return
*/
public static SqlSession getSession() {
SqlSession session = tLocal.get();
if(session == null) {
tLocal.set(factory.openSession());
}
return tLocal.get();
}
/**
* 关闭SqlSession
*/
public static void closeSession() {
SqlSession session = tLocal.get();
if(session != null) {
session.close();
tLocal.set(null);
}
}
}
创建过滤器SessionFilter类
@WebFilter("/*")
public class SessionFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SqlSession session = MyBatisUtil.getSession(); //获取SqlSession对象
try {
chain.doFilter(request, response);
session.commit(); //提交
} catch (Exception e) {
session.rollback(); //出异常,回滚到初始状态
e.printStackTrace();
}finally {
MyBatisUtil.closeSession(); //关闭
}
}
}
这样只要程序一加载servlet,就必须进过过滤器Filter,从而创建SqlSession对象,只要线程不变,SqlSession对象就不变。最后,上面StudentService类里的方法就可以简化为
public class StudentService {
public int insStudent(Student student) {
SqlSession session = MyBatisUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class); //接口绑定
return mapper.insStudent(student);
}
}
四 总结
通过过滤器Filter把创建SqlSession对象的公共代码提取出来,这样在你的项目中不管创建多少个类,多少个方法,创建SqlSession的代码只要写一遍,既简化了代码,也避免了频繁创建工厂,提高程序的效率。