mybatis系列----启动流程

mybatis系列–1–启动流程

1 前言

常用的一些框架的源码基本上都已经研究过了,现在就剩mybatis了。那么首先我们得知道mybatis有什么用,他帮我们做了什么?

2 原生jdbc

面试中常问的一道题目:jdbc的流程是什么?

一般来说分为如下几步:

  • 加载驱动
  • 获取数据库连接
  • 获取PreparedStatement对象
  • 执行sql
  • 处理结果集
  • 关闭结果集,statement,数据库连接

具体的代码如下:

Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
    Class.forName("org.postgresql.Driver");
    connection = DriverManager.getConnection("jdbc:postgresql://192.168.1.7:5432/postgres", "postgres", "123456");
    preparedStatement = connection.prepareStatement("select * from cas_base.user where id=?");
    preparedStatement.setString(1, "1");
    resultSet = preparedStatement.executeQuery();
    while (resultSet.next()) {
        String id = resultSet.getString("id");
        String username = resultSet.getString("username");
        System.out.println(id);
        System.out.println(username);
    }
} catch (SQLException throwables) {
    throwables.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} finally {
    if (resultSet != null) {
        try {
            resultSet.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    if (preparedStatement != null) {
        try {
            preparedStatement.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

这一块代码就不用注释了,大家应该都玩过。现在就有一个问题,每一个查询都要写一遍这个代码,而真正游泳的代码就只有sql和结果集解析逻辑,其他的代码对于别的查询来说,都是一样的,那么能不能把这一部分逻辑抽取出来,让开发者只需要关注sql和结果集解析呢?mybatis就是用来做这个事情的。

3 mybatis的简单用法

入门可以直接去看mybatis官网

mybatis提供了两种配置方式:xmljava代码

这里先演示xml方式的配置

mybatis-config.xmlmybatis配置文件

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="org.postgresql.Driver"/>
                <property name="url" value="jdbc:postgresql://192.168.1.7:5432/postgres"/>
                <property name="username" value="postgres"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="org/example/dao/UserMapper.xml"/>
    </mappers>
</configuration>

UserDao:

public interface UserDao {

    User findUserById(String id);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.dao.UserDao">
    <select id="findUserById" resultType="org.example.domain.User">
        select * from cas_base.user where id = #{id}
    </select>
</mapper>

User:

public class User {
    private String id;
    private String username;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

启动代码

String resource = "org/example/mybatis-config.xml";
InputStream inputStream = null;
try {
    // 加载配置文件
    inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
    e.printStackTrace();
}
// 解析配置文件,并构建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sql会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.findUserById("1");
System.out.println(user.getId()+":"+user.getUsername());

4 启动流程

我们就上面那段代码,看看mybatis具体是如何连接数据库,查询并处理结果的

4.1 SqlSessionFactoryBuilder().build(inputStream)

public SqlSessionFactory build(InputStream inputStream) {
    // 调用重载的build方法
    return build(inputStream, null, null);
}

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        // 创建了一个配置文件解析器,解析配置文件
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        // 通过Configuration对象构造一个DefaultSqlSessionFactory对象
        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.
        }
    }
}

我们看下这个这个配置文件解析器XMLConfigBuilder的创建过程

// 避免重复解析
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    // 使用自己私有构造方法,构造对象
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    // 创建了一个空的Configuration对象
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    // 这个是mybatis的环境对象,对应月mybatis配置文件中的environment标签中的配置
    this.environment = environment;
    // 这个XPathParser是真正用来解析配置文件的
    this.parser = parser;
}

Configuration对象是在XMLConfigBuilder父类BaseBuilder中定义的

这个创建完成了之后,就开始parser.parse()解析配置文件了

public Configuration parse() {
    // 重复解析,抛异常
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 使用XPathParser解析器解析配置文件中的configuration节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

XPathParser解析器解析配置文件中的configuration节点,得到该节点所有信息对象XNode,具体如何解析的这里不看。

private void parseConfiguration(XNode root) {
  try {
    // issue #117 read properties first
    // 解析下面规定的节点,把解析到的内容封装到configuration对象中
    propertiesElement(root.evalNode("properties"));
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    loadCustomLogImpl(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
    // 解析environments节点
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    //解析mapper文件(这里非常重要)
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

到这里,mybatis就将xml文件中的所有配置解析到Configuration对象和Environment对象中了。接下来就是build()方法构建SqlSessionFactory对象了

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

public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
}

总结一下:这里无非就是解析配置文件和mapper文件,然后创建SqlSessionFactory对象

重点看如何解析mapper文件

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            // 解析mappers标签下的package标签
            if ("package".equals(child.getName())) {
                String mapperPackage = child.getStringAttribute("name");
                configuration.addMappers(mapperPackage);
            } else {
                // 对应mapper节点可配的三种属性,我上面例子用的是resource
                String resource = child.getStringAttribute("resource");
                String url = child.getStringAttribute("url");
                String mapperClass = child.getStringAttribute("class");
                if (resource != null && url == null && mapperClass == null) {
                    ErrorContext.instance().resource(resource);
                    // 加载mapper资源
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    // mapper 解析器,专门用来解析mapper
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                    // 解析mapper
                    mapperParser.parse();
                } else if (resource == null && url != null && mapperClass == null) {
                    ErrorContext.instance().resource(url);
                    InputStream inputStream = Resources.getUrlAsStream(url);
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                    mapperParser.parse();
                } else if (resource == null && url == null && mapperClass != null) {
                    Class<?> mapperInterface = Resources.classForName(mapperClass);
                    configuration.addMapper(mapperInterface);
                } else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }
}

我们常用的一般都是<mappers> <mapper resource="org/example/dao/UserMapper.xml"/> </mappers>这种配置,指定mapper文件的位置,所以我们重点看看mybatis是如何解析mapper文件,并生成代理对象的

public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
         configuration, resource, sqlFragments);
}

可以发现,和解析配置文件一样,解析mapper文件也是用的XPathParser

public void parse() {
    // 判断是否已经加载过该资源,不会重复加载
    // 其实里面就是一个set集合,加载过的,会记录到这个set中
    if (!configuration.isResourceLoaded(resource)) {
        // 解析mapper文件的mapper节点,4.1.1
        configurationElement(parser.evalNode("/mapper"));
        // 记录到已加载资源的集合中
        configuration.addLoadedResource(resource);
         // 创建代理类生成工厂,解析接口上的注解,4.1.2
        bindMapperForNamespace();
    }

    // 和启动主流程无关,先忽略
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

4.1.1 configurationElement(parser.evalNode("/mapper"))

// 构造方法new的对象
private final MapperBuilderAssistant builderAssistant;

private void configurationElement(XNode context) {
    try {
        // 拿到命名空间
        String namespace = context.getStringAttribute("namespace");
        // 从这里也可以看出来,命名空间必填
        if (namespace == null || namespace.isEmpty()) {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
        // 保存
        builderAssistant.setCurrentNamespace(namespace);
        // 解析mapper下面的各种节点
        cacheRefElement(context.evalNode("cache-ref"));
        cacheElement(context.evalNode("cache"));
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        sqlElement(context.evalNodes("/mapper/sql"));
        // 解析增删改查节点
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
}

重点看解析增删改查节点

private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
        buildStatementFromContext(list, configuration.getDatabaseId());
    }
    // 取出节点中属性和sql,封装为MappedStatement对象
    buildStatementFromContext(list, null);
}


private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
        // 又来一个解析器,专门用来解析增删改查节点的
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            // 解析
            statementParser.parseStatementNode();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        }
    }
}

解析增删改查节点

public void parseStatementNode() {
    // 下面这些属性,使用过select标签的应该熟悉吧
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        return;
    }

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);

    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
        keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
        keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
                                                   configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
            ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
        resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");

    // 将解析到配置封装为一个MappedStatement,并保存到configuration中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
                                        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
                                        resultSetTypeEnum, flushCache, useCache, resultOrdered,
                                        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

