六、Mybatis学习实践-Mybatis框架分析

仓库

文章涉及的代码都将统一存放到此仓库

代码地址:Gitee

简介

Mybatis是一款优秀的ORM框架,学习Mybatis功能特性将有助于编写高性能程序,本文将深入分析Mybatis运行原理,了解实现机制。

配置

开发者可以灵活使用Mybatis功能,例如,启用关闭缓存、使用驼峰命名规则、自定义类型转换器等,通过改变配置文件来控制和扩展Mybatis功能。Mybatis所有配置信息都保存在Configuration类中,主要涉及两类信息:

  • Mybatis主配置文件中的信息保存在Configuration中(Mybatis配置信息)
  • Mybatis Mapper XML文件中的信息保存在Configuration中(Mapper 接口对应的SQL语句)

Mybatis主配置文件

使用Mybatis需要提供配置文件用来创建SqlSessionFactory,配置文件包含11个一级元素

<configuration>
  <!-- 属性配置,可以在后续配置中使用,可以理解为定义变量,这些变量可以在后续节点中使用 -->
  <properties>
    <property name="username" value="xxx"/>
  </properties>
  <!-- 设置信息, 用于开启关闭特定功能,例如开启使用二级缓存 -->
  <settings>
    <setting name="cacheEnabled" value="true"/>
  </settings>
  <!-- 设置类型别名,好处是可以使用短标识符代替完整类名信息 -->
  <typeAliases>
    <typeAlias type="com.hzchendou.blog.demo.entity.AuthorDO" alias="author"/>
  </typeAliases>
  <!-- 定义类型转化,指定类型使用特定类型转换器,对输入参数和输出结果进行转换 -->
  <typeHandlers>
    <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.hzchendou.blog.demo.enums.BlogStatusEnums"/>
  </typeHandlers>
  <!-- 对象工程,Mybatis使用对象工厂创建对象,我们自定义对象工厂,对创建的对象进行一些初始化操作,例如设置对象来源数据库等等,这个属性不建议使用,除非业务中有特殊需求,例如加入统计等 -->
  <objectFactory type="org.apache.ibatis.reflection.factory.DefaultObjectFactory"></objectFactory>
  <!-- 对目标对象进行包装,包装后的对象用于属性操作,我们可以使用这个功能自定义一种数据库表字段到实体类属性之间的名称映射规则,例如数据库表中的字段blog_title,我们可以将blog_title映射到BlogDO中的title,不建议启用 -->
  <objectWrapperFactory type="org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory"/>
  <!-- 反射工厂类,默认提供反射Reflector的缓存功能,Reflector用于提供对对象的方式setter和getter操作,不建议使用 -->
  <reflectorFactory type="org.apache.ibatis.reflection.DefaultReflectorFactory"/>
  <!-- 插件配置,可以在扩展点中提供增强功能例如提供分页查询 -->
  <plugins>
    <plugin interceptor="com.hzchendou.blog.demo.interceptor.PaginationInterceptor"/>
  </plugins>
  <!-- 环境配置,包含数据源以及事务,同时可以使用多环境配置,方便开发测试环境进行切换 -->
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!-- 数据库选择器,可以在SQL语句中添加databaseId属性,来选择在特定数据库类型下执行,从而达到SQL适配的效果,也可以在SQL元素内使用_databaseId变量来选择如何组装SQL,对于单数据源类型可以忽略 -->
  <databaseIdProvider type="DB_VENDOR">
    <property name="Microsoft SQL Server" value="ms"/>
    <property name="MySQL" value="mysql"/>       
    <property name="Oracle" value="oracle" />
  </databaseIdProvider>
  <!-- mapper配置 -->
  <mappers>
    <mapper resource="mapper/BlogMapper.xml"/>
    <mapper resource="mapper/AuthorMapper.xml"/>
  </mappers>
</configuration>

上述配置文件中objectFactory、objectWrapperFactory、reflectorFactory、plugins、databaseIdProvider元素都是不常用的配置,剩余的配置都是简单易使用的配置,如果你阅读过源码你就会发现Mybatis提供了多种配置方式,我们以properties元素为例,properties中的属性可以用上文中的property子元素进行定义,也可以通过以下两种方式以资源文件的形式加载

  • <properties resource="xxxx.properties">
  • <properties url="xxxx.properties">

