当前位置:Gxlcms > 数据库问题 > Mybatis源码分析:SqlSessionManager

Mybatis源码分析:SqlSessionManager

时间:2021-07-01 10:21:17 帮助过:9人阅读

private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { 2 this.sqlSessionFactory = sqlSessionFactory; 3 this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( 4 SqlSessionFactory.class.getClassLoader(), 5 new Class[]{SqlSession.class}, 6 new SqlSessionInterceptor()); 7 } 8 //通过InputStream进行实例化 9 public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) { 10 return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties)); 11 } 12 //通过SqlSessionFactory 进行实例化 13 public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) { 14 return new SqlSessionManager(sqlSessionFactory); 15 } 16 //通过Reader进行实例化 17 public static SqlSessionManager newInstance(Reader reader, String environment) { 18 return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null));

 

   我们知道使用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,姓名,年纪三个值。

  • SqlMapper接口
 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()生成SqlSession实例

 技术图片

技术图片

如果启用了startManagedSession()方法,则会从localSqlSession中获取SqlSession实例,可以看到两次的id号都是71,也就是两次查询都是同一个实例。

  • 使用startManagedSession()生成SqlSession实例

技术图片

技术图片

Mybatis源码分析:SqlSessionManager

标签:variable   关闭   并且   tar   llb   直接   ora   一点   res   

人气教程排行