时间:2021-07-01 10:21:17 帮助过:9人阅读
我们知道使用JDK动态代理必须实现InvocationHandler接口并且覆写invoke()方法,SqlSessionManager构造器在实例化代理类时SqlSessionInterceptor类就实现了InvocationHandler接口,观察invoke()方法。invoke第一行代码尝试从本地线程中拿到Sqlsession,但如果未调用startManagedSession()方法,那么永远拿到的都是null值,也就是说,invoke每次通过openSession()方法拿到新实例。
1 private class SqlSessionInterceptor implements InvocationHandler { 2 public SqlSessionInterceptor() { 3 // Prevent Synthetic Access 4 } 5 6 @Override 7 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 8 //从本地线程中获取获取SqlSession,使用SqlSessionManager时并不会向本地线程中放置Sqlsession,此时sqlsession实例为null,除非启用了startManagedSession() 9 final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); 10 if (sqlSession != null) { 11 try { 12 return method.invoke(sqlSession, args); 13 } catch (Throwable t) { 14 throw ExceptionUtil.unwrapThrowable(t); 15 } 16 } else { 17 //通过sqlSessionFactory获取SqlSession 18 final SqlSession autoSqlSession = openSession(); 19 try { 20 final Object result = method.invoke(autoSqlSession, args); 21 autoSqlSession.commit(); 22 return result; 23 } catch (Throwable t) { 24 autoSqlSession.rollback(); 25 throw ExceptionUtil.unwrapThrowable(t); 26 } finally { 27 autoSqlSession.close(); 28 } 29 } 30 } 31 }
对比使用了startManagedSession()方法的SqlSessionManager和直接newInstance()的测试有什么不同。首先配置一个SqlMapper,存在一个list方法,查询学生表中的学生id,姓名,年纪三个值。
1 package com.zzz.mybatis.mapper; 2 3 import java.util.List; 4 import java.util.Map; 5 import org.apache.ibatis.annotations.Flush; 6 import org.apache.ibatis.annotations.MapKey; 7 import org.apache.ibatis.annotations.Select; 8 9 public interface SqlMapper { 10 @Select("select id,name,age from student") 11 public List<Map<String, Object>> list(); 12 13 @Flush 14 public void flush(); 15 16 @MapKey(value="id") 17 @Select("select id,name,age from student") 18 public Map<String,Map<String,String>> listByMapkey(); 19 }
1 package com.zzz.mybatis.service; 2 3 import org.apache.ibatis.session.SqlSessionManager; 4 import org.junit.Test; 5 6 public class SqlSessionManagerTest { 7 @Test 8 public void SqlSessionManagerByNewInstance() { 9 SqlSessionManager manager=SqlSessionManager.newInstance(getClass().getResourceAsStream("/mybatis-config.xml")); 10 Object rs=manager.selectOne("com.zzz.mybatis.mapper.SqlMapper.list"); 11 System.out.println(rs.toString()); 12 } 13 14 @Test 15 public void SqlSessionManagerByLocalThread() { 16 SqlSessionManager manager=SqlSessionManager.newInstance(getClass().getResourceAsStream("/mybatis-config.xml")); 17 manager.startManagedSession(); 18 Object rs=manager.selectOne("com.zzz.mybatis.mapper.SqlMapper.list"); 19 System.out.println(rs.toString()); 20 rs=manager.selectOne("com.zzz.mybatis.mapper.SqlMapper.list"); 21 System.out.println(rs.toString()); 22 } 23 24 }
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e853265] ==> Preparing: select id,name,age from student ==> Parameters: <== Columns: id, name, age <== Row: 1, zhangsan, 22 <== Total: 1 Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@57e1b0c] ==> Preparing: select id,name,age from student ==> Parameters: <== Columns: id, name, age <== Row: 1, zhangsan, 22 <== Total: 1
两者的区别和联系
两者的查询方式都是通过JDK的动态代理实现,只不过在sqlSessionProxy内部一个使用了本地线程获取SqlSession实例,一个使用openSession()方法获取SqlSession实例。使用debug观察使用了非manager.startManagedSession();所使用的SqlSession对象,可以看到localSqlSession中取到的值为NULL,SqlSession必须通过openSessiom()方法获取实例。
如果启用了startManagedSession()方法,则会从localSqlSession中获取SqlSession实例,可以看到两次的id号都是71,也就是两次查询都是同一个实例。
Mybatis源码分析:SqlSessionManager
标签:variable 关闭 并且 tar llb 直接 ora 一点 res