将解析到配置封装为一个MappedStatement,并保存到configuration

public MappedStatement addMappedStatement(
    String id,
    SqlSource sqlSource,
    StatementType statementType,
    SqlCommandType sqlCommandType,
    Integer fetchSize,
    Integer timeout,
    String parameterMap,
    Class<?> parameterType,
    String resultMap,
    Class<?> resultType,
    ResultSetType resultSetType,
    boolean flushCache,
    boolean useCache,
    boolean resultOrdered,
    KeyGenerator keyGenerator,
    String keyProperty,
    String keyColumn,
    String databaseId,
    LanguageDriver lang,
    String resultSets) {

    if (unresolvedCacheRef) {
        throw new IncompleteElementException("Cache-ref not yet resolved");
    }

    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    // 构造者模式,构造一个MappedStatement对象
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
        statementBuilder.parameterMap(statementParameterMap);
    }

    MappedStatement statement = statementBuilder.build();
    // 将解析好的MappedStatement对象保存到configuration中
    configuration.addMappedStatement(statement);
    return statement;
}

我们可以发现在configuration对象中有这么一个map集合,它用来存储id(方法名)->MappedStatement的映射关系

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
    .conflictMessageProducer((savedValue, targetValue) ->
                             ". please check " + savedValue.getResource() + " and " + targetValue.getResource());


