自律,是一个人最好的修行
三个SqlSessionDefaultSqlSessionSqlSessionManager1.获取DefaultSqlSession的能力2.解决DefaultSqlSession的不足2.1解决自动关闭问题2.1解决线程安全问题。SqlSessionTemplate1.解决线程安全问题2.解决自动关闭问题总结
三个SqlSession
DefaultSqlSession与SqlSessionManager 与SqlSessionTemplate 是我常见的3种sqlsesion
从类图可以看出他们三个都实现了了SqlSession,也就是他们都可以表示一个会话。与其他不同的是SqlSessionManager实现了SqlSessionFactory
这三种sqlsession的区别是啥?他们的应用场景是啥呢?
这一切我们从DefaultSqlSession开始说起。
DefaultSqlSession
DefaultSqlSession 是SqlSession的默认实现。
当我们单独使用Mybatis时,我们通常使用DefaultSqlSession 来执行SQL,操作数据库。
String resource = "mybatis-config.xml"; // 读取配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); // 构建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); try { User user = sqlSession.selectOne("MyMapper.selectUser", 1); System.out.println(user); } finally { sqlSession.close(); }
但是DefaultSqlSession存在两个不足。
- 我们需要自己手动关闭sqlsesion,我们知道,人总是不可靠的。忘关sqlsession 是有很大概率发生的
- 线程安全问题:DefaultSqlSession是线程不安全的Sqlsession 。也就是说DefaultSqlSession不能是单例,
如何解决这两个问题?
自动关闭Session问题:
- 我可以自己做一个切面,专门处理session关闭问题
- Mybatis为我们提供了升级版的DefaultSqlSession, SqlSessionManager可以解决这个问题
线程安全问题:
- 既然不能共用,很自然的,我们每次使用DefaultSqlSession的时候都从SqlSessionFactory当中获取一个就可以了啊。
- 但是我们仍然想使用单例版的Sqlsession怎么办?别慌,Mybatis为我们提供了升级版的DefaultSqlSession ,SqlSessionManager可以解决这个问题。
SqlSessionManager
个人认为,与其说SqlSessionManager是DefaultSqlSession 的升级版,不如说SqlSessionManager是DefaultSqlSession代理版(或者封装版)
为什么这样说?来看看SqlSessionManager能干吗
1.获取DefaultSqlSession的能力
String resource = "mybatis-config.xml"; // 读取配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionManager sqlSessionManager =SqlSessionManager.newInstance(inputStream); //获取一个SqlSession SqlSession session = sqlSessionManager.openSession(); //SqlSessionManager 类的openSession public SqlSession openSession() { return sqlSessionFactory.openSession(); }
从这个角度看,其实他跟SqlSession 没有什么区别。他只是封装了SqlSession ,具体工作还是交给SqlSession去做的.
sqlSessionManager.openSession() = sqlSessionFactory.openSession()
得到的Sqlsession自然也是DefaultSqlSession
2.解决DefaultSqlSession的不足
2.1解决自动关闭问题
为了解决自动关闭问题,SqlSessionManager使用了代理技术来实现了自动关闭问题。
使用JDK动态代理技术,动态生成代理对象sqlSessionProxy ,并用内部类SqlSessionInterceptor来对SqlSession的执行方法进行增强。
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; //使用JDK代理技术,生成一个代理对象 this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor()); }
我们以insert为例来看看
sqlSessionManager.insert() public int insert(String statement) { return sqlSessionProxy.insert(statement); }
当执行insert()方法时,sqlSessionManager内部是调用sqlsessionProxy代理对象的insert方法。之后执行增强器的SqlSessionInterceptor#invoke方法。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); if (sqlSession != null) {// 当使用线程本地变量 try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else {//不使用线程本地变量。 //从sqlSessionFactory获取一个DefaultSqlSession final SqlSession autoSqlSession = openSession(); try { final Object result = method.invoke(autoSqlSession, args); autoSqlSession.commit();//提交 return result; } catch (Throwable t) { autoSqlSession.rollback();//回滚 throw ExceptionUtil.unwrapThrowable(t); } finally { autoSqlSession.close();//关闭sqlsession } } } }
invoke方法内部,调用openSession() 从sqlSessionFactory中获取一个DefaultSqlSession,执行对应的方法,并在finally
中执行关闭sqlsession
最后的执行时序
sqlSessionManager.insert() 的背后依然是DefaultSqlSession.insert 。并且帮助我们close 了DefaultSqlSession。
开发人员再也不必担心,忘记关闭DefaultSqlSession 了。
在执行sqlSessionManager的sqlsession方法时, 其本质也是每次都创建DefaultSqlSession ,不正是线程安全的吗?
单例是sqlSessionManager ;但真正执行的是DefaultSqlSession