Loading

mybatis-advanced

mybatis源码分析

mybatis整体架构设计

image-20240309092001726

组件之间的调用关系

image-20240309093725447

源码环境搭建

获取源码工程

获取mybatis3.5.7源码工程:https://github.com/mybatis/mybatis-3/tree/mybatis-3.5.7

image-20240309094552368

下载之后存放到自定义文件夹中

导入源码工程

在idea中导入源码工程

image-20240309101958281

image-20240309102031850

导入完成之后可以发现mybatis很多的组件

image-20240309102127187

到此,源码工程导入成功

搭建测试环境

将test目录下的resources文件夹进行标记

先进行标记,否则就会找不到这个文件夹下面的资源

image-20240309102400121

编写mybatis核心配置文件

image-20240309102418945

sqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/><!--事务管理使用的是JDBC-->
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="mapper/UserMapper.xml"></mapper>
  </mappers>
</configuration>

编写mapper映射文件

image-20240309102520701

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">
<!--namespace绑定一个Dao/Mapper接口-->
<mapper namespace="com.jjh.dao.UserDao">
  <select id="selectUser" parameterType="int" resultType="com.jjh.pojo.User">
    select * from user where id=#{id}
  </select>
</mapper>

编写实体类

image-20240309102621267

User

package com.jjh.pojo;

public class User {

  private int id;
  private String name;
  private String pwd;

  public User() {
  }

  public User(int id, String name, String pwd) {
    this.id = id;
    this.name = name;
    this.pwd = pwd;
  }

  public int getId() {
    return id;
  }

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

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getPwd() {
    return pwd;
  }

  public void setPwd(String pwd) {
    this.pwd = pwd;
  }

  @Override
  public String toString() {
    return "User{" +
      "id=" + id +
      ", name='" + name + '\'' +
      ", pwd='" + pwd + '\'' +
      '}';
  }
}

编写测试类

image-20240309102719359

MybatisTest

public class MybatisTest {

  @Test
  public void test1() throws IOException {

    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");

    SqlSessionFactory sqlSessionFactory = 
    new SqlSessionFactoryBuilder().build(inputStream);

    SqlSession sqlSession = sqlSessionFactory.openSession();

    User user = sqlSession.selectOne("com.jjh.dao.UserDao.selectUser", 1);

    System.out.println(user);

    sqlSession.close();
  }
}

测试运行

image-20240309102830848

测试环境搭建成功,接下来就可以进行源码分析

源码分析-初始化

配置文件加载

// 加载配置文件
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");

进入源码查看

// resource就是配置文件的路径
public static InputStream getResourceAsStream(String resource) throws IOException {
  // 调用了另一个静态方法getResourceAsStream
  return getResourceAsStream(null, resource);
}

查看getResourceAsStream

// loader此时为null,resource就是配置文件的路径
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
  // classLoaderWrapper就是加载器的包装器
  InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
  // 对读取到的流进行判断  
  if (in == null) {
    throw new IOException("Could not find resource " + resource);
  }
  return in;
}

查看classLoaderWrapper

// 就是通过无参构造创建classLoaderWrapper对象
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();

// 查看无参构造,发现ClassLoaderWrapper里面有多个加载器
public class ClassLoaderWrapper {
  ClassLoader defaultClassLoader;
  ClassLoader systemClassLoader;
  // 无参构造,获取系统类加载器  
  ClassLoaderWrapper() {
    try {
      systemClassLoader = ClassLoader.getSystemClassLoader();
    } catch (SecurityException ignored) {
      // AccessControlException on Google App Engine
    }   

查看classLoaderWrapper.getResourceAsStream(resource, loader)

// classLoader为null,resource配置文件路径
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
  return getResourceAsStream(resource, getClassLoaders(classLoader));
}

// getClassLoaders(classLoader),返回存放多个加载器的数组
// classLoader为null
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
  // 得到了多个加载器  
  return new ClassLoader[]{
      // null
      classLoader,
      // null
      defaultClassLoader,
      // 通过当前线程获取到的加载器
      Thread.currentThread().getContextClassLoader(),
      // 不为null,当前类使用的加载器
      getClass().getClassLoader(),
      // 系统类加载器,在创建ClassLoaderWrapper对象的时候就已经获得了
      systemClassLoader};
}