每一个元素的解析过程不在此介绍,解析过程都在XMLConfigBuilder中的parseConfiguration方法中。

SqlSessionFactory

SqlSessionFactory是Mybatis框架中的顶层类,从命名中可以看出是SqlSession的工厂类,主要功能是创建SqlSession,提供了多种创建SqlSession的方法

public interface SqlSessionFactory {
  SqlSession openSession();
  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);
  Configuration getConfiguration();
}

Mybatis中提供了两个实现类

  1. DefaultSqlSessionFactory
  2. SqlSessionManager

Mybatis中默认使用DefaultSqlSessionFactory,调用DefaultSqlSessionFactory.openSessionFromDataSource方法创建SqlSession

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);
      /// 创建执行器,负责执行SQL操作
      final Executor executor = configuration.newExecutor(tx, execType);
      /// 组装创建SqlSession对象
      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();
    }
  }

从上面代码中,我们可以看到三个不同功能的类,TransactionFactory、Executor、DefaultSqlSession(实现了接口SqlSession),下面我们分别来介绍这三个类

TransactionFactory

TransactionFactory负责管理Transaction事务,Mybatis提供了两个事务管理器

  1. JdbcTransactionFactory
  2. ManagedTransactionFactory

下面介绍两个事务管理器有什么不同

  • JdbcTransactionFactory
    • JdbcTransactionFactory创建JdbcTransaction类型事务,
    • JdbcTransaction实现了事务的提交、回滚方法
  • ManagedTransactionFactory
    • ManagedTransactionFactory创建ManagedTransaction类型事务
    • ManagedTransaction没有实现事务的提交、回滚方法

从上面可以看出ManagedTransactionFactory采用托管的方式让容器来实现事务,例如结合SpringBoot使用时,Spring容器管理事务(SpringBoot中使用SpringManagedTransactionFactory事务管理器),JdbcTransactionFactory实现了事务方法,承担管理事务职责。

Transaction

这个类没有什么特别的,就是提供了标准的事务提交和回滚接口

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

Executor

Executor是Mybatis中最重要的类,所有的SQL操作都是交给Executor执行,Mybatis中提供了三种类型执行器,先大致介绍用处,后续将详细分析一下三个类的不同(可以通过Settings中指定defaultExecutorType值来设置使用哪个Executor)

  1. SimpleExecutor,当我们在配置文件中没有指定时,这是默认使用的执行器
  2. ReuseExecutor,这个执行器在SimpleExecutor的基础上会重复利用Statement(JDBC中的SQL执行器)
  3. BatchExecutor,这个执行器会批量提交更新操作从而提高效率

我们先来看一下Executor接口定义

public interface Executor {
  ResultHandler NO_RESULT_HANDLER = null;
   更新操作
  int update(MappedStatement ms, Object parameter) throws SQLException;
  /// 查询操作,使用了缓存
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
   查询操作
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
   查询操作,使用游标处理结果(调用存储过程返回结果可以使用游标处理)
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
  /// 刷新statement,在BatchExecutor中有将提交SQL,在ReuseExecutor中将刷新statement缓存
  List<BatchResult> flushStatements() throws SQLException;
  /// 提交事务
  void commit(boolean required) throws SQLException;
  /// 事务回滚
  void rollback(boolean required) throws SQLException;
  /// 获取缓存key信息
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  /// 判断是否有缓存
  boolean isCached(MappedStatement ms, CacheKey key);
  ///清除本地缓存
  void clearLocalCache();
 ///延迟加载
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  /// 获取事务
  Transaction getTransaction();
  /// 关闭session
  void close(boolean forceRollback);
  /// 是否已关闭
  boolean isClosed();
  /// 设置目标Executor,对目标执行器进行包装,例如CacheExecutor,缓存执行器,用于实现二级缓存功能
  void setExecutorWrapper(Executor executor);
}

Executor接口方法比较多,但是大致可以分为三类操作:

  1. SQL操作
    1. update
    2. query
    3. queryCursor
    4. flushStatements
  2. 数据库事务
    1. commit
    2. rollback
    3. getTransaction
  3. 缓存
    1. createCacheKey
    2. isCached
    3. clearLocalCache
    4. setExecutorWrapper(在Mybatis中的CachingExecutor通过ExecutorWrapper实现缓存功能)