public void addMappedStatement(MappedStatement ms) {
    // map集合,key是方法名,value是MappedStatement
    mappedStatements.put(ms.getId(), ms);
}

我们没必要关注StrictMap是个什么map,只要是map那么它存储数据的方式就一定是key->value

4.1.2 bindMapperForNamespace()

private void bindMapperForNamespace() {
    // 把4.1.1中解析到namespace取出来
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
        Class<?> boundType = null;
        try {
            // 根据命名空间反射加载类
            boundType = Resources.classForName(namespace);
        } catch (ClassNotFoundException e) {
            // ignore, bound type is not required
        }
        if (boundType != null && !configuration.hasMapper(boundType)) {
            // Spring may not know the real resource name so we set a flag
            // to prevent loading again this resource from the mapper interface
            // look at MapperAnnotationBuilder#loadXmlResource
            // 再次将资源名记录到已加载资源的集合中,为spring考虑
            configuration.addLoadedResource("namespace:" + namespace);
            // 核心,创建dao接口的代理
            configuration.addMapper(boundType);
        }
    }
}

根据命名空间反射加载接口,所以这里namespace一定要写对,如果加载到了,就开始创建接口对应的代理类了

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
}

这里又涉及到了一个MapperRegistry,它是一个mapper注册中心,用来维护dao接口和其代理类生成工厂的映射关系

// 核心map,维护了dao接口和其代理类生成工厂的关系
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

public <T> void addMapper(Class<T> type) {
    // dao必须是接口
    if (type.isInterface()) {
        // 判断接口是否已创建过代理类生成工厂
        if (hasMapper(type)) {
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
        }
        boolean loadCompleted = false;
        try {
            // 创建代理类生成工厂
            knownMappers.put(type, new MapperProxyFactory<>(type));
            // It's important that the type is added before the parser is run
            // otherwise the binding may automatically be attempted by the
            // mapper parser. If the type is already known, it won't try.
            // 解析接口上的注解,从这里可以看到,注解的优先级大于mapper文件
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
        } finally {
            if (!loadCompleted) {
                knownMappers.remove(type);
            }
        }
    }
}

public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
}

创建接口对应代理类生成工厂,然后解析接口上注解,覆盖mapper文件上的配置。

4.2 sqlSessionFactory.openSession()

在这里插入图片描述

我们可以看到,这个接口中总的来说,就两个方法

  • openSession():获取SqlSession会话对象
  • getConfiguration():获取Configuration配置对象

很明显SqlSessionFactory主要用途就是获取SqlSeesion对象,那么它是如何构建这个会话对象的呢?

接着往下看。

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

executorType是一个枚举,Configuration中默认的executorTypeSIMPLE

public enum ExecutorType {
    SIMPLE, REUSE, BATCH
}

这三个的用途后面再说,先看默认的。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        // mybatis环境
        final Environment environment = configuration.getEnvironment();
        // 根据环境配置获取一个事务工厂
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        // 创建事务管理对象,ManagedTransaction
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // Executor 执行器,mybatis的4大组件之一,执行查询
        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();
    }
}

下面我们先来粗略的看看上面涉及到的几个组件

4.2.1 TransactionFactory

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    // 环境中没有,就创建
    if (environment == null || environment.getTransactionFactory() == null) {
        return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
}

在这里插入图片描述

事务工厂,主要用途就是用来创建一次数据库连接的事务对象,mybatis默认为ManagedTransactionFactory

4.2.2 Transaction

@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    // Silently ignores autocommit and isolation level, as managed transactions are entirely
    // controlled by an external manager.  It's silently ignored so that
    // code remains portable between managed and unmanaged configurations.
    return new ManagedTransaction(ds, level, closeConnection);
}