// 查看getResourceAsStream(resource, getClassLoaders(classLoader))
// resource配置文件路径,classLoader多个加载器的数组
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
  // 遍历classLoader数组
  for (ClassLoader cl : classLoader) {
    // 选出第一个不为null的加载器
    if (null != cl) {
      // 加载配置文件
      InputStream returnValue = cl.getResourceAsStream(resource);
      // 重试机制,如果加载的配置文件是null,在路径前加上"/"
      if (null == returnValue) {
        returnValue = cl.getResourceAsStream("/" + resource);
      }
      // 读取到流,返回这个字节输入流
      if (null != returnValue) {
        return returnValue;
      }
    }
  }
  return null;
}

到这里,配置文件加载完成

// 通过类加载器对配置文件进行加载,加载为输入流,存到内存中,并没有被解析
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");

文件解析

// 通过无参创建SqlSessionFactoryBuilder对象,这个无参只是单纯的创建了对象,没有任何附加操作
// build方法
// 1.解析了配置文件和映射文件,将解析后的数据封装到Configuration对象中
// 2.创建了DefaultSqlSessionFactory工厂对象,这个对象中存放着全局配置类Configuration对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

配置文件解析

build(inputStream)方法

// inputStream配置文件字节流
public SqlSessionFactory build(InputStream inputStream) {
  return build(inputStream, null, null);
}

// build(inputStream, null, null)
// environment指定环境,对应的是核心配置文件中environment标签,此时为null
// properties也为null
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 1.创建XpathParser解析器对象,将输入流解析成为document对象
    // 2.创建全局的配置对象Configuration对象
    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.
    }
  }

// new XMLConfigBuilder(inputStream, environment, properties)    
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    // 调用了另外一个构造方法
    // XPathParser基于Java Xpath解析器,解析配置文件
    // new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())
    // 创建XPathParser对象,里面的document属性就是被解析的输入流的文档对象
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), 	   	  	  environment, props);
}

// new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    // 解析inputStream流成为一个document文档对象
    this.document = createDocument(new InputSource(inputStream));
  }

//this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), 	   	  	environment, props);
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  // 创建Configuration对象,并通过TypeAliasRegistry注册一些Mybatis内部相关类的别名
  super(new Configuration());
  ErrorContext.instance().resource("SQL Mapper Configuration");
  this.configuration.setVariables(props);
  this.parsed = false;
  this.environment = environment;
  this.parser = parser;
}    

到这里为止

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 1.创建XpathParser解析器对象,将输入流解析成为document对象
    // 2.创建全局的配置对象Configuration对象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // 通过parser.parse()方法将XMLConfigBuilder对象里面的XpathParser的document文档对象进行解析
    // 将解析之后的值封装到configuration中
    // build(parser.parse())返回DefaultSqlSessionFactory对象,该对象拥有ConfigBuilder对象
    // ConfigBuilder对象装配配置文件的信息
    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.
    }
  }

映射文件解析

通过上面的步骤我们知道,document文档对象会被解析,数据被Configuration配置类封装,所以映射文件的解析就发生在这个过程当中

XMLConfigBuilder.parseConfiguration

private void parseConfiguration(XNode root) {
  try {
    // issue #117 read properties first
    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
    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);
  }
}

mapperElement方法

// parent就表示配置文件中的mappers标签的内容
private void mapperElement(XNode parent) throws Exception {
	
if (resource != null && url == null && mapperClass == null) {
  ErrorContext.instance().resource(resource);
  try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
  	// 创建了XMLMapperBuilder对象,专门用来解析mapper映射文件
    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, 	   	 resource, configuration.getSqlFragments());
    // 通过parse方法对映射文件进行解析
    mapperParser.parse();
  }
}
// 解析映射配置文件
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    // parser.evalNode("/mapper")返回的是映射文件的XNode对象  
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}
//configurationElement(parser.evalNode("/mapper"));
private void configurationElement(XNode context) {
  try {
    String namespace = context.getStringAttribute("namespace");
    // 映射文件中的namespace不能为null
    if (namespace == null || namespace.isEmpty()) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    // builderAssistant就是构建MappedStatement对象的构建助手
    builderAssistant.setCurrentNamespace(namespace);
    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);
  }
    
