Mybatis整体结构

Mybatis整体结构

├─annotations ->注解相关 @select @insert
├─binding -> mapper 相关
├─builder -> 解析 xml 相关
├─cache -> 缓存
├─cursor -> 返回结果 resultset
├─datasourcer -> 数据管理
├─exceptionsr -> 异常
├─executorr -> 执行器
├─io -> classloader
├─jdbc -> jdbc
├─lang -> jdk7 jdk8
├─logging -> 日志相关
├─mapping -> mapper 相关的封装
├─parsing -> xml 相关解析
├─plugin -> 拦截器
├─reflection -> 反射相关
├─scripting -> 数据厂家
├─session -> sessiomn
├─transaction -> 事务
└─type -> 返回类型对应

Mybatis核心概念

Configuration 、SqlSessionFactory 、Session 、Executor 、MappedStatement、StatementHandler 、ResultSetHandler

名称意义
Configuration管理 mybatis-config.xml 全局配置关系类
SqlSessionFactorySession 管理工厂接口
SessionSqlSession 是一个面向用户(程序员)的接口。SqlSession 中提供了很多操作数据库的方法
Executor执行器是一个接口(基本执行器、缓存执行器)作用:SqlSession 内部通过执行器操作数据库
MapperStatement底层封装对象 作用:对操作数据库存储封装,包括 sql 语句、输入输出参数
StatementHandler具体操作数据库相关的 handler 接口
ResultSetHandler具体操作数据库返回结果的 handler 接口

Mybatis的使用

       /**
         * 步骤分析:
         *  第一步:读取配置文件(此时读取的是主配置文件SqlMapConfig.xml,它里面指定了映射配置文件位置)
         *  第二步:创建SqlSessionFactory对象(它是一个工厂对象,通过类名就可以得知,它是生产SqlSession)
         *  第三步:使用SqlSession对象,创建一个IUserDao接口的代理实现类。
         *  第四步:使用代理实现类对象,调用findAll方法实现查询所有
         *  第五步:释放资源
         
 mybatis框架主要是围绕着SqlSessionFactory进行的
         (1)、定义一个Configuration对象,其中包含数据源、事务、mapper文件资源以及影响数据库行为属性设置settings
         (2)、通过配置对象,则可以创建一个SqlSessionFactoryBuilder对象
         (3)、通过 SqlSessionFactoryBuilder 获得SqlSessionFactory 的实例。
         (4)、SqlSessionFactory 的实例可以获得操作数据的SqlSession实例,通过这个实例对数据库进行操作
         */
public class MyBatisTest {
    public static void main(String[] args) throws IOException {
        // 读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SQLSessionFactory工厂的构建对象
        /**
         *  定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
         *  为什么使用工厂模式:
         *  1、一个调用者想创建一个对象,只要知道其名称就可以了。
         *  2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
         *  3、屏蔽产品的具体实现,调用者只关心产品的接口。
         *  理解:你需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现
         **/
        // SqlSessionFactoryBuilder:负责构建SqlSessionFactory,并且提供了多个build的重载方法
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        // 创建创建SqlSessionFactory 它是一个创建SqlSession的工厂 是一个接口
        SqlSessionFactory factory = builder.build(in);

        // 使用工厂生产一个SqlSession
        // SqlSession是MyBatis的关键对象,是执行持久化操作的独享,类似于JDBC中的Connection.
        // 它是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操作的关键对象.
        // SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法,它的底层封装了JDBC连接,
        // 可以用SqlSession实例来直接执行被映射的SQL语句.每个线程都应该有它自己的SqlSession实例.
        // SqlSession的实例不能被共享,同时SqlSession也是线程不安全的,
        // 绝对不能将SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中.
        // 也绝不能将SqlSession实例的引用放在任何类型的管理范围中,比如Servlet当中的HttpSession对象中.
        // 使用完SqlSeesion之后关闭Session很重要,应该确保使用finally块来关闭它.
        SqlSession session = factory.openSession();

        // 使用SqlSession创建一个dao接口的代理实现类
        // SqlSession.getMapper()就是绕一大圈去动态代理一个对象。这个对象根据返回类型和参数类型,最后用SqlSession的方法。
        IUserDao proxyUserDao = session.getMapper(IUserDao.class);
        // 使用代理dao调用方法
        List<User> all = proxyUserDao.findAll();
        for (User user : all) {
            System.out.println(user);
        }
        // 释放资源
        session.close();
    }

配置文件SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--引入jdbc配置文件-->
    <properties resource="jdbc.properties"/>
    <!--设置别名 实体类-->
    <typeAliases>
        <!-- 单个
        <typeAlias type="fun.chenqi.ssm.dao.IUserDao"></typeAlias>
        -->
        <package name="fun.chenqi.ssm.domain"></package>
    </typeAliases>