在这里插入图片描述

这些方法应该很容易理解,获取连接,提交,回滚,关闭,获取连接超时时间

它有两个实现类ManagedTransactionManagedTransaction

在这里插入图片描述

4.2.3 Executor

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

在这里插入图片描述

从上面的方法可以发现,这个Executor就是用来负责与数据库交互的,封装了jdbc的增删改查,它的实现类就比较多了。

在这里插入图片描述

粗略一看,是不是有熟悉的,SimpleExecutorReuseExecutorBatchExecutor,是不是与上面说的ExecutorType一一对应,这里我们只看SimpleExecutor

4.2.4 SqlSession

private final Configuration configuration;
private final Executor executor;

private final boolean autoCommit;
private boolean dirty;

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
}

在这里插入图片描述

这个SqlSession里面也是定义了增删改查和事务相关方法,这不是和Executor重复了吗,其实,这个SqlSession是门面模式的应用,提供给用户友好的操作,将内部实现逻辑屏蔽了,真正与数据库交互的还是Executor

它也有两个实现类DefaultSqlSessionSqlSessionManager

在这里插入图片描述

4.2.5 总结

这一步我们看到,只是创建了Transaction对象和Executor对象,并使用这两个对象构建一个SqlSession会话对象。

4.3 sqlSession.getMapper(UserDao.class)

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

使用configuration类的getMapper()方法来获取对应接口的代理类对象

@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 我们在4.1.1中解析了mapper文件,所以如果type是我们在mapper的namespace中配置的,
    //那么这里是肯定能拿到该接口对应代理类生成工厂的
    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);
    }
}

工厂生成代理类

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    // 生成代理对象
    return newInstance(mapperProxy);
}


protected T newInstance(MapperProxy<T> mapperProxy) {
    // jdk动态代理生成代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

mybatis使用jdk动态代理生成代理对象,了解jdk动态代理的人就应该知道,代理类的控制逻辑就在InvocationHandler中,而这里mybatis用的是MapperProxy,点进去一看,继承了InvocationHandler,好家伙,那我们直接关注invoke(Object proxy, Method method, Object[] args)方法即可

// 缓存
private final Map<Method, MapperMethodInvoker> methodCache;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 基础方法,直接反射执行MapperProxy的方法
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
}

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
        // A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
        // It should be removed once the fix is backported to Java 8 or
        // MyBatis drops Java 8 support. See gh-1929
        // 先拿缓存
        MapperMethodInvoker invoker = methodCache.get(method);
        if (invoker != null) {
            return invoker;
        }

        return methodCache.computeIfAbsent(method, m -> {
            // java8开始有的接口的默认方法
            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 {
                // 普通方法走这里
                return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
            }
        });
    } catch (RuntimeException re) {
        Throwable cause = re.getCause();
        throw cause == null ? re : cause;
    }
}

invoker翻译为调用者,普通方法对应的调用者是PlainMethodInvoker,这里是策略模式应用,默认方法对应的调用者是DefaultMethodInvoker。而这样做的好处就是逻辑分开,避免一堆的if-else。但是MapperMethod又是什么呢?

4.3.1 new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())

// 方法名->sql类型的映射关系
private final SqlCommand command;
// 封装了法返回值信息
private final MethodSignature method;

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    // 解析得到方法对应sql类型
    this.command = new SqlCommand(config, mapperInterface, method);
    // 解析方法返回值类型
    this.method = new MethodSignature(config, mapperInterface, method);
}

SqlCommandMapperMethod的静态内部类

// 方法名
private final String name;
// 方法对应sql类型。增删改查的哪一种
private final SqlCommandType type;

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
    final String methodName = method.getName();
    // 声明方法的类
    final Class<?> declaringClass = method.getDeclaringClass();
    // 获取到该方法对应的MappedStateMent对象
    MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
                                                configuration);
    if (ms == null) {
        if (method.getAnnotation(Flush.class) != null) {
            name = null;
            type = SqlCommandType.FLUSH;
        } else {
            throw new BindingException("Invalid bound statement (not found): "
                                       + mapperInterface.getName() + "." + methodName);
        }
    } else {
        // 方法名
        name = ms.getId();
        // 增删改查,解析mapper文件增删改查节点得到的
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
            throw new BindingException("Unknown execution method for: " + name);
        }
    }
}


