六SqlSession 的创建过程以及 Executor 执行 SQL 过程详解

Posted archerLuo罗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了六SqlSession 的创建过程以及 Executor 执行 SQL 过程详解相关的知识,希望对你有一定的参考价值。

  • 源码地址:https://github.com/RononoaZoro/mybatis-book/tree/master 的 mybatis-book ( mybatis-chapter04 和 mybatis-chapter05)
  • 文章内容出自《Mybatis 3 源码深度解析》第五章
  • 自己实现代码地址:https://github.com/RononoaZoro/mybatis-book/tree/master 的 archer-mybatis

1、XPath 方式解析 XML 文件

users.xml

<?xml version="1.0" encoding="UTF-8" ?>
<users>
    <user id = "1">
        <name>张三</name>
        <createTime>2018-06-06 00:00:00</createTime>
        <passward>admin</passward>
        <phone>180000000</phone>
        <nickName>阿毛</nickName>
    </user>
    <user id = "2">
        <name>李四</name>
        <createTime>2018-06-06 00:00:00</createTime>
        <passward>admin</passward>
        <phone>180000001</phone>
        <nickName>明明</nickName>
    </user>
</users>

实体类 UserEntity

package com.blog4java.mybatis.xpath;

import lombok.Data;

import java.util.Date;

@Data
public class UserEntity {
    private Long id;
    private String name;
    private Date createTime;
    private String password;
    private String phone;
    private String nickName;
}

1)、XPath 解析

package com.blog4java.mybatis.xpath;

import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.io.Resources;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class XPathExample {

    @Test
    public void testXPathParser() {
        try {
            // 创建DocumentBuilderFactory实例
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 创建DocumentBuilder实例
            DocumentBuilder builder = factory.newDocumentBuilder();
            InputStream inputSource = Resources.getResourceAsStream("users.xml");
            Document doc = builder.parse(inputSource);
            // 获取XPath实例
            XPath xpath = XPathFactory.newInstance().newXPath();
            // 执行XPath表达式,获取节点信息
            NodeList nodeList = (NodeList)xpath.evaluate("/users/*", doc, XPathConstants.NODESET);
            List<UserEntity> userList = new ArrayList<>();
            for(int i=1; i < nodeList.getLength() + 1; i++) {
                String path = "/users/user["+i+"]";
                String id = (String)xpath.evaluate(path + "/@id", doc, XPathConstants.STRING);
                String name = (String)xpath.evaluate(path + "/name", doc, XPathConstants.STRING);
                String createTime = (String)xpath.evaluate(path + "/createTime", doc, XPathConstants.STRING);
                String passward = (String)xpath.evaluate(path + "/passward", doc, XPathConstants.STRING);
                String phone = (String)xpath.evaluate(path + "/phone", doc, XPathConstants.STRING);
                String nickName = (String)xpath.evaluate(path + "/nickName", doc, XPathConstants.STRING);
                // 调用buildUserEntity()方法,构建UserEntity对象
                UserEntity userEntity = buildUserEntity(id,name, createTime, passward, phone, nickName);
                userList.add(userEntity);
            }
            System.out.println(JSON.toJSONString(userList));
            System.out.println(userList.get(0).getCreateTime());
        } catch (Exception e) {
            throw new BuilderException("Error creating document instance.  Cause: " + e, e);
        }
    }

    private UserEntity buildUserEntity(String id,String name,
                                       String createTime, String passward,
                                       String phone, String nickName)
            throws IllegalAccessException, InvocationTargetException {
        UserEntity userEntity = new UserEntity();
        DateConverter dateConverter = new DateConverter(null);
        dateConverter.setPattern("yyyy-MM-dd HH:mm:ss");
        ConvertUtils.register(dateConverter,Date.class);
        BeanUtils.setProperty(userEntity,"id",id);
        BeanUtils.setProperty(userEntity,"name",name);
        BeanUtils.setProperty(userEntity,"createTime",createTime);
        BeanUtils.setProperty(userEntity,"passward",passward);
        BeanUtils.setProperty(userEntity,"phone",phone);
        BeanUtils.setProperty(userEntity,"nickName",nickName);
        return userEntity;
    }
}

2)、Mybatis XNode 解析

package com.blog4java.mybatis.xpath;

import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.junit.Test;

import java.io.Reader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class XPathParserExample {

    @Test
    public void testXPathParser() throws Exception {
        Reader resource = Resources.getResourceAsReader("users.xml");
        XPathParser parser = new XPathParser(resource);
        // 注册日期转换器
        DateConverter dateConverter = new DateConverter(null);
        dateConverter.setPattern("yyyy-MM-dd HH:mm:ss");
        ConvertUtils.register(dateConverter, Date.class);
        List<UserEntity> userList = new ArrayList<>();
        // 调用evalNodes()方法获取XNode列表
        List<XNode> nodes = parser.evalNodes("/users/*");
        // 对XNode对象进行遍历,获取user相关信息
        for (XNode node : nodes) {
            UserEntity userEntity = new UserEntity();
            Long id = node.getLongAttribute("id");
            BeanUtils.setProperty(userEntity, "id", id);
            List<XNode> childNods = node.getChildren();
            for (XNode childNode : childNods) {
                    BeanUtils.setProperty(userEntity, childNode.getName(),
                            childNode.getStringBody());
            }
            userList.add(userEntity);
        }
        System.out.println(JSON.toJSONString(userList));
    }
}

2、SqlSession 实例创建

package com.blog4java.mybatis.sqlsession;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.Reader;

public class SqlSessionExample {