//context.evalNodes("select|insert|update|delete")
public List<XNode> evalNodes(String expression) {
  // 将解析的标签封装为一个个的XNode对象,放在List集合中  
  return xpathParser.evalNodes(node, expression);
} 
    
// buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
// list就是封装的一个个增删改查标签的XNode节点列表    
private void buildStatementFromContext(List<XNode> list) {
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  // 真正的进行解析
  buildStatementFromContext(list, null);
}   
    
 // 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);
    }
  }  

查看statementParser.parseStatementNode()方法

public void parseStatementNode() {
  // 获取statement的id属性,特别关键的值  
  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,解析Sql,封装sql语句(未进行参数绑定)和入参信息
  // 1.替换占位符,保存#{}里面的内容 2.拼接动态sql
  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");
  // 通过builderAssistant构建者助手,创建MappedStatement对象
  // 并将创建的MappedStatement对象放在Map<String, MappedStatement> mappedStatementsmap集合中
  // map集合的key就是namespace.标签id,值就是这个标签对应的MappedStatement对象
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 1.创建XpathParser解析器对象,将输入流解析成为document对象
    // 2.创建全局的配置对象Configuration对象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // 通过parser.parse()方法
    //  1.配置文件解析    
    // 	  1.将XMLConfigBuilder对象里面的XpathParser的document文档对象进行解析
    //    2.将解析之后的值封装到configuration中
    //  2.将映射文件进行解析
    //    1.每个增删改查标签会对应创建一个MappedStatement对象
    //    2.将每个MappedStatement对象放到mappedStatements集合中,集合的key就是namespace.标签id
    //    3.mappedStatements就是configuration的属性,所以也就是说映射文件的信息也在configuration中
    // build(parser.parse())返回DefaultSqlSessionFactory对象,该对象拥有ConfigBuilder对象
    // ConfigBuilder对象装配配置文件的信息
    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.
    }
  }

SqlSource对象创建流程

因为映射文件中的sql需要进行解析,包括占位符的替换,#{}里面内容的保存,和动态sql的分析。这些操作的实现就是通过SqlSource完成的

XMLStatementBuilder.parseStatementNode()方法进行解析

// 占位符号的替换,动态sql解析
// 这个方法都干了什么
//  1.将#{}替换为?,保存了#{}里面的内容,放到parameterMappings中
//  2.保存了参数类型的class对象
// 最终将解析之后的数据就放在了sqlSource中,使用时通过getBoundSql方法拿到sql等信息
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
  XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
  // 解析sql  
  return builder.parseScriptNode();
}
// 解析sql
public SqlSource parseScriptNode() {
  // parseDynamicTags(context)的作用:
  //  1.将带有${}的sql信息,封装到TextSqlNode中
  //  2.将带有#{}的sql信息,封装到StaticTextSqlNode中
  //  3.将动态sql标签中的sql信息分别封装到不同的SqlNode中
  //  4.将所有的节点信息都封装到contents的list集合中
  // rootSqlNode对象就包含了contents list对象
  MixedSqlNode rootSqlNode = parseDynamicTags(context);
  SqlSource sqlSource;
  // 如果这个sql标签中包含${}或者是动态sql,isDynamic就为true
  if (isDynamic) {
    sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
  // 如果是普通sql或者是#{},isDynamic就为false  
  } else {
    sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
  }
  return sqlSource;
}

// 我们来分析普通sql或者是带有#{}的sql的解析过程
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?>      	   parameterType) {
  // 调用另外一个构造方法
  this(configuration, getSql(configuration, rootSqlNode), parameterType);
}

public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
  SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
  Class<?> clazz = parameterType == null ? Object.class : parameterType;
  // 解析sql
  sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}

// 解析sql
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
  ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
  GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
  String sql;
  if (configuration.isShrinkWhitespacesInSql()) {
    // 解析  
    sql = parser.parse(removeExtraWhitespaces(originalSql));
  } else {
    // 解析
    sql = parser.parse(originalSql);
  }
  // handler.getParameterMappings()将#{}里面的内容放到了StaticSqlSource的parameterMappings中
  return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