在这里要重点关注SQL操作以及缓存功能

SQL操作

查看SimpletExecutor中的update操作

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    /// 获取StatementHandler操作,用于处理SQL输入参数以及输出参数,以及SQL语句预编译操作
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
     获取Statement,同时执行预编译操作
    stmt = prepareStatement(handler, ms.getStatementLog());
    /// 执行SQL更新操作
    return handler.update(stmt);
  } finally {
   closeStatement(stmt);
  }
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  /// 获取连接
  Connection connection = getConnection(statementLog);
  /// 创建Statement对象并且做一些初始化操作,例如超时时间、最大查询记录数等
  stmt = handler.prepare(connection, transaction.getTimeout());
  /// 执行参数预编译(只有PreparedStatement和CallableStatement才会执行SQL预编译操作)
  handler.parameterize(stmt);
  return stmt;
}

SimpleExecutor使用StatementHandler对象来处理SQL操作

  • 1、创建Statement,初始化statement参数配置(SQL操作超时时间、单次查询最大查询记录数)
  • 2、执行SQL预编译操作
  • 3、执行update更新操作

其它三个类中的操作有所不同,但是逻辑上是相同的,BatchExecutor类中加入了批量处理

/// 缓存的待执行的statement
private final List<Statement> statementList = new ArrayList<>();
/// 缓存的待返回的结果集
private final List<BatchResult> batchResultList = new ArrayList<>();
/// 缓存上一个执行的SQL语句
private String currentSql;
/// 缓存上一个MappedStatement(MappedStatement存储Mapper XML中的SQL元素解析后的数据)
private MappedStatement currentStatement;

 在JDBC 中的PreparedStatement和
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
  final Configuration configuration = ms.getConfiguration();
  final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
  final BoundSql boundSql = handler.getBoundSql();
  final String sql = boundSql.getSql();
  final Statement stmt;
  /// 如果当前待执行的SQL与上一个执行的SQL相同并且当前MappedStatement与上一个也相同
  /// 那么直接使用缓存的最后一个Statement对象执行SQL操作,注意这里只是添加SQL到批处理缓存中
  /// 并不会立即执行,而是在doFlushStatements方法中批量执行缓存的updaye操作
  if (sql.equals(currentSql) && ms.equals(currentStatement)) {
    int last = statementList.size() - 1;
    stmt = statementList.get(last);
    applyTransactionTimeout(stmt);
    handler.parameterize(stmt);// fix Issues 322
    BatchResult batchResult = batchResultList.get(last);
    batchResult.addParameterObject(parameterObject);
  } else {
    /// 如果不是那么创建新的statement进行操作
    Connection connection = getConnection(ms.getStatementLog());
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);    // fix Issues 322
    currentSql = sql;
    currentStatement = ms;
    statementList.add(stmt);
    batchResultList.add(new BatchResult(ms, sql, parameterObject));
  }
  /// 将待执行的SQL操作加入到批处理中
  handler.batch(stmt);
  return BATCH_UPDATE_RETURN_VALUE;
}

BatchExecutor中的doUpdate不会直接执行SQL操作,为了提高效率会将待处理的SQL语句加入到批处理中,然后调用doFlushStatements方法执行批处理操作

ReuseExecutor实现了statement的复用,代码如下:

/// Statement缓存
private final Map<String, Statement> statementMap = new HashMap<>();


@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Configuration configuration = ms.getConfiguration();
  StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
  ///获取Statement
  Statement stmt = prepareStatement(handler, ms.getStatementLog());
  return handler.update(stmt);
}


private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  BoundSql boundSql = handler.getBoundSql();
  String sql = boundSql.getSql();
  ///判断SQL语句是否存在缓存
  if (hasStatementFor(sql)) {
    /// 从缓存中获取Statement
    stmt = getStatement(sql);
    applyTransactionTimeout(stmt);
  } else {
    /// 重新创建Statement
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    putStatement(sql, stmt);
  }
  handler.parameterize(stmt);
  return stmt;
}

private boolean hasStatementFor(String sql) {
  try {
     查询SQL语句对应的 Statement 是否存在,并且没有关闭连接
    Statement statement = statementMap.get(sql);
    return statement != null && !statement.getConnection().isClosed();
  } catch (SQLException e) {
    return false;
  }
}