    <!--配置mybatis的环境-->
    <!--配置MySQL的环境-->
    <environments default="mysql">
        <environment id="mysql">

            <!--配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置是否使用连接池,以及使用哪种连接池
               使用:POOLED      不使用:UNPOOLED  以上都是使用mybatis内置的
               非Mybatis内置的:JDNI(
            -->

            <dataSource type="POOLED">
                <!--配置连接数据库的四个要素-->
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定写有sql语句的映射配置文件位置-->
    <mappers>
        <!--  单个映射
        <mapper resource="fun/chenqi/ssm/dao/IUserDao.xml"></mapper>
        <mapper resource="fun/chenqi/ssm/dao/IAccountDao.xml"></mapper>
        -->
        <!--整个包映射-->
          <package name="fun.chenqi.ssm.dao"/>
        <!-- <mapper resource="fun/chenqi/ssm/dao/IUserDao.xml"/> -->
        <!--<mapper class="fun.chenqi.ssm.dao.IUserDao"/>-->
    </mappers>
</configuration>

源码解析 Configuration

InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in); // 第三行

以上代码是读取配置文件,然后通过SqlSessionFactoryBuilder传入配置信息(输入流)创建一个SqlSessionFactory对象。

1、从上面第三行代码跟进去 :

/**
    这块代码是根据传入的配置信息输入流创建一个XMLConfigBuilder对象,这个对象是专门生成  ConfigBuilder对象。
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse()); // 第六行
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

2、上面的代码,先跟进第六行parser.parse():

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration")); // 第六行
  return configuration;
}

3、 再跟进这块代码的第六行parser.evalNode("/configuration"):

public XNode evalNode(Object root, String expression) {
  Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
  if (node == null) {
    return null;
  }
  return new XNode(this, node, variables);
}

这块代码是干嘛的,其实就是读取配置文件SqlMapConfig.xml的根标签</configuration>然后返回一个XNode对象!XNode对象里面存的是配置文件的信息。

读取完了根标签是不是接下来要读取根标签下面的其它标签了,回到 2、处这块代码

parseConfiguration(parser.evalNode("/configuration"));

其实就是读取根标签下面的其它标签!

private void parseConfiguration(XNode root) {
  try {
    //issue #117 read properties first
    propertiesElement(root.evalNode("properties"));
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

4、回到 1、处第六行 return build(parser.parse());

这块代码就是返回一个DefaultSqlSessionFactory对象,

SqlSessionFactory是接口,DefaultSqlSessionFactory是实现类

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

以上就是对源码Configuration的解读


源码解析 SqlSession

// 创建SQLSession对象
SqlSession sqlSession = factory.openSession();

这块代码调用openSession()方法创建了SqlSession对象,

这个方法具体干了哪些事?跟进去看下:

@Override
public SqlSession openSession() {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); // configuration.getDefaultExecutorType() = SIMPLE
}

再次跟进去:

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();
  }
}

这块代码很清楚的告诉我们干了哪些事:

读取配置文件信息,创建事务,创建Executor执行器,这个执行器就是操作数据库,执行sql语句的。

以上就是对SqlSession源码的解读


源码解析 Executor

还是看上面的第十行代码, final Executor executor = configuration.newExecutor(tx, execType);

这行代码传入俩参数事务,Executor的类型,默认是simple

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) { // 默认true 因为一级缓存默认开启
    executor = new CachingExecutor(executor);
  }
  // 责任链模式拦截器
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

Mybatis的Executor执行器两大类,缓存的CachingExecutor和非缓存的BaseExecutor,

非缓存的BaseExcutor是个接口,有三个实现类:

BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理

ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)

缓存的CachingExecutor

先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。

Mybatis二级缓存的配置

<!-- 1.sqlmapconfig.xml -->
<settings>
    <!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
    <setting name="cacheEnabled" value="true" />
</settings>
<!-- 2.xxxMapper.xml -->
<mapper namespace="fun.chenqi.ssm.dao.IUserDao">
<<cache/> 
<!-- 这个标签必须开启,否则还是一级缓存,一级缓存默认开启,就算 <setting name="cacheEnabled" value="true" />这行代码不写也开启一级缓存。-->

一级缓存和二级缓存的区别

一级缓存是会话(Session)级别,同一次会话中执行多次相同的sql查询,除了第一次查数据库,剩下的查询直接查缓存。

二级缓存基于同一个namespace,不同的sqlsession会话也能缓存起来。

分析代码

如果 <setting name="cacheEnabled" value="false" /> 即关闭了缓存 ,最后返回的Executor传啥就是啥

如果 <setting name="cacheEnabled" value="true" /> 即开启缓存,一级缓存默认开启,这句代码可省略,二级缓存需要开启 <<cache/> 。不管是那种执行器,最后返回的一定是可缓存的CachingExecutor执行器。


源码解析 MappedStatement、StatementHandler、ResultSetHandler

在上面Mybatis的使用中,这块代码:

IUserDao proxyUserDao = session.getMapper(IUserDao.class);
List<User> all = proxyUserDao.findAll();

其实是通过动态代理来实现,这块的源码等会在看,先看操作数据库的另外一种实现方式:

 List<User> list = sqlSession.selectList("fun.chenqi.ssm.dao.IUserDao2.userByUsername", "张");

这种方式是使用sqlsession自带的接口执行查询,第一个参数是mapper.xml对应的sql语句,第二个参数是执行sql传入的参数。来跟下这块的代码:

// 这块代码调用了本类的selectList()方法,跟进去
@Override
public <E> List<E> selectList(String statement, Object parameter) {
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

下面的代码MappedStatement登场

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
  // 传入fun.chenqi.ssm.dao.IUserDao2.userByUsername,根据配置configuration获取信息
  // MappedStatement是一个被封装的操作数据库的对象,包含sql语句,输入参数等。
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

接着跟executor.query()

这是个接口有俩实现类,BaseExecutor和CacheExecutor,由前面的代码我们知道先走CacheExecutor缓存执行器:

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
// query():
 @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

如果缓存有数据直接返回,如果没有数据,调用BaseExecutor的query():

@Override
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.");
  }
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List<E> list;
  try {
    queryStack++;
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; // 13行
    if (list != null) {
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); // 17行
    }
  } finally {
    queryStack--;
  }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}

resultHandler登场,它是处理返回结果的

上面的代码第13行和17行重要

13行是查localCache缓存看看有没有数据,有数据返回给resultHandler,没有的话执行17行queryFromDatabase(),查询数据库。

这里有个疑问,刚才不是查过cache缓存了么,现在怎么又多出来一个localCache缓存。

我通过debug得知原来是这样的!

因为我们在sqlmapconfig.xml配置了<setting name="cacheEnabled" value="true" />

这行代码默认就是true可以不写,它表示了每次mybatis的查询会先查二级缓存,二级缓存是跨Sqlsession的,二级缓存没有会查一级缓存localCache!

我们配置了true,所以执行器默认的就是CacheExecutor,它里面会查二级缓存。当我设置为false的时候,执行器变成了SimpleExecutor,根本不走二级缓存的代码,它会查一级缓存,没有的话才会查库。

接着跟第17行代码:

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
  // 执行doQuery()查库
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
  // 清空一级缓存
    localCache.removeObject(key);
  }
  // 更新一级缓存
  localCache.putObject(key, list);
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
  return list;
}

跟doQuery()代码:

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

这里StatementHandler登场了,它是负责操作数据库的

继续跟handler.<E>query(stmt, resultHandler);

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  String sql = boundSql.getSql();
  statement.execute(sql);
  return resultSetHandler.<E>handleResultSets(statement);
}

这里就是操作数据库执行sql语句的代码了,

然后通过resultSetHandler.<E>handleResultSets(statement);处理返回结果:

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

  final List<Object> multipleResults = new ArrayList<Object>();

  int resultSetCount = 0;
  ResultSetWrapper rsw = getFirstResultSet(stmt);

  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  while (rsw != null && resultMapCount > resultSetCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  String[] resultSets = mappedStatement.getResultSets();
  if (resultSets != null) {
    while (rsw != null && resultSetCount < resultSets.length) {
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
      if (parentMapping != null) {
        String nestedResultMapId = parentMapping.getNestedResultMapId();
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
        handleResultSet(rsw, resultMap, null, parentMapping);
      }
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
  }

  return collapseSingleResultList(multipleResults);
}

以上就是mybatis一个完整sql查询执行过程!


再次回到

IUserDao proxyUserDao = session.getMapper(IUserDao.class);
List<User> all = proxyUserDao.findAll();

这里使用了动态代理来实现的,可以在程序运行期间,在不改变源代码的情况下对原代码增强

通过session.getMapper(IUserDao.class);获取要代理对象的信息

一直跟进去会到这里:

@Override
public <T> T getMapper(Class<T> type) {
  return configuration.<T>getMapper(type, this);
}
.......
// 很明显了 这里就是通过反射创建真实对象的代理对象
 protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

List<User> all = proxyUserDao.findAll();

代码执行到这里的时候,会对这块代码增强:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

mapperMethod.execute():

接下来的代码就很熟悉了,通过sqlsession操作数据库了!

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
   Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
      break;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName() 
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

以上就是MyBatis代理的方式执行sql的全过程!


四天时间终于撸完MyBatis核心代码,接下来继续撸Spring源码...

Last modification:October 24th, 2019 at 01:59 pm