// GenericTokenParser.parse方法
builder.append(handler.handleToken(expression.toString()));

// SqlSourceBuilder.handleToken方法
@Override
public String handleToken(String content) {
  parameterMappings.add(buildParameterMapping(content));
  // #{}替换成为?
  return "?";
}

openSession方法

// 1.创建了事务对象
// 2.创建了执行器对象
// 3.创建了DefaultSqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
@Override
public SqlSession openSession() {
  // 调用openSessionFromDataSource,参数1:执行器类型 参数2:事务的隔离级别 参数3:事务是否自动提交
  // 补充:执行器都有哪几种
  //   1.BaseExecutor:基础执行器,是一个抽象类,封装了公共方法和公共变量的类  
  //     1.SimpleExecutor:简单执行器,执行一次sql就会创建一个statement对象
  //     2.BatchExecutot:批量执行器,在进行批量执行的时候效果更好
  //     3.CloseExecutor:无用执行器
  //     4.ReUseExecutor:重用执行器,有一个map集合,会把statement对象存到集合中,statement重用
  //   2.CachingExecutor:缓存执行器,装饰器模式。只要mybatis当前是允许缓存的,就会装饰其他执行器  
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
// configuration.getDefaultExecutorType()
public ExecutorType getDefaultExecutorType() {
  // 返回的是默认的一个枚举类型 ExecutorType.SIMPLE(简单的执行器)
  return defaultExecutorType;
}

// 查看默认的执行器类型是什么
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

// 执行器类型,有三种
public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    // 创建了事务工厂
    final TransactionFactory transactionFactory = 				    	      	   	     	 getTransactionFactoryFromEnvironment(environment);
    // 创建事务对象,对应在配置文件中配置的事务类型,这里使用的是JDBC
    tx = transactionFactory.newTransaction(environment.getDataSource(), level,    	   	     autoCommit);
    // 创建了执行器对象,默认mybatis是开启缓存的,所以对执行器对象使用CacheExecutor进行了一层装饰
    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();

SqlSession执行流程

User user = sqlSession.selectOne("com.jjh.dao.UserDao.selectUser", 1);

BoundSql

// DefaultSqlSession.selectOne方法
@Override
public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  // 调用了selectList方法
  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就是分页参数
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  // Executor.NO_RESULT_HANDLER,结果集处理器,此时为null
  return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
    // 根据传入的statementId,从configuration的map集合中获取MappedStatement对象
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 获取BoundSql对象,这个对象中存放的就是真正要执行的sql(已经完成了占位符替换的sql)
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  // 创建缓存的key
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public BoundSql getBoundSql(Object parameterObject) {
  // 通过sqlSource获取getBoundSql
  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;
}

cacheKey

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 获取BoundSql对象,这个对象中存放的就是真正要执行的sql(已经完成了占位符替换的sql)
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  // 创建缓存的key
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

如果mybatis开启缓存的话,当查询数据时,先去缓存中查找,如果缓存中没有的话,就会到数据库中找。将找到的数据先写到缓存当中,然后在将数据返回给用户。所以缓存的使用很重要,在mybatis中,实现缓存的原理就是map集合,value就是在数据库中查询到的数据。那么怎么样保证key的唯一性是一个关键的问题,比如两个sql一模一样,第二个sql怎么去缓存中找数据,所以key的设置是至关重要的,接下来我们查看源码,看看mybatis是如何对key进行设置的

// CachingExecutor.createCacheKey方法
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
  // mybatis如果开启缓存,执行器会被CachingExecutor进行装饰,所以delegate就是被装饰的执行器
  // 在这里是SimpleExecutor执行器
  return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
// delegate的createCacheKey方法
// 我们看到返回的是CacheKey对象,作为map的key
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  CacheKey cacheKey = new CacheKey();
  cacheKey.update(ms.getId());
  cacheKey.update(rowBounds.getOffset());
  cacheKey.update(rowBounds.getLimit());
  cacheKey.update(boundSql.getSql());
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  TypeHandlerRegistry typeHandlerRegistry =    		         				   		       ms.getConfiguration().getTypeHandlerRegistry();
  // mimic DefaultParameterHandler logic
  for (ParameterMapping parameterMapping : parameterMappings) {
    if (parameterMapping.getMode() != ParameterMode.OUT) {
      Object value;
      String propertyName = parameterMapping.getProperty();
      if (boundSql.hasAdditionalParameter(propertyName)) {
        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);
      }
      cacheKey.update(value);
    }
  }
  if (configuration.getEnvironment() != null) {
    // issue #176
    cacheKey.update(configuration.getEnvironment().getId());
  }
  return cacheKey;
}