private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
                                               Class<?> declaringClass, Configuration configuration) {
    String statementId = mapperInterface.getName() + "." + methodName;
    if (configuration.hasStatement(statementId)) {
        return configuration.getMappedStatement(statementId);
        // 这个判断是什么意思?就是说如果找到了声明方法所在的接口,还没有找到对应的
        // MappedStatement,那肯定就不存在对应方法的MappedStatement了
    } else if (mapperInterface.equals(declaringClass)) {
        return null;
    }
    // 递归解析接口的接口
    for (Class<?> superInterface : mapperInterface.getInterfaces()) {
        if (declaringClass.isAssignableFrom(superInterface)) {
            MappedStatement ms = resolveMappedStatement(superInterface, methodName,
                                                        declaringClass, configuration);
            if (ms != null) {
                return ms;
            }
        }
    }
    return null;
}

本质就是为了得到方法对应的sql是增删改查的哪一种

MethodSignature也是MapperMethod的静态内部类

private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final boolean returnsOptional;
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;

public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
    // 方法返回值类型
    Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
    if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
        // 基础类型。4类8种
    } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
    } else {
        // 其他类型,比如void
        this.returnType = method.getReturnType();
    }
    this.returnsVoid = void.class.equals(this.returnType);
    // 判断是否是集合或数组
    this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
    this.returnsCursor = Cursor.class.equals(this.returnType);
    // 判断是否是Optional容器
    this.returnsOptional = Optional.class.equals(this.returnType);
    this.mapKey = getMapKey(method);
    this.returnsMap = this.mapKey != null;
    this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
    this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
    this.paramNameResolver = new ParamNameResolver(configuration, method);
}

本质就是解析方法返回值

4.3.2 cachedInvoker(method).invoke(proxy, method, args, sqlSession)

我们已经知道,cachedInvoker(method)最终返回的是PlainMethodInvoker

private static class PlainMethodInvoker implements MapperMethodInvoker {
  private final MapperMethod mapperMethod;

  public PlainMethodInvoker(MapperMethod mapperMethod) {
    super();
    this.mapperMethod = mapperMethod;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      // 执行MapperMethod的execute方法完成数据库交互
    return mapperMethod.execute(sqlSession, args);
  }
}

最终是通过MapperMethodexecute(sqlSession, args)方法执行增删改查的

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 增删改查+FLUSH
    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:
            // 返回值是void
            if (method.returnsVoid() && method.hasResultHandler()) {
                executeWithResultHandler(sqlSession, args);
                result = null;
                // 返回多条数据
            } else if (method.returnsMany()) {
                result = executeForMany(sqlSession, args);
                // 返回map
            } else if (method.returnsMap()) {
                result = executeForMap(sqlSession, args);
                // 返回游标
            } else if (method.returnsCursor()) {
                result = executeForCursor(sqlSession, args);
                // 单条数据,或者返回容器Optional
            } else {
                Object param = method.convertArgsToSqlCommandParam(args);
                 // 最终使用sqlSession的selectOne()方法完成查询
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional()
                    && (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
             // 最终使用sqlSession的flushStatements()方法完成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;
}

这里我们只看select返回多条数据这一种

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    // 方法入参
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
        RowBounds rowBounds = method.extractRowBounds(args);
        // 最终使用sqlSession的selectList()方法完成查询
        result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
         // 最终使用sqlSession的selectList()方法完成查询
        result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
        if (method.getReturnType().isArray()) {
            return convertToArray(result);
        } else {
            return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
        }
    }
    return result;
}

综上:我们完全可以得出结论,代理类中最终使用sqlSession中方法完成增删改查。

所以我们接下来的工作就变成了sqlSession是如何完成增删改查的

5 sqlSession完成增删改查

@Override
public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    // 单条也是先按找茬多条逻辑来的
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
        return list.get(0);
    } else if (list.size() > 1) {
        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
        return null;
    }
}


@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 拿到方法名对应的MappedStatement
        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();
    }
}

使用执行器执行查询,并解析结果

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 获取sql,里面封装了sql相关信息,以及sql对应的入参,见5.1
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 缓存,mybatis的一级缓存
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 执行查询,并解析结果
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

5.1 ms.getBoundSql(parameterObject)

获取sql,里面封装了sql相关信息,以及sql对应的入参

// 解析mapper文件后,会将sql和解析好的占位符信息封装到这个对象中
private SqlSource sqlSource;