private Statement getStatement(String s) {
  return statementMap.get(s);
}

ReuseExecutor实现的功能比较简单,就是通过SQL复用Statement。

Executor在执行SQL操作使用了一个重要类StatementHandler,这个在后面将会介绍,现在只要了解这个StatementHandle可以处理输入参数,预编译SQL,同时提供更新和查询SQL操作接口

缓存

Executor中实现了一级和二级缓存

上述三个Executor(SimpleExecutor、BatchExecutor、ReuseExecutor)都继承了BaseExecutor,在这个类中实现了一级缓存。

///本地缓存,在构造函数中初始化,this.localCache = new PerpetualCache("LocalCache");
protected PerpetualCache localCache;


public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  ///当前查询操作标记为需要清除时,在查询前会执行清除操作(设置flushCache为true时)
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List<E> list;
  try {
    queryStack++;
    /// 从缓存中获取结果
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
      /// 缓存存在直接返回
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
      /// 缓存不存在时,会执行SQL查询,并将结果放入缓存中
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    queryStack--;
  }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
     如果设置为STATEMENT,那么执行完Query操作就会清除缓存,默认时SESSION
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}

二级缓存与命名空间绑定,在Mapper XML中设置<cache/>元素开启指定命名空间下的缓存功能,如果开启了缓存功能,那么会在上述三个Executor的基础上包装一层CachingExecutor

/// 目标Executor
private final Executor delegate;

public CachingExecutor(Executor delegate) {
  /// 设置代理类,
  this.delegate = delegate;
  delegate.setExecutorWrapper(this);
}

@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
  /// 获取 Cache,MappedStatement 与Mapper XML中的SQL一一对应,那么这里获取到的缓存就是在Mapper XML中配置的缓存
  Cache cache = ms.getCache();
  /// 如果开启了缓存
  if (cache != null) {
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);
      @SuppressWarnings("unchecked")
      /// 查询缓存中是否存在查询缓存信息
      List<E> list = (List<E>) tcm.getObject(cache, key);
      /// 没有的话会执行SQL,然后放入缓存
      if (list == null) {
        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578 and #116
      }
      /// 如果存在缓存那么执行返回
      return list;
    }
  }
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

Executor中的SQL操作以及缓存功能介绍完了,下面我们对Executor中的StatementHandler进行分析。

StatementHandler

StatementHandler代表JDBC中的Statement类,在Mybatis中有四个实现类:

  1. SimpleStatementHandler,与JDBC中的Statement类型对应,不能执行预编译
  2. PreparedStatementHandler,与JDBC中的PreparedStatement类型对应,可以执行预编译,默认使用该类型处理
  3. CallableStatementHandler,与JDBC中的CallableStatement类型对应,用于处理存储过程调用
  4. RoutingStatementHandler,是一个门面类,实际调用上述1、2、3中的一种类型执行操作
public interface StatementHandler {
  /// 创建 Statement对象
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;
  /// 设置SQL参数
  void parameterize(Statement statement)
      throws SQLException;
  /// 批量操作
  void batch(Statement statement)
      throws SQLException;
  /// 更新操作
  int update(Statement statement)
      throws SQLException;
  /// 查询操作,返回列表
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  /// 查询操作,返回游标
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  /// 获取SQL类,保存SQL参数、sql字符串、sql参数映射信息
  BoundSql getBoundSql();
  /// 获取参数处理器
  ParameterHandler getParameterHandler();
}

SqlSession

与DefaultSqlSessionFactory一样,SqlSession也是Mybatis中的顶层类,通过SqlSession可以完成与SQL相关的所有操作,例如查询数据、更新数据、事务提交、事务回滚等。通过前面Executor介绍,我们知道Executor也可以完成SQL相关操作,那么SqlSession是提供了重复功能吗?答案是没有,在SqlSession中是通过调用Executor方法来完成SQL操作的,也就是说SqlSession是Mybatis对外提供的API类,程序通过SqlSession来操作SQL,而Executor是内部实现,通过SqlSession可以降低程序开发复杂度。