// 查看CacheKey的update方法
// 传递进来的参数不同,就会计算出不同的hashcode值,并把参数放进updateList集合中
public void update(Object object) {
  int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
  count++;
  checksum += baseHashCode;
  baseHashCode *= count;
  hashcode = multiplier * hashcode + baseHashCode;
  updateList.add(object);
}

// 意思就是说只要传递进来的参数一样,就会有相同的hashcode值,所以我们看看创建CacheKey时用了几次update方法
// 标签id
cacheKey.update(ms.getId());
// 分页参数
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
// 被解析的sql
cacheKey.update(boundSql.getSql());
// 传递的参数值
cacheKey.update(value);
// 当前的环境id
cacheKey.update(configuration.getEnvironment().getId());
// 可以这么理解,如果两次sql,上面的这几个参数都是一样的话,就认为两个cachekey对象是一样的
// 所以能够在缓存中通过key获取上查询的值

缓存优先级

DefaultSqlSession.selectList方法

return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  // 获取二级缓存
  Cache cache = ms.getCache();
  // 如果二级缓存不为null,表示我们在映射配置文件中开启了二级缓存
  if (cache != null) {
    // 如果我们在映射配置文件中配置了刷新缓存,那么每次查询都要清空缓存
    // 默认不会清空缓存,所以不会走这个方法
    flushCacheIfRequired(ms);
    // 判断映射文件中是否配置了useCache,默认为true
    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);
}

// delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
@SuppressWarnings("unchecked")
@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();
    }
  }
 // 返回list
 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 {
    // 执行完成从一级缓存中移除这个key
    localCache.removeObject(key);
  }
  // 将查询数据放到一级缓存中
  localCache.putObject(key, list);
  
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
  return list;
}

SimpleExecutor.doQuery方法,真正的去数据库中查询数据

StatementHandler

image-20240309192810455

SimpleExecutor.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);
    // 准备处理器,主要包括创建statement以及动态参数的设置
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  // 创建的是RoutingStatementHandler语句处理器,查看被装饰的哪个处理器
  StatementHandler statementHandler = new RoutingStatementHandler(executor,       	  	   mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  // 插件机制,对核心对象进行拦截
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  // 如果在映射文件中没有指定处理器类型,默认使用的就是PreparedStatementHandler处理器
  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());
  }
}
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
  return Collections.emptyList();
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  // 真正获取Connection对象
  Connection connection = getConnection(statementLog);
  // 创建statement对象
  stmt = handler.prepare(connection, transaction.getTimeout());
  // 参数化处理
  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;
  }
}

参数的设置

// 参数化处理
handler.parameterize(stmt);
@Override
public void parameterize(Statement statement) throws SQLException {
  delegate.parameterize(statement);
}

@Override
public void parameterize(Statement statement) throws SQLException {
  parameterHandler.setParameters((PreparedStatement) 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 {
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException | SQLException e) {
          throw new TypeException("Could not set parameters for mapping: " + 					  parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}
@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);
    // 准备处理器,主要包括创建statement以及动态参数的设置
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 真正的执行sql,从数据库中查询数据
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws         SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行sql
    ps.execute();
    // 封装结果集
    return resultSetHandler.handleResultSets(ps);
  }

解析结果集

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  // 解析结果集
  return resultSetHandler.handleResultSets(ps);
}
@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,里面存放着结果集
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  // 获取结果集映射ResultMap集合
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  while (rsw != null && resultMapCount > resultSetCount) {
    // 得到结果集映射信息,取出第一个结果
    ResultMap resultMap = resultMaps.get(resultSetCount);
    // 根据映射规则对结果集进行pojo转换
    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);
}
posted @ 2024-03-28 00:16  PursueExcellence  阅读(17)  评论(0)    收藏  举报