SqlSession与SqlSessionFactory到底是什么关系?
1. SqlSession和SqlSessionFactory的接口定义
SqlSession:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public interface SqlSession extends Closeable { <T> T selectOne(String var1); <T> T selectOne(String var1, Object var2); <E> List<E> selectList(String var1); <E> List<E> selectList(String var1, Object var2); <E> List<E> selectList(String var1, Object var2, RowBounds var3); <K, V> Map<K, V> selectMap(String var1, String var2); <K, V> Map<K, V> selectMap(String var1, Object var2, String var3); <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4); void select(String var1, Object var2, ResultHandler var3); void select(String var1, ResultHandler var2); void select(String var1, Object var2, RowBounds var3, ResultHandler var4); int insert(String var1); int insert(String var1, Object var2); int update(String var1); int update(String var1, Object var2); int delete(String var1); int delete(String var1, Object var2); void commit(); void commit( boolean var1); void rollback(); void rollback( boolean var1); List<BatchResult> flushStatements(); void close(); void clearCache(); Configuration getConfiguration(); <T> T getMapper(Class<T> var1); Connection getConnection(); } |
SqlSession,数据库的C、R、U、D及事务处理接口,你懂的。
SqlSessionFactory:
1 2 3 4 5 6 7 8 9 10 11 | public interface SqlSessionFactory { SqlSession openSession(); SqlSession openSession( boolean var1); SqlSession openSession(Connection var1); SqlSession openSession(TransactionIsolationLevel var1); SqlSession openSession(ExecutorType var1); SqlSession openSession(ExecutorType var1, boolean var2); SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2); SqlSession openSession(ExecutorType var1, Connection var2); Configuration getConfiguration(); } |
不解释,你懂的。
2. SqlSession和SqlSessionFactory的类结构图
(Made In Intellij Idea IDE)
SqlSession实现类:DefaultSqlSession和SqlSessionManager
SqlSessionFactory实现类:DefaultSqlSessionFactory和SqlSessionManager
3. DefaultSqlSession和DefaultSqlSessionFactory源码分析
org.apache.ibatis.session.defaults.DefaultSqlSession.java部分源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | private Configuration configuration; private Executor executor; @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException( "Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } @Override public int update(String statement, Object parameter) { try { dirty = true ; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException( "Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } |
总结:似乎一切的一切,都是从配置对象Configuration中取出材料来,委托给执行器Executor去处理。
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.java部分源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this .configuration = configuration; } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null ; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException( "Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } //... |
创建一个DefaultSqlSession并返回,这里出现了那个贯穿Mybatis执行流程的Executor接口,非常重要的接口,后续会对其进行仔细分析。
4. SqlSessionManager源码分析(重点)
SqlSessionManager同时实现了SqlSession和SqlSessionFactory接口。
org.apache.ibatis.session.SqlSessionManager.java部分源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | public class SqlSessionManager implements SqlSessionFactory, SqlSession { private final SqlSessionFactory sqlSessionFactory; // proxy private final SqlSession sqlSessionProxy; // 保持线程局部变量SqlSession的地方 private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>(); private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { this .sqlSessionFactory = sqlSessionFactory; // 这个proxy是重点 this .sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( SqlSessionFactory. class .getClassLoader(), new Class[]{SqlSession. class }, new SqlSessionInterceptor()); } public static SqlSessionManager newInstance(Reader reader) { return new SqlSessionManager( new SqlSessionFactoryBuilder().build(reader, null , null )); } public static SqlSessionManager newInstance(Reader reader, String environment) { return new SqlSessionManager( new SqlSessionFactoryBuilder().build(reader, environment, null )); } //... // 设置线程局部变量sqlSession的方法 public void startManagedSession() { this .localSqlSession.set(openSession()); } public void startManagedSession( boolean autoCommit) { this .localSqlSession.set(openSession(autoCommit)); } //... @Override public <T> T selectOne(String statement, Object parameter) { return sqlSessionProxy.<T> selectOne(statement, parameter); } @Override public <K, V> Map<K, V> selectMap(String statement, String mapKey) { return sqlSessionProxy.<K, V> selectMap(statement, mapKey); } //... |
变量sqlSessionFactory:相当于DefaultSqlSessionFactory的实例(不是proxy)。
变量sqlSessionProxy:是JDK动态代理出来的proxy(是proxy)。
动态代理的目的,是为了通过拦截器InvocationHandler,增强目标target的方法调用。
target:DefaultSqlSession的实例。
所有的调用sqlSessionProxy代理对象的C、R、U、D及事务方法,都将经过SqlSessionInterceptor拦截器,并最终由目标对象target实际完成数据库操作。
org.apache.ibatis.session.SqlSessionInterceptor.java的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | private class SqlSessionInterceptor implements InvocationHandler { public SqlSessionInterceptor() { // Prevent Synthetic Access } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final SqlSession sqlSession = SqlSessionManager. this .localSqlSession.get(); if (sqlSession != null ) { try { // 1、存在线程局部变量sqlSession(不提交、不回滚、不关闭,可在线程生命周期内,自定义sqlSession的提交、回滚、关闭时机,达到复用sqlSession的效果) return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else { // 2、不存在线程局部变量sqlSession,创建一个自动提交、回滚、关闭的SqlSession(提交、回滚、关闭,将sqlSession的生命周期完全限定在方法内部) 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的生命周期,必须严格限制在方法内部或者request范围(也称之为Thread范围),线程不安全,线程之间不能共享。(官方文档有明确说明)
1、request范围使用SqlSession
1 2 3 4 5 6 7 8 9 10 11 12 | sqlSessionManager.startManagedSession(); try { sqlSessionManager.query1(); sqlSessionManager.query2(); sqlSessionManager.update1(); sqlSessionManager.update2(); //... } catch (Throwable t) { sqlSessionManager.rollback(); } finally { sqlSessionManager.close(); } |
一次性执行了一系列的方法业务,最后统一异常回滚,统一关闭sqlSession,全程创建1次sqlSession,销毁1次sqlSession。只是个例子,具体如何使用线程本地变量sqlSession,完全取决于你自己。
2、method范围使用SqlSession
1 2 | SqlSessionManager.query1(); SqlSessionManager.query2(); |
以上伪代码,各自分别开启了一个SqlSession,并销毁了各自的SqlSession。即,创建了2次SqlSession,销毁了2次SqlSession。
注:SqlSessionManager似乎是废弃不使用的了,但是,它并不妨碍我们探究其源码
原文:https://my.oschina.net/zudajun/blog/665956
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了