在Mybatis中提供两个SqlSession实现类:

  • DefaultSqlSession,Mybatis默认使用该类创建SqlSession对象
  • SqlSessionManager。SqlSessionManager使用线程变量将SqlSession与线程进行绑定

SqlSession中的查询、更新操作不再讲解,本质都是调用Executor方法来实现,在这里重点讲解一下getMapper方法(在DefaultSqlSession中)

@Override
public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}

调用了Configuration.getMapper方法

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

调用了MapperRegister中的getMapper方法

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

紧接着调用了MapperProxyFactory.newInstance方法

public class MapperProxyFactory<T> {
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  public Class<T> getMapperInterface() {
    return mapperInterface;
  }
  public Map<Method, MapperMethodInvoker> getMethodCache() {
    return methodCache;
  }
  /// 创建接口代理对象
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  ///创建接口代理对象
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

从上面可以看出,当调用代理对象中的接口方法,最终都将调用mapperProxy中的invoke方法(Jdk代理类接口方法)

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    /// 如果是Object中的方法,那么直接调用
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else {
      /// 调用MapperMethodInvoker中的invoke方法
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
  try {
    /// 如果不存在缓存,那么需要创建新的MapperMethodInvoker对象
    return MapUtil.computeIfAbsent(methodCache, method, m -> {
      if (m.isDefault()) {
        try {
          if (privateLookupInMethod == null) {
            return new DefaultMethodInvoker(getMethodHandleJava8(method));
          } else {
            return new DefaultMethodInvoker(getMethodHandleJava9(method));
          }
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException
            | NoSuchMethodException e) {
          throw new RuntimeException(e);
        }
      } else {
         最终调用 PlainMethodInvoker
        return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
    });
  } catch (RuntimeException re) {
    Throwable cause = re.getCause();
    throw cause == null ? re : cause;
  }
}

创建Mapper接口代理对象的调用栈比较深,读者可以沿着方法调用过程多读几遍源码加强记忆,最终MapperProxy类调用MapperMethod中的execute来执行SQL操作

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        /// 调用SqlSession方法来完成SQL操作
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
         /// 省略无关代码
      }
      case DELETE: {
        /// 省略无关代码
      }
      case SELECT:
        /// 省略无关代码
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
     /// 省略无关代码
    return result;
  }

从上面可以看出,最终还是由SqlSession来完成SQL操作,同样SqlSession是调用Executor方法来完成SQL操作,因此最终还是Executor承担了所有SQL操作工作。

总结

Mybatis是一款功能强大的ORM框架,同时提供了灵活的功能配置方式。

  • Configuration封装了Mybatis配置文件中的11中元素配置
  • 通过Configuration配置可以获取SqlSessionFactory(Mybatis默认使用DefaultSqlSessionFactory)
  • 通过SqlSessionFactory可以获取SqlSession(Mybatis默认使用DefaultSqlSession)
  • SqlSession可以执行SQL操作包括管理事务
  • SqlSession底层委托给Executor来执行
  • Executor包含三种类型执行器
    • SimpleExecutor(默认执行器,没有特别功能)
    • BatchExecutor 使用批处理的方式来执行SQL更新操作
    • ReUseExecutor 使用缓存SQL对应的Statement来重复使用Statement
  • Executor中调用Statementhandler来完成Jdbc中的Statement操作
  • Mybatis提供了三种类型StatementHandler,分别对应三种Statement
    • SimpleStatementHandler, 对应要一般Statement,无法执行预编译操作,因此不能执行带占位符的SQL
    • PreparedStatementHandler,对应PreparedStatement,可以执行预编译操作,也是Mybatis默认使用的处理器
    • CallableStatementhandler,对应CallableStatement,用于处理存储过程调用
  • SqlSession还提供了一个重要功能获取Mapper接口代理对象
    • Mybatis使用Jdk中的Proxy代理工具来创建Mapper接口代理对象,使用MapperProxy最为InvocationHandler
    • MapperProxy中最终调用了MapperMethod中的execute方法
    • MapperMethod.execute方法最终调用了SqlSession来执行SQL操作

联系方式

技术更新换代速度很快,我们无法在有限时间掌握全部知识,但我们可以在他人的基础上进行快速学习,学习也是枯燥无味的,加入我们学习牛人经验:

QQ:901856121

点击:加群讨论 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值