public BoundSql getBoundSql(Object parameterObject) {
    // sql,解析好的占位符信息,占位符对应的参数值
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    // 占位符和参数的映射关系
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
        boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    // 合并嵌套的结果集
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
        String rmId = pm.getResultMapId();
        if (rmId != null) {
            ResultMap rm = configuration.getResultMap(rmId);
            if (rm != null) {
                hasNestedResultMaps |= rm.hasNestedResultMaps();
            }
        }
    }

    return boundSql;
}

重点说下这个sqlSource

public interface SqlSource {

    BoundSql getBoundSql(Object parameterObject);

}

接口,且只有一个方法(用途是把占位符参数传进去构建一个BoundSql)

在这里插入图片描述

它的实现类一共有上述4种,RawSqlSource只是对其他三个的代理,它内部持有一个SqlSource

private final SqlSource sqlSource;

@Override
public BoundSql getBoundSql(Object parameterObject) {
    return sqlSource.getBoundSql(parameterObject);
}

StaticSqlSource为例

// sql信息
private final String sql;
// 映射关系
private final List<ParameterMapping> parameterMappings;
// 配置对象
private final Configuration configuration;

@Override
public BoundSql getBoundSql(Object parameterObject) {
    // 这里是把参数值也放进去了,重新构建了一个BoundSql对象,包含了sql执行的所有的信息
    return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}

5.2 query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)

// SimpleExecutor
private final Executor delegate;

@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, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            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);
}

这里使用的是CachingExecutor,很明显的装饰者模式,在原来的功能基础上加上缓存的功能,真正执行jdbc的是SimpleExecutor

protected int queryStack;
private boolean closed;
protected PerpetualCache localCache;


@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 {
            // 查库
            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 {
        // 查库
        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;
}


@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    // 这个就是jdbc里面的Statement
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        // 获取操作StatementHandler的处理器,5.2.1
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 获取statement对象,5.2.2
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 执行查询并解析结果集,5.2.3
        return handler.query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
}

这里和原生的jdbc流程差不多

5.2.1 configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql)

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 封装了Statement执行sql所需要的信息(这是一个外壳,真正干活的是它内部的StatementHandler)
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 又一次执行了插件
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}

封装Statement执行sql所需要的信息,并第二次执行插件

看看StatementHandler这个接口

在这里插入图片描述

实现类

在这里插入图片描述

干活的其实就最下面三个

PreparedStatementHandler:创建PrepareStatement对象

SimpleStatementHandler:创建Statement对象

CallableStatementHandler:执行存储过程的

那这个RoutingStatementHandler是干嘛的呢,我们来看下它的构造方法

// 干活的
private final StatementHandler delegate;

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
   // 根据配置创建不同的StatementHandler
    switch (ms.getStatementType()) {
        case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

}

简单来说就是将if-else抽到单独一个类中管理嘛

5.2.2 prepareStatement(handler, ms.getStatementLog())

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取连接
    Connection connection = getConnection(statementLog);
    // 获取Statement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 将statement对象保存到参数处理器中
    handler.parameterize(stmt);
    return stmt;
}


protected Connection getConnection(Log statementLog) throws SQLException {
    // 通过事务来获取连接
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
        return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
        return connection;
    }
}

mybatis是通过事务对象获取数据库连接的,那么,提交事务和回滚事务也必然是通过事务对象来的

接下来我们再看看StatementHandler是如何获取jdbcStatement对象的

// 实际干活的
private final StatementHandler delegate;

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  return delegate.prepare(connection, transactionTimeout);
}

PreparedStatementHandler

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
        // 获取Statement对象
        statement = instantiateStatement(connection);
        // 设置sql执行超时时间(底层使用stmt.setQueryTimeout(queryTimeout))
        setStatementTimeout(statement, transactionTimeout);
        // 设置要向数据库获取的行数(底层使用stmt.setFetchSize(defaultFetchSize))
        setFetchSize(statement);
        return statement;
        // 异常需要关闭statement
    } catch (SQLException e) {
        closeStatement(statement);
        throw e;
    } catch (Exception e) {
        closeStatement(statement);
        throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
}

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
        String[] keyColumnNames = mappedStatement.getKeyColumns();
        if (keyColumnNames == null) {
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
        } else {
            return connection.prepareStatement(sql, keyColumnNames);
        }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
        return connection.prepareStatement(sql);
    } else {
        return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
}