    @Test
    public void testSqlSession() throws IOException {
        // 获取Mybatis配置文件输入流
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        // 通过SqlSessionFactoryBuilder创建SqlSessionFactory实例
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        // 调用SqlSessionFactory的openSession()方法,创建SqlSession实例
        SqlSession session = sqlSessionFactory.openSession();
    }
}

3、SqlSession 创建过程详解

3.1、初始化 Configuration对象

  • 1)、SqlSessionFactoryBuilder#build 方法中初始化了 Configuration 对象(参考:五、Mybatis 核心对象之 Configuration 对象初始化详解
  • 2)、SqlSession 采用工厂模式创建,所以需要创建 SqlSessionFactory 对象,SqlSessionFactory 只有一个默认实现 DefaultSqlSessionFactory
// 通过SqlSessionFactoryBuilder创建SqlSessionFactory实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
        
       //初始化 Configuration 对象
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
        
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
}

3.2、创建 SqlSession 对象

  • 1)、SqlSession 是 Mybatis 提供的面向用户操作数据库的 API
  • 2)、SqlSession 默认实现是 DefaultSqlSession
// 调用SqlSessionFactory的openSession()方法,创建SqlSession实例
SqlSession session = sqlSessionFactory.openSession();
public class DefaultSqlSessionFactory implements SqlSessionFactory {  private final Configuration configuration;  public DefaultSqlSessionFactory(Configuration configuration) {    this.configuration = configuration;  }  @Override  public SqlSession openSession() {    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);  }      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    Transaction tx = null;    try {      // 获取Mybatis主配置文件配置的环境信息      final Environment environment = configuration.getEnvironment();      // 创建事务管理器工厂      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);      // 创建事务管理器      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);      // 根据Mybatis主配置文件中指定的Executor类型创建对应的Executor实例      final Executor executor = configuration.newExecutor(tx, execType);      // 创建DefaultSqlSession实例      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();    }  }}
  • 3)、Executor 是真正用来执行 SQL 的执行器
  • 4)、默认执行器为 SimpleExecutor
final Executor executor = configuration.newExecutor(tx, execType);
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor executor;    // 根据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);    }    // 如果cacheEnabled属性为ture,这使用CachingExecutor对上面创建的Executor进行装饰    if (cacheEnabled) {      executor = new CachingExecutor(executor);    }    // 执行拦截器链的拦截逻辑    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }

4、Executor 执行 SQL 过程详解

  • 1)、初始化 SqlSession 对象,并拿到 Configuration 对象
  • 2)、通过全路径获取 MappedStatement
package com.blog4java.mybatis.example;import com.alibaba.fastjson.JSON;import com.blog4java.common.DbUtils;import com.blog4java.mybatis.example.entity.UserEntity;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.io.Resources;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.session.*;import org.apache.ibatis.transaction.jdbc.JdbcTransaction;import org.junit.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.sql.SQLException;import java.util.List;public class ExecutorExample {    @Before    public void initData() {        DbUtils.initData();    }    @Test    public void testExecutor() throws IOException, SQLException {        // 获取配置文件输入流        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");        // 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        // 调用openSession()方法创建SqlSession实例        SqlSession sqlSession = sqlSessionFactory.openSession();        Configuration configuration = sqlSession.getConfiguration();        // 从Configuration对象中获取描述SQL配置的MappedStatement对象        MappedStatement listAllUserStmt = configuration.getMappedStatement(                "com.blog4java.mybatis.com.blog4java.mybatis.example.mapper.UserMapper.listAllUser");        //创建ReuseExecutor实例        Executor reuseExecutor = configuration.newExecutor(                new JdbcTransaction(sqlSession.getConnection()),                ExecutorType.REUSE        );        // 调用query()方法执行查询操作        List<UserEntity> userList =  reuseExecutor.query(listAllUserStmt,                null,                RowBounds.DEFAULT,                Executor.NO_RESULT_HANDLER);        System.out.println(JSON.toJSON(userList));    }}
  • 2)创建 Executor 对象
 //创建ReuseExecutor实例Executor reuseExecutor = configuration.newExecutor(    new JdbcTransaction(sqlSession.getConnection()),    ExecutorType.REUSE);
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor executor;    // 根据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);    }    // 如果cacheEnabled属性为ture,这使用CachingExecutor对上面创建的Executor进行装饰    if (cacheEnabled) {      executor = new CachingExecutor(executor);    }    // 执行拦截器链的拦截逻辑    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }
  • 3)、执行获取结果
// 调用query()方法执行查询操作List<UserEntity> userList =  reuseExecutor.query(listAllUserStmt,null,RowBounds.DEFAULT,Executor.NO_RESULT_HANDLER);System.out.println(JSON.toJSON(userList));

BaseExecutor

  @Override  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    // 获取BoundSql对象,BoundSql是对动态SQL解析生成的SQL语句和参数映射信息的封装    BoundSql boundSql = ms.getBoundSql(parameter);    // 创建CacheKey,用于缓存Key    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);    // 调用重载的query()方法    return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } @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;      if (list != null) {        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);      } else {        // 缓存中获取不到,则调用queryFromDatabase()方法从数据库中查询        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);      }    } 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;  }  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);    }   

以上是关于六SqlSession 的创建过程以及 Executor 执行 SQL 过程详解的主要内容,如果未能解决你的问题,请参考以下文章

SqlSessionFactory创建SqlSession的过程

MyBatis 基础知识总结 5SqlSessionFactory和SqlSession

最最常用的 SqlSessionFactory 和 SqlSession,你真的了解吗?

Mybatis学习笔记-增删改的操作 -对SqlSession的优化封装-优化代码

Mybatis源码 - SqlSessionTemplate的介绍及创建过程

MyBatis的执行过程