1. SqlSession和SqlSessionFactory的接口定义

SqlSession:

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:

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部分源码:

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部分源码:

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部分源码。

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的源码。

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

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

SqlSessionManager.query1();
SqlSessionManager.query2();

以上伪代码,各自分别开启了一个SqlSession,并销毁了各自的SqlSession。即,创建了2次SqlSession,销毁了2次SqlSession。

注:SqlSessionManager似乎是废弃不使用的了,但是,它并不妨碍我们探究其源码

原文:https://my.oschina.net/zudajun/blog/665956

SqlSession与SqlSessionFactory到底是什么关系?的更多相关文章

  1. JS原型的问题Object和Function到底是什么关系

    var F = function(){}; Objcert.prototype.a = function(){}; Function.prototype.b = function(){}; F 既能访 ...

  2. 内核与ramdisk到底是什么关系

    转自:http://www.lupaworld.com/forum.php?mod=viewthread&tid=61425 原名:内核与ramdisk到底是什么关系? 个人Notes:    ...

  3. SOA和微服务到底是什么关系

    本文原创,原文地址为:http://www.cnblogs.com/fengzheng/p/5847441.html SOA和微服务到底是什么关系? 说实话,我确实不明白SOA和微服务到底有什么本质上 ...

  4. SOA和微服务到底是什么关系?

    SOA和微服务到底是什么关系? 说实话,我确实不明白SOA和微服务到底有什么本质上的区别,两者说到底都是对外提供接口的一种架构设计方式.我倒觉得微服务其实就是随着互联网的发展,复杂的平台.业务的出现, ...

  5. Unicode,UTF-32,UTF-16,UTF-8到底是啥关系?

    编码的目的,就是给抽象的字符赋予一个数值,好在计算机里面表示.常见的ASCII使用8bit给字符编码,但是实际只使用了7bit,最高位没有使用,因此,只能表示128个字符:ISO-8859-1(也叫L ...

  6. [转载]javaEE规范和SSH三大框架到底有什么关系

    转载自: http://blog.csdn.net/bingjing12345/article/details/20641891 1994-2000 年是互联网的大航海时代. 请注意,下面的时间点及其 ...

  7. javaEE规范和SSH三大框架到底有什么关系

    转自博客:http://blog.csdn.net/bingjing12345/article/details/20641891 1994-2000 年是互联网的大航海时代. 请注意,下面的时间点及其 ...

  8. ARM内核和架构都是什么意思,它们到底是什么关系?

    ARM产品越来越丰富,命名也越来越多.很多朋友提问: ARM内核和架构都是什么意思?内核和架构的关系是什么?比如ARMv7架构,这个架构指的是什么?小编选出了几个精彩回答!希望对嵌友们在选择设计电路时 ...

  9. Zend与PHP之间到底是什么关系

    Zend与PHP之间是什么关系 What is Zend's relationship with PHP? 每次看到PHP虚拟机中出现zend.zendvar之类的都很困惑,特意查了一下... PHP ...

随机推荐

  1. 几十行js实现很炫的canvas交互特效

    几十行js实现很炫的canvas交互特效 废话不多说,先上效果图! 本篇文章的示例代码都是抄的一个叫Franks的老外在yutube上的一个教学视频,他还出了很多关于canvas的视频,十分值得学习, ...

  2. 【数据结构&算法】08-栈概念&源码

    目录 前言 栈的定义 定义 常见应用 栈的常见应用 进栈出栈变化形式 栈的抽象数据类型 栈的顺序存储结构及实现 栈的顺序存储结构 顺序栈 顺序栈的结构定义 两栈共享空间 栈的链式存储结构及实现 栈的链 ...

  3. IP数据报中如果不分片,分片标志值是什么?

    过了好久才解决这个简单的问题,罪过罪过- 答案:如果IP数据报不分片,分片标志DF(Don't Fragment)会被设置为1.分片标志MF(More Fragment)设置为0. 下面是详细解释: ...

  4. uni-app nvue页面动态修改导航栏按钮

    话不多说上代码 let pages = getCurrentPages() let page = pages[pages.length - 1]; let currentWebview = page. ...

  5. 自动下载MarkDown格式会议论文的程序

    近期师兄发给我一个压缩包让我整理文献,而我发现压缩包里的内容是这样: 这样: 和这样的: 我大概看了一下,可能有270多篇文章是这种格式,俗话说的好,没有困难的工作,只有勇敢的研究僧.所以决定用Pyt ...

  6. 全球首发-基于.NET 6长线支持Zoomla!逐浪CMS v8.6.0正式发布

    传送门: https://www.z01.com/down/3778.shtml 全新Zoomla!逐浪CMS v8.6.0 全于首个基于.net 6长线支持的CMS-Zoomla!逐浪CMS v8. ...

  7. [源码解析] PyTorch 分布式(8) -------- DistributedDataParallel之论文篇

    [源码解析] PyTorch 分布式(8) -------- DistributedDataParallel之论文篇 目录 [源码解析] PyTorch 分布式(8) -------- Distrib ...

  8. 论文解读(LLE)《Nonlinear Dimensionality Reduction by Locally Linear Embedding》and LLE

    论文题目:<Nonlinear Dimensionality Reduction by Locally Linear Embedding > 发表时间:Science  2000 论文地址 ...

  9. Springboot .properties或.yml配置文件读取pom.xml文件值

    有时候配置文件需要读取pom文件配置<properties></properties>中间自定义属性值的时候可以用@@获取 例:@package.parameter@ 然后还需 ...

  10. Android系统编程入门系列之硬件交互——多媒体摄像头

    多媒体系列硬件 多媒体包括图片.动画.音频.视频,这些多媒体素材的采集(输入)主要依靠摄像头和麦克风等硬件设备转化为基础数据,而他们的播放渲染(输出),则需要依靠具有相关功能的编解码软件.当然随着硬件 ...