Mybatis源码分析(二)SqlSession和Excutor

导读

上一篇文章介绍了Mybatis是怎样读取并解析mybatis-config.xml和xxxMapper.xml等xml信息并封装到Configuration对象中。这一篇文章主要介绍Mybatis的主要对象SqlSession和事务是怎样管理和执行的。带着两个疑问:1. 构造好的Configuration对象是在哪里用的?2. SqlSession又是怎样创建的?

一、使用Configuration对象:

回到XMLConfigBuilder的parse方法中,这个方法调用了一个主要方法parseConfiguration()解析完xml配置文件之后,返回一个configuration对象,如下

    public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            // 主要方法,解析xml标签等配置信息放到configuration中
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            //返回configuration对象
            return this.configuration;
        }
    }
build(parser.parse());

这个build方法就是传入一个Configuration对象,然后构建一个DefaultSqlSession对象。

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

到些SqlSession对象就构建出来了

配置文件解析流程

解析配置文件.png

获取到SqlSessionFactory之后 ,就可以创建SqlSession了。

二、构建SqlSession:

从前面的配置文件解析流程我们知道,得到的sqlSessionFactory是DefaultSqlSessionFactory类型

sqlSession = sqlSessionFactory.openSession();

所以上面调用的openSession()方法为DefaultSqlSessionFactory中的方法。

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  //配置文件所有内容
  private final Configuration configuration;
  //创建session
  @Override
  public SqlSession openSession() {
    //调用的是另外一个openSessionFromDataSource方法
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  //其实是调用这个方法
  //protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //对应xml标签<environments> ,这个在配置文件解析的时候就已经存放到configuration中了。
      final Environment environment = configuration.getEnvironment();
      //构建事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //构建一个事务对象  
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //创建一个executor来执行SQL  
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建一个DefaultSqlSession对象并返回
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); 
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  
    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }

创建事务Transation:

 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

TransactionFactory有两个子类,如下:


TransactionFactory类图.png

事务工厂类型可以配置为JDBC类型或者MANAGED类型。

  • JdbcTransactionFactory生产JdbcTransaction。
  • ManagedTransactionFactory生产ManagedTransaction。

如果我们配置的是MANAGED,会把事务交给容器来管理,比如JBOSS,Tomcat。因为我们是本地跑的程序,如果配置成MANAGED就会不有任何事务。
但是,如果是Spring+Mybatis,则没有必要配置,因为我们会直接在applicationContext.xml里配置数据源和事务管理器,从而覆盖Mybatis的配置。

三、创建Executor

// 把事务传给newExecutor()方法创建执行器Executor对象。
configuration.newExecutor(tx, execType)

// 调用configuration的newExecutor方法创建Executor。

final Executor executor = configuration.newExecutor(tx, execType);
//Configuration中
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //第一步
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //第二步
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //第三步
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

创建Excutor的过程分为三步:
第一步:创建执行器
Executor的基本类型有三种:SIMPLE为默认类型。

public enum ExecutorType {
    SIMPLE, REUSE, BATCH
}

Mybatis Excutor类图:

Mybatis Excutor类图.png

提问:为什么要让抽象类BaseExecutor实现Executor接口,然后让具体实现类继承抽象类呢?
这就是模板方法模式的实现。

模板方法模式就是定义一个算法骨架,并允许子类为一个或者多个步骤提供实现。模板方法是得子类可以再不改变算法结构的情况下,重新定义算法的某些步骤。
抽象方法是在子类汇总实现的,每种执行器自己实现自己的逻辑,BaseExecutor最终会调用到具体的子类。

抽象方法:

protected abstract int doUpdate(MappedStatement var1, Object var2) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean var1) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, BoundSql var5) throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4) throws SQLException;

第二步:缓存装饰

if (cacheEnabled) {
  executor = new CachingExecutor(executor);
}

如果cacheEnabled=true,会用装饰器设计模式对Executor进行装饰。
第三步:插件代理

executor = (Executor) interceptorChain.pluginAll(executor);

到此,执行器创建的就搞定了。
Executor创建完毕后,就该创建DefaultSqlSession了,如下:

//创建一个DefaultSqlSession对象并返回
return new DefaultSqlSession(configuration, executor, autoCommit);

进入DefaultSqlSession的构造方法:

public class DefaultSqlSession implements SqlSession {
   private final Configuration configuration;
   private final Executor executor;
   public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
     this.configuration = configuration;
     this.executor = executor;
     this.dirty = false;
     this.autoCommit = autoCommit;
   }
}

DefaultSqlSession中包含两个重要属性:configuration和executor

  • configuration 存放配置信息
  • executor 执行器

到此,SqlSession对象构建完毕。

sqlSession = sqlSessionFactory.openSession();
// 这里的sqlSession其实就是DefaultSqlSession:
// sqlSession = new DefaultSqlSession();

构建SqlSession过程如下图:


构建SqlSession.png

参照文档: mybatis官网-配置腾讯云博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容