底层使用原生jdbcconnection.prepareStatement(sql)来获取Statement对象

还有最后一点,handler.parameterize(stmt)这个是干嘛的

@Override
public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
}
// 参数处理器,处理预编译sql占位符对应的参数
protected final ParameterHandler parameterHandler;
@Override
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
}

Statement对象保存到参数处理器中

@Override
public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 拿到占位符和参数的映射关系
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                // 拿到类型转换器
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    // 通过类型转换器,转换参数值类型,然后设置到Statement对象中
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException | SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

看看mybatis自定义的类型转化器

在这里插入图片描述

有兴趣的可以了解下

总结:这个方法会获取连接,获取PrePareStatement对象,设置sql,设置占位符值。

原生的jdbc流程走到这里,就剩最后三步了,执行sql,解析结果集,关闭连接

5.2.3 handler.query(stmt, resultHandler)

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
}
// 结果集处理器 
protected final ResultSetHandler resultSetHandler;
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 原生jdbc执行sql的操作
    ps.execute();
    // 处理结果集
    return resultSetHandler.handleResultSets(ps);
}

原生jdbc执行sql的操作很简单,重点是后面结果集解析

DefaultResultSetHandler:默认的结果集解析器(也是唯一的)

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

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

    int resultSetCount = 0;
    // 获取结果集包装
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 结果集映射
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    // 顺序获取ResultMap
    while (rsw != null && resultMapCount > resultSetCount) {
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 根据ResultMap上配置的规则解析结果集
        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);
}


private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    // 原生jdbc获取结果集
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
        // move forward to get the first resultset in case the driver
        // doesn't return the resultset as the first result (HSQLDB 2.1)
        if (stmt.getMoreResults()) {
            rs = stmt.getResultSet();
        } else {
            if (stmt.getUpdateCount() == -1) {
                // no more results. Must be no resultset
                break;
            }
        }
    }
    // 包装结果集
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}

根据ResultMap上配置的规则解析结果集

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
        if (parentMapping != null) {
            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
        } else {
            // 结果处理器
            if (resultHandler == null) {
                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                // 处理结果
                handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                // 多结果集的先不看
                multipleResults.add(defaultResultHandler.getResultList());
            } else {
                handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
        }
    } finally {
        // issue #228 (close resultsets)
        closeResultSet(rsw.getResultSet());
    }
}


//
// HANDLE ROWS FOR SIMPLE RESULTMAP
//
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    // 嵌套的结果,比如collection或者association
    if (resultMap.hasNestedResultMaps()) {
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        // 无嵌套的简单结果
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
    throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
        // 真正解析结果集,得到接口方法返回值
        Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
        // 储藏结果,一级缓存
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
}



private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 创建结果对象,此时对象中的属性值还是空的 5.2.3.1
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        // 获取结果对象的元数据信息
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        if (shouldApplyAutomaticMappings(resultMap, false)) {
            // 使用自动映射,给结果对象属性赋值 5.2.3.2
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
        }
        // 使用resultMap中定义的映射关系 5.2.3.3
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    // 最终结果
    return rowValue;
}

执行sql,并解析最终的结果集

5.2.3.1 createResultObject(rsw, resultMap, lazyLoader, columnPrefix)
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    this.useConstructorMappings = false; // reset previous mapping result
    final List<Class<?>> constructorArgTypes = new ArrayList<>();
    final List<Object> constructorArgs = new ArrayList<>();
    // 创建结果对象
    Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
            // issue gcode #109 && issue #149
            if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
                break;
            }
        }
    }
    // 是否使用有参构造方法实例化的对象
    this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
    return resultObject;
}


private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
    throws SQLException {
    final Class<?> resultType = resultMap.getType();
    // 获取类的元数据信息,实际上就是类的方法,属性,构造器等等
    final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
    final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
    if (hasTypeHandlerForResultObject(rsw, resultType)) {
        return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
    } else if (!constructorMappings.isEmpty()) {
        return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
        // 返回值类型是接口或者包含默认构造方法
    } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
        // 使用objectFactory反射创建对象(使用默认构造方法实例化)
        return objectFactory.create(resultType);
    } else if (shouldApplyAutomaticMappings(resultMap, false)) {
        return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
    }
    throw new ExecutorException("Do not know how to create an instance of " + resultType);
}

很明显,此处创建结果对象,但并没有向对象中的属性值赋值

5.2.3.2 applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix)
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    // 自动映射关系
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (!autoMapping.isEmpty()) {
        for (UnMappedColumnAutoMapping mapping : autoMapping) {
            // 从结果集中取出该属性对应值
            final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
            if (value != null) {
                foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
                // gcode issue #377, call setter on nulls (value is not 'found')
                // 使用setter方法给对应属性赋值
                metaObject.setValue(mapping.property, value);
            }
        }
    }
    return foundValues;
}


private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    final String mapKey = resultMap.getId() + ":" + columnPrefix;
    List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
    if (autoMapping == null) {
        autoMapping = new ArrayList<>();
        final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
        for (String columnName : unmappedColumnNames) {
            String propertyName = columnName;
            if (columnPrefix != null && !columnPrefix.isEmpty()) {
                // When columnPrefix is specified,
                // ignore columns without the prefix.
                if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
                    propertyName = columnName.substring(columnPrefix.length());
                } else {
                    continue;
                }
            }
            final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
            if (property != null && metaObject.hasSetter(property)) {
                if (resultMap.getMappedProperties().contains(property)) {
                    continue;
                }
                final Class<?> propertyType = metaObject.getSetterType(property);
                if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
                    final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
                    autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
                } else {
                    configuration.getAutoMappingUnknownColumnBehavior()
                        .doAction(mappedStatement, columnName, property, propertyType);
                }
            } else {
                configuration.getAutoMappingUnknownColumnBehavior()
                    .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
            }
        }
        autoMappingsCache.put(mapKey, autoMapping);
    }
    return autoMapping;
}

本质上就是反射解析对象属性,拿到属性名,然后把属性名对应的属性从结果集中取出来,最后使用setter方法设置结果对象中

5.2.3.3 applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix)
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
    // 获取xml中配置的映射属性
    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
        String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
        if (propertyMapping.getNestedResultMapId() != null) {
            // the user added a column attribute to a nested result map, ignore it
            column = null;
        }
        if (propertyMapping.isCompositeResult()
            || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
            || propertyMapping.getResultSet() != null) {
            Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
            // issue #541 make property optional
            final String property = propertyMapping.getProperty();
            if (property == null) {
                continue;
            } else if (value == DEFERRED) {
                foundValues = true;
                continue;
            }
            if (value != null) {
                foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
                // gcode issue #377, call setter on nulls (value is not 'found')
                // settter方法设置属性值
                metaObject.setValue(property, value);
            }
        }
    }
    return foundValues;
}

使用xml中配置的映射属性,再次从结果集中取值,设置到结果对象中

6 总结

4大组件

  • Executor:执行器
  • MappedStatement:封装了xml上配置的sql信息
  • StatementHandle:获取连接,设置sql,设置参数
  • ResultHandler:执行sql,处理结果集,关闭连接
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot和MyBatis-Plus是两个流行的Java开发框架。Spring Boot是一个用于简化和加速Java应用程序开发的框架,而MyBatis-Plus是MyBatis的增强版,提供了更多的特性和功能。 使用Spring Boot2和MyBatis-Plus的组合,能够使开发过程更加高效和简便。Spring Boot2已经集成了很多常用的配置和功能,例如自动配置、快速启动器和监控等。这些功能使得开发者可以更加专注于业务逻辑而不用过多关注繁琐的配置。 而MyBatis-Plus提供了一系列强大且易于使用的功能,例如代码生成器、分页查询、逻辑删除和性能优化等。代码生成器能够帮助开发者快速生成数据库表对应的实体类、Mapper接口以及XML映射文件,大大提高了开发效率。分页查询和逻辑删除功能使得数据操作更加方便和灵活。另外,MyBatis-Plus对SQL语句进行了优化,能够提升数据库查询的性能。 使用Spring Boot2和MyBatis-Plus的组合还能够更好地兼容其他常用的开发工具和框架,例如Spring Security和Swagger等。Spring Security提供了安全认证和授权的功能,能够保护应用程序的安全性。Swagger可以生成API文档,并提供了一个用户友好的UI界面,方便开发者进行接口测试和调试。 总之,Spring Boot2和MyBatis-Plus的组合能够提高开发效率、简化配置、优化性能并提供额外的功能。对于Java开发者来说,这是一个非常不错且受欢迎的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值