MyBatis运行原理
MyBatis运行原理
前言
- 第一次阅读源码,或许有那么一点点参考意见,仅作个人记录,后续会再改一改。
- Mybati 版本号: 3.5.13,配置文件是以XML的方式
- 请配合标题查看代码中有注释的部分,笔者只贴部分运行的代码,其他在运行中没有使用到的代码笔者删除,不是笔者不想贴,是太长了影响阅读。请各位读者多复制类名和方法名去寻找或者按照debug流程,还有一定要看markdown的大纲
配置文件
<?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"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?allowMultiQueries=true"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 映射文件地址,在SpringBoot中是 mapper-locations: classpath:mapper/**/*.xml--> <mappers> <mapper resource="mapper/UserMapper.xml"/> <mapper resource="mapper/DeptMapper.xml"/> </mappers> </configuration>
运行dome
- debug断点打字 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream) 进入build方法
/** * <P>运行原理配用代码</P> * * @author unknown * @since 2023/10/10 01:06 * {@link MybatisSourceCodeExplanation} 文字解析 */ public class OperatingPrincipleTest { public static void main(String[] args) throws IOException { // 读取Mybatis配置文件.获取输入流 InputStream resourceAsStream = getResourceAsStream("mybatis-config.xml"); /* 构建 SqlSessionFactorBean 类,在下面的 SqlSessionFactoryBuilder 标题中解释了如何构建 SqlSessionFactory 类 其实 SqlSessionFactory 类中值有一个类 Configuration , 在构建的过程中都是在为 Configuration 类进行赋值 */ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); // 获取SqlSession.设置自动提交数据 SqlSession sqlSession = sqlSessionFactory.openSession(true); // 获取Mapper接口类 UserMapper mapper = sqlSession.getMapper(UserMapper.class); User byId = mapper.findById(2); System.out.println("查询数据: " + byId); // 关闭连接 sqlSession.close(); } }
解析运行流程
SqlSessionFactoryBuilder
-
解析
mybatis-config.xml
配置文件,为Configuration
类设置默认值(也就是Mybatis中有些默认配置) -
解析
Mapper.xml
文件,添加到Configuration
类中的一个Map
集合-
以增删改查标签中的
ID
属性为key
,MappedStatement
为value
,MappedStatement
中包含了每一个增改删除标签。 -
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>
-
-
解析
Mapper.xml
文件中namespace
的值,添加到Configuration
类中的MapperRegistry
对象中-
protected final MapperRegistry mapperRegistry = new MapperRegistry(this); -
MapperRegistry
对象包含了项目中所有namespace
的值
-
-
至于如何读取配置文件信息,在源码的 77行 打下断点
// 在SqlSessionFactoryBuilder类中,对 build 方法进行了重载,而 build 方法最终返回 SqlSessionFactory 对象 public class SqlSessionFactoryBuilder { // reader: 读取配置文件流 public SqlSessionFactory build(Reader reader) { return build(reader, null, null); } // reader: 读取配置文件流 // environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置 public SqlSessionFactory build(Reader reader, String environment) { return build(reader, environment, null); } // reader: 读取配置文件流 // properties:以 Properties 文件形式存在的配置文件 public SqlSessionFactory build(Reader reader, Properties properties) { return build(reader, null, properties); } // reader: 读取配置文件流 // environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置 // properties:以 Properties 文件形式存在的配置文件 public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { if (reader != null) { reader.close(); } } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } // inputStream: Mybatis配置文件流 public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } // inputStream: Mybatis配置文件流 // environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置 public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } // inputStream: Mybatis配置文件流 // environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置 public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } // inputStream: Mybatis配置文件流 // environment: Mybatis配置文件中的environment标签,在mybatis-config.xml配置文件中可以看到是关于数据库信息的配置 // properties:以 Properties 文件形式存在的配置文件 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { // 创建XMLConfigBuilder对象 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //>>>解析配置文件(具体看下个标题),调用最后一个build方法,返回 SqlSessionFactory 的默认实现类 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { if (inputStream != null) { inputStream.close(); } } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } // 返回 SqlSessionFactory 的默认实现类 public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
parser.parse()
-
构建
XMLConfigBuilder
对象,在构造方法中调用parse
方法解析mybatis-config.xml
-
private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment, Properties props) { super(newConfig(configClass)); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); // 在解析配置文件时,判断是否已解析 this.parsed = false; this.environment = environment; // 调用类中的parser this.parser = parser; }
-
-
读取
mybatis-config.xml
文件的configuration
标签parser.evalNode("/configuration")
: 返回XNode
对象,该对象包含mybatis-config.xml
文件的全部内容
-
读取
XNode
对象中的properties
标签- propertiesElement(root.evalNode("properties"));
-
读取
settings
标签,转为Properties
对象- Properties settings = settingsAsProperties(root.evalNode("settings"));
-
调用
settingsElement
方法,当配置文件内有些属性是没有配置的时候,设置默认属性值并保存在configuration
对象中 -
settingsElement(settings);
-
调用
mapperElement
方法,解析业务接口对应的Mapper
文件,判断引入方式, 获取文件地址- mapperElement(root.evalNode("mappers"));
-
通过文件地址解析,具体查看下个小标题
- mapperParser.parse();
public class XMLConfigBuilder extends BaseBuilder { // 构建XMLConfig文件对象,在构造方法中调用 parser 方法解析 private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment, Properties props) { super(newConfig(configClass)); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); // 在解析配置文件时,判断是否已解析 this.parsed = false; this.environment = environment; this.parser = parser; } public Configuration parse() { // 在 XMLConfigBuilder 方法赋值,也就是在构建XMLConfigBuilder对象时 // SqlSessionFactoryBuilder类中的XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // parser.evalNode("/configuration") : 返回XNode对象,该对象是"mybatis-config.xml"文件的全部内容 parseConfiguration(parser.evalNode("/configuration")); // 返回 configuration 对象,此对象包含了所有的配置信息如下: // "mybatis-config.xml" // 我们业务接口对应的mapper.xml // Statement保持的正删改查信息 // MapperRegistry中保存的namespace的值 return configuration; } // 读取"mybatis-config.xml"文件中的配置内容 private void parseConfiguration(XNode root) { try { // 先读取properties相关的属性 // issue #117 read properties first propertiesElement(root.evalNode("properties")); // 读取settings相关的属性 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):"设置默认的配置属性" settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); // 解析业务接口对应的Mapper.xml(例如UserMapper.xml) mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } // 当配置文件内有些属性是没有配置的时候,设置默认属性值并保存在configuration对象中 private void settingsElement(Properties props) { // 看到这些代码的时,想必所有的配置文件都是这样的,有配置属性的加载配置属性,没有配置属性的设置默认值 configuration .setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setAutoMappingUnknownColumnBehavior( AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType"))); configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); configuration.setLazyLoadTriggerMethods( stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler"))); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false)); configuration.setArgNameBasedConstructorAutoMapping( booleanValueOf(props.getProperty("argNameBasedConstructorAutoMapping"), false)); configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType"))); configuration.setNullableOnForEach(booleanValueOf(props.getProperty("nullableOnForEach"), false)); } private void mapperElement(XNode parent) throws Exception { if (parent != null) { // 获取子节点,遍历 for (XNode child : parent.getChildren()) { // 判断"mybatis-config.xmml"中引入的方式,本文用的是 resource 标签 // <mappers> // <mapper resource="mapper/UserMapper.xml"/> // <mapper resource="mapper/DeptMapper.xml"/> // </mappers> if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { // 以 resource 属性引入的方式 String resource = child.getStringAttribute("resource"); // 以 url 属性引入的方式 String url = child.getStringAttribute("url"); // 以 class 属性引入的方式 String mapperClass = child.getStringAttribute("class"); // 判断引入方式,如果以 resource 不等于 null 并且 url 等于 null 并且 class 等于 null if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); try (InputStream inputStream = Resources.getResourceAsStream(resource)) { XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); // 调用 mapperParser.parse(); mapperParser.parse(); } // 判断引入方式,如果以 resource 等于 null 并且 url 不等于 null 并且 class 等于 null } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); try (InputStream inputStream = Resources.getUrlAsStream(url)) { XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); // 调用 mapperParser.parse(); 具体逻辑查看下面标题 mapperParser.parse() mapperParser.parse(); } // 判断引入方式,如果以 resource 等于 null 并且 url 等于 null 并且 class 不等于 null } 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."); } } } } } }
mapperParser.parse()
- 解析业务接口Mapper对应的xml文件
public class XMLMapperBuilder extends BaseBuilder { // 解析业务接口Mapper对应的xml文件,由于是for循序调用,每次解析一个xml文件 public void parse() { // 判断资源是否已被加载,返回false,加上!取反 if (!configuration.isResourceLoaded(resource)) { // 获取xml文件中的 Mapper 标签 configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); // 看完标题上面configurationElement解析mapper到为buildParameterMapping的方法再来看此方法 // 添加本地资源和将namespace的值添加到 MapperRegistry 对象中 bindMapperForNamespace(); } // 以下三个方法笔者debug时没有参数,看不到执行处理过程 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } private void configurationElement(XNode context) { try { // 获取 mapper 标签 String namespace = context.getStringAttribute("namespace"); // 判断是否为 null if (namespace == null || namespace.isEmpty()) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 设置当前命名空间 builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); // 获取 parameterMap 标签 parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 获取 resultMap 标签 resultMapElements(context.evalNodes("/mapper/resultMap")); // 获取 SQL 片段 sqlElement(context.evalNodes("/mapper/sql")); // 解析 select|insert|update|delete 标签 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); } } // 解析 select|insert|update|delete 标签代码实现 private void buildStatementFromContext(List<XNode> list) { // 如果 databaseId 不等于 null, // databaseId 是多数据源标识 if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } // 继续调用 buildStatementFromContext方法 buildStatementFromContext(list, null); } // 解析 select|insert|update|delete 标签代码实现 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { // 遍历所有 select|insert|update|delete 标签,每个标签都是一个单独的 context 对象 for (XNode context : list) { // 构建解析 select|insert|update|delete 标签的 XMLStatementBuilder 对象 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { // 这才是真正的解析业务接口Mapper对应的xml文件代码 具体逻辑查看下级标题 parseStatementNode() statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } } private void bindMapperForNamespace() { // 获取 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); // 将namespace的值添加到 MapperRegistry 对象中,和Statement对象包含所有的增删改查一样,MapperRegistry包含这所有的mapper.xml文件地址 // 在MapperRegistry对象中有两个属性: // 1. private final Configuration config; // 2. private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); configuration.addMapper(boundType); } } } }
parseStatementNode()
- 解析 mapper.xml 文件中的 select | insert | update | delete 标签(
public class XMLStatementBuilder extends BaseBuilder { // 解析 select|insert|update|delete 标签代码实现 public void parseStatementNode() { // 获取select|insert|update|delete标签内的属性ID ,以下只写属性,不在写select|insert|update|delete标签内 String id = context.getStringAttribute("id"); // 获取 databaseId 的属性值 String databaseId = context.getStringAttribute("databaseId"); // 判断当前数据库ID是否和标签内的databaseId一致 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } // 获取标签名称.如 select|insert|update|delete 标签 String nodeName = context.getNode().getNodeName(); // 转换成枚举对象字段属性 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); // 判断是否是 select 标签,以下几乎都是对 select|insert|update|delete 标签内的属性进行判断,就不写注释了 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"); if (resultTypeClass == null && resultMap == null) { resultTypeClass = MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id); } 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"); boolean dirtySelect = context.getBooleanAttribute("affectData", Boolean.FALSE); // 把 mapper.xml 中的每个标签和标签中的属性解析并且添加到 MappedStatement 中 查看下面标题 addMappedStatement builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect); } }
buildParameterMapping
- 把mapper.xml标签中解析完的属性进行校验并且构建 MappedStatement 对象
- 换而言之,每个 MappedStatement 对象就是 select | insert | update | delete 标签的所有内容
public class MapperBuilderAssistant extends BaseBuilder { // XMLStatementBuilder类中调用的 addMappedStatement 方法 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, boolean dirtySelect) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); } // 命名规范校验 id = applyCurrentNamespace(id, false); // 构建 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(flushCache).useCache(useCache).cache(currentCache).dirtySelect(dirtySelect); // 用于获取与给定映射语句相关的参数映射信息 ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement = statementBuilder.build(); // configuration 中定义一个 mappedStatements 的 map 集合,将 Statement 添加到 map 集合中. // 在这个for循环中,至此是不是可以推测项目类中所有的增删改查都会存在 mappedStatements 的 map 集合中? configuration.addMappedStatement(statement); return statement; } }
sqlSessionFactory.openSession(true)
-
断点打在 SqlSession sqlSession = sqlSessionFactory.openSession(true),进入openSession方法
-
获取数据库信息构建 TransactionFactory 类
-
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
-
-
通过数据库配置信息,事物隔离级别,是否自动提交构建 Transaction 类
-
Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
-
-
通过Transaction类和枚举对象 ExecutorType(默认SIMPLE) 构建 Executor类
-
final Executor executor = configuration.newExecutor(tx, execType); -
使用 configuration && executor && autoCommit 构建 DefaultSqlSession 类并且返回 DefaultSqlSession
-
return new DefaultSqlSession(configuration, executor, autoCommit);
-
// 看源码和SqlSessionFactoryBuilder类一样,有多种构造方法 public class DefaultSqlSessionFactory implements SqlSessionFactory { /* configuration.getDefaultExecutorType(): 获取默认的ExecutorType-> SIMPLE,此枚举有三个字段,SIMPLE | REUSE | BATCH SIMPLE: 在SIMPLE执行器下,每次执行SQL语句时都会创建一个新的数据库连接,执行完毕后立即关闭连接。这种执行器类型适用于简单的查询和非事务性操作,但可能在频繁执行SQL语句的情况下引入较大的开销。 REUSE: MyBatis会重复使用现有的数据库连接,而不是每次都创建新的连接。这可以显著降低连接创建和销毁的开销,适用于频繁执行多个SQL语句的情况,例如事务中的多个查询。 BATCH: BATCH 执行器类型通常适用于批量操作,MyBatis 会收集多个 SQL 语句,并在执行时一次性提交它们,但不适用于单个查询操作。 null: 事物隔离级别 autoCommit: 是否设置自动提交 false | true */ @Override public SqlSession openSession(boolean autoCommit) { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 获取"mybatis-config.xml"中的数据库配置 final Environment environment = configuration.getEnvironment(); // 通过数据库配置获取 TransactionFactory 类 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 通过数据库配置信息,事物隔离级别,是否自动提交构建 Transaction 类 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); /* 通过Transaction类和枚举对象 ExecutorType(默认SIMPLE) 构建 Executor类,如果开启了二级缓存那么会创建一个缓存执行器,在执行增删改 查方法时会优先查询缓存,详细内容看 newExecutor 标题 */ final Executor executor = configuration.newExecutor(tx, execType); // 使用 configuration && executor&& autoCommit 构建 DefaultSqlSession 类,DefaultSqlSession 实现 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(); } } }
newExecutor
- 根据 ExecutorType 类型构建 Executor 类,如果开启二级缓存,则构建 CachingExecutor 对象
public class Configuration { //创建 Executor 类 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { // 三元表达式,判断 ExecutorType 的类型 executorType = executorType == null ? defaultExecutorType : executorType; Executor executor; // 根据 ExecutorType 类型构建 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); } // 是否开启二级缓存,如果开启则构建 CachingExecutor 对象 if (cacheEnabled) { // 重点 查看下面标题 CachingExecutor executor = new CachingExecutor(executor); } // 将 executor 添加到拦截链,使用所有拦截器包装 Executor 类,mybatis也有一个拦截器可以实现 return (Executor) interceptorChain.pluginAll(executor); } }
CachingExecutor
public class CachingExecutor implements Executor { private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); // 在 CachingExecutor 的构造方法中传入了 Executor类,只不过在 Executor 类中的 query 方法增加了缓存处理 public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); // 创建缓存索引 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } }
sqlSession.getMapper(UserMapper.class)
- 调用 DefaultSqlSession 类中的 getMapper 方法
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; private final boolean autoCommit; private boolean dirty; private List<Cursor<?>> cursorList; // 调用 Configuration.getMapper 方法 @Override public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); } } // 这里不是内部类,而是笔者懒,直接找到 Configuration 类中的 getMapper 方法粘贴到这里 // 调用 mapperRegistry.getMapper方法 public class Configuration { public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 查看下面标题 mapperRegistry.getMapper return mapperRegistry.getMapper(type, sqlSession); } }
mapperRegistry.getMapper
public class MapperRegistry { public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 获取 MapperProxyFactory 类 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { // 返回一个MapperProxy代理类 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } } public class MapperProxyFactory<T> { public T newInstance(SqlSession sqlSession) {\ // 使用 sqlSession , mapperInterface , methodCache 构建 MapperProxy 类 // sqlSession: 包configuration,Executor,是否自动提交 // mapperInterface: 系统业务接口.如UserMap.java // methodCache: 方法缓存 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); // MapperProxy对象实现了 InvocationHandler 接口,此接口是JavaJDK动态代理一起使用 return newInstance(mapperProxy); } }
mapper.findById(2)
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -4724728412955527868L; private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC; private static final Constructor<Lookup> lookupConstructor; private static final Method privateLookupInMethod; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethodInvoker> methodCache; // 构造方法,看源码记得要看下构造方法 public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } static { Method privateLookupIn; try { privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); } catch (NoSuchMethodException e) { privateLookupIn = null; } privateLookupInMethod = privateLookupIn; Constructor<Lookup> lookup = null; if (privateLookupInMethod == null) { // JDK 1.8 try { lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class); lookup.setAccessible(true); } catch (NoSuchMethodException e) { throw new IllegalStateException( "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", e); } catch (Exception e) { lookup = null; } } lookupConstructor = lookup; } // proxy: 代理类 // method: 执行的方法 // args: SQL参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 判断method.getDeclaringClass()是不是Object类中的方法,如hashCode和equals和toString // 因为method.getDeclaringClass() 是代理类 ,Object是所有类的父类! if (Object.class.equals(method.getDeclaringClass())) { // 如果是就直接执行Object方法 return method.invoke(this, args); } // 调用内部接口的invoke方法 return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } // 定义一个内部接口 interface MapperMethodInvoker { Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable; } // 内部接口实现类 private static class PlainMethodInvoker implements MapperMethodInvoker { private final MapperMethod mapperMethod; // 内部类构造方法,虽然定义了一个新的内部类,但看构造方法还是使用的是 MapperMethod 对象 public PlainMethodInvoker(MapperMethod mapperMethod) { this.mapperMethod = mapperMethod; } // 再次调用 MapperMethod 的 invoke 方法.具体流程查看 mapperMethod.execute 标题 @Override public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { return mapperMethod.execute(sqlSession, args); } } }
mapperMethod.execute
public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); } // MapperMethod 的 invoke 方法 public Object execute(SqlSession sqlSession, Object[] args) { Object result; // 判断增删改查标签 switch (command.getType()) { // 新增方法 case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } // 更新方法 case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } // 删除方法 case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } // 查询方法 case SELECT: // 如果方法没有返回值 if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; // 如果方法返回多条数据 } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); // 如果方法返回Map集合 } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); // 如果方法返回游标 } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); // 以上结果都不满足 } else { // 对参数SQL参数进行解析,具体查看 method.convertArgsToSqlCommandParam(args) 标题 Object param = method.convertArgsToSqlCommandParam(args); // command.getName(): 获取增删改查标签的id值 // param: 包含参数的 map 集合 // sqlSession.selectOne: 调用 selectOne 方法,具体内容请查看 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: 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; } }
method.convertArgsToSqlCommandParam(args)
- 对 UserMapper.java 传进来的参数进行解析
public class ParamNameResolver { // 定义参数名前缀 public static final String GENERIC_NAME_PREFIX = "param"; private final boolean useActualParamName; private final SortedMap<Integer, String> names; private boolean hasParamAnnotation; // 参数名称解析器 public ParamNameResolver(Configuration config, Method method) { // 获取参数使用的实际名称 this.useActualParamName = config.isUseActualParamName(); // 获取参数类型 final Class<?>[] paramTypes = method.getParameterTypes(); // 获取使用 @Parame 注解的参数,这是一个二维数组,第一个数组装的是注解的索引值,第二个装的是索引值对应的参数相关信息 final Annotation[][] paramAnnotations = method.getParameterAnnotations(); // 创建一个 map ,用来存储后续解析完的参数 final SortedMap<Integer, String> map = new TreeMap<>(); // 获取数组长度 int paramCount = paramAnnotations.length; // get names from @Param annotations (从@Param注释中获取名称) for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { if (isSpecialParameter(paramTypes[paramIndex])) { // skip special parameters continue; } String name = null; // 变量二维数组 for (Annotation annotation : paramAnnotations[paramIndex]) { // 如果注解中包含 Param if (annotation instanceof Param) { // 设置为ture hasParamAnnotation = true; // 将注解的值赋值给name, org.apache.ibatis.annotations.Param("id") name = ((Param) annotation).value(); break; } } if (name == null) { // @Param was not specified. if (useActualParamName) { name = getActualParamName(method, paramIndex); } if (name == null) { // use the parameter index as the name ("0", "1", ...) // gcode issue #71 name = String.valueOf(map.size()); } } // 将注解的索引和注解的值存入map map.put(paramIndex, name); } // 存入成员变量中,方便其他方法使用 names = Collections.unmodifiableSortedMap(map); } /** * <p> * A single non-special parameter is returned without a name. Multiple parameters are named using the naming rule. In * addition to the default names, this method also adds the generic names (param1, param2, ...). * </p> * 返回一个没有名称的非特殊参数。多个参数使用命名规则命名。除了默认名称之外,此方法还添加通用名称(param1、param2、...) */ // 参数解析 对照源码上的注释,加上类中定义的参数前缀,在使用Mybatis中可以按照参数的前后顺序也可以用param1、param2、..进行取值 public Object getNamedParams(Object[] args) { // 获取参数数量 final int paramCount = names.size(); // 非空判断 if (args == null || paramCount == 0) { return null; } // 如果参数是单个,直接返回 if (!hasParamAnnotation && paramCount == 1) { Object value = args[names.firstKey()]; return wrapToMapIfCollection(value, useActualParamName ? names.get(names.firstKey()) : null); // 如果参数是多个,封装成一个map } else { // 定义参数封装集合 final Map<String, Object> param = new ParamMap<>(); int i = 0; // 遍历参数集合,至于这个 names.entrySet() 是哪里来的数据,请debug此类中的 ParamNameResolver 方法 for (Map.Entry<Integer, String> entry : names.entrySet()) { param.put(entry.getValue(), args[entry.getKey()]); // add generic param names (param1, param2, ...) // 添加生成的参数名称.如 param1, param2, ... final String genericParamName = GENERIC_NAME_PREFIX + (i + 1); // ensure not to overwrite parameter named with @Param (确保不覆盖以@Param命名的参数) // 如果 param 集合中不包含 genericParamName 则添加到 param集合中 if (!names.containsValue(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } // 返回包含参数的 map 集合 return param; } } }
sqlSession.selectOne
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; private final boolean autoCommit; private boolean dirty; private List<Cursor<?>> cursorList; public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; } public DefaultSqlSession(Configuration configuration, Executor executor) { this(configuration, executor, false); } @Override public <T> T selectOne(String statement) { return this.selectOne(statement, null); } // 调用 selectOne 方法后续 // statement: 增删改查标签的id值 // parameter: 解析完后的参数集合 @Override public <T> T selectOne(String statement, Object parameter) { // 其实在Mybatis中执行的selectOne访问,本质上还是调用了selectList方法,只不过在返回值上进行了处理\ // 继续往下查看 selectList 方法内部逻辑 List<T> list = this.selectList(statement, parameter); // 如果结果是单个,就取第一个,笔者看到这行代码时愣了一下,这才回想起索引是从0开始的,size并不是~ if (list.size() == 1) { return list.get(0); } // 如果结果是多个,直接抛出异常,经典报错!相信大家都不会陌生 if (list.size() > 1) { throw new TooManyResultsException( "Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } } // selectList 方法 @Override public <E> List<E> selectList(String statement) { // 继续向下调用 return this.selectList(statement, null); } // 继续向下调用 @Override public <E> List<E> selectList(String statement, Object parameter) { // 继续向下调用 return this.selectList(statement, parameter, RowBounds.DEFAULT); } // 继续向下调用 @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { // 继续向下调用 return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER); } // selectList 处理过程 private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { // 在标题 SqlSessionFactoryBuilder 的下文有提到过, // MappedStatement是configuration中的一个集合,此集合存储着所有的增删改查标签的内容, // 以属性ID的值为key,增删改查标签的所有内容为value // 通过属性ID的key获取对应的语句标签内容 // 在getMappedStatement 方法中还有一个比较有意思的方法(buildAllStatements),看方法名是构建所有的Statement, // 但是笔者在debug的时候没有数据,无法进入判断语句,待后续在了解 MappedStatement ms = configuration.getMappedStatement(statement); // 判断是否是脏读,数据库的四种隔离级别,读未提交(脏读),读已提交,.可重复读和串行化 dirty |= ms.isDirtySelect(); // wrapCollection(parameter) 标题查看,重点 // executor.query 标题查看 return executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } }
wrapCollection(parameter)
- 在上面的 method.convertArgsToSqlCommandParam(args) 标题中是对多个参数的解析
- 而在 wrapCollection(parameter) 中是对集合类型的数据进行解析
public class ParamNameResolver { public static final String GENERIC_NAME_PREFIX = "param"; private final boolean useActualParamName; /** * Wrap to a {@link ParamMap} if object is {@link Collection} or array. * * @param object * a parameter object * @param actualParamName * an actual parameter name (If specify a name, set an object to {@link ParamMap} with specified name) * * @return a {@link ParamMap} * * @since 3.5.5 */ // 对集合类型的参数进行解析 public static Object wrapToMapIfCollection(Object object, String actualParamName) { // 如果是集合 if (object instanceof Collection) { // 创建map集合,存储value ParamMap<Object> map = new ParamMap<>(); // 以字符串"collection" 为key和value为Mapper.xml中的入参存储到map map.put("collection", object); // 如果List if (object instanceof List) { // 以字符串"list" 为key和value为Mapper.xml中的入参存储到map map.put("list", object); } // 如果有使用 @param 注解给定参数名称,则添加到map中 Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object)); // 返回该map return map; } // 如果参数 != nll 并且是数组 if (object != null && object.getClass().isArray()) { ParamMap<Object> map = new ParamMap<>(); // 以字符串"array" 为key和value为Mapper.xml中的入参存储到map map.put("array", object); // 如果有使用 @param 注解给定参数名称,则添加到map中 Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object)); // 返回该map return map; } return object; } }
executor.query
public class CachingExecutor implements Executor { private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 获取 BoundSql 代表SQL语句的详细信息:SQL语句,参数,以及参数在SQL语句中的映射关系 // setParameters: 在 PreparedStatement 类中的 setParameters 方法中完成参数对#{}的替换 BoundSql boundSql = ms.getBoundSql(parameterObject); // 创建缓存的key: 当前的hashcode,当前的检查码,增删改查的方法名,参数的hashcode,执行的SQL语句,传入的参数值和当前环境信息 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); // 调用下面的 query 方法(在前文有说过,CachingExecutor 的构造方法传入 Executor 对象, // delegate.setExecutorWrapper(this)跟踪下去发现在 BaseExecutor 中 声明了 Executor 类就是 this) return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } // query 方法 @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // 获取二级缓存 Cache cache = ms.getCache(); // 如果缓存 != null : 本次 debug 没有开启二级缓存 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; } } // 调用 BaseExecutor 类中的 query 方法 请查看标题: BaseExecutor.query return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } }
BaseExecutor.query
public abstract class BaseExecutor implements Executor { private static final Log log = LogFactory.getLog(BaseExecutor.class); protected Transaction transaction; protected Executor wrapper; protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads; protected PerpetualCache localCache; protected PerpetualCache localOutputParameterCache; protected Configuration configuration; protected int queryStack; private boolean closed; protected BaseExecutor(Configuration configuration, Transaction transaction) { this.transaction = transaction; this.deferredLoads = new ConcurrentLinkedQueue<>(); this.localCache = new PerpetualCache("LocalCache"); this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache"); this.closed = false; this.configuration = configuration; this.wrapper = this; } // @SuppressWarnings("unchecked") @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // ms.getResource(): UserMapper.xml的相对路径 // ms.getId(): insert | select | update | delete 标签中的ID属性 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++; // 如果 resultHandler == null 则用缓存的key去查缓存,否则 = null 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; } /** * Apply a transaction timeout. * * @param statement * a current statement * * @throws SQLException * if a database access error occurs, this method is called on a closed <code>Statement</code> * * @since 3.4.0 * * @see StatementUtil#applyTransactionTimeout(Statement, Integer, Integer) */ protected void applyTransactionTimeout(Statement statement) throws SQLException { StatementUtil.applyTransactionTimeout(statement, statement.getQueryTimeout(), transaction.getTimeout()); } // 处理一级缓存的输出参数 private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) { if (ms.getStatementType() == StatementType.CALLABLE) { final Object cachedParameter = localOutputParameterCache.getObject(key); if (cachedParameter != null && parameter != null) { final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter); final MetaObject metaParameter = configuration.newMetaObject(parameter); for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) { if (parameterMapping.getMode() != ParameterMode.IN) { final String parameterName = parameterMapping.getProperty(); final Object cachedValue = metaCachedParameter.getValue(parameterName); metaParameter.setValue(parameterName, cachedValue); } } } } } // 从数据库去查询 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 将key添加到缓存中. EXECUTION_PLACEHOLDER: 占位符,没有什么实际意义 localCache.putObject(key, EXECUTION_PLACEHOLDER); try { // 前文有说到,使用Executor的类型是 SIMPLE ,so doQuery 会被 SimpleExecutor 调用,查看下面标题 SimpleExecutor.doQuery 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
public class SimpleExecutor extends BaseExecutor { public SimpleExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } // 从数据库查询数据 // ms: 当前查询标签的详细信息 // parameter: sql参数 // rowBounds: Mybatis封装的逻辑分页,不好用而没什么人使用 // resultHandler: 暂时没有使用到,看命名就知道是处理返回结果的 // boundSql: SQL语句的详细信息:SQL语句,参数,以及参数在SQL语句中的映射关系 @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { // 声明 Statement 对象,此对象也是原生JDBC的 Statement Statement stmt = null; try { // 获取"mybatis-config.xml"配置信息 Configuration configuration = ms.getConfiguration(); // 重点 configuration.newStatementHandler构建 StatementHandler 对象,具体逻辑请查看 newStatementHandler 标题 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); // 执行查询语句 11 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { // 声明 Statement 对象 Statement stmt; // 获取数据库链接 Connection connection = getConnection(statementLog); // 解析 connection 类, 查看下面标题 prepare stmt = handler.prepare(connection, transaction.getTimeout()); // 重点:参数预编译,查看下面标题 parameterize handler.parameterize(stmt); return stmt; } }
newStatementHandler
public class Configuration { public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 构建 StatementHandler 对象,实际返回的是 PreparedStatementHandler对象,具体逻辑查看下面标题 RoutingStatementHandler StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 将 statementHandler 添加到拦截链,使用所有拦截器包装 statementHandler 类,在Executor中有同样的操作 return (StatementHandler) interceptorChain.pluginAll(statementHandler); } }
RoutingStatementHandler
- 用于构建原生 JDBC 中的 Statement 对象
public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { /* - statementType 有三个属性,在默认值是 **PREPARED**,在java.sql包下,这三个属性都是接口形式。 - STATEMENT - 在 Java 中 Statement 继承自 Wrapper ,提供了执行语句和获取结果的基本方法。 - 普通的不带参的查询SQL,支持批量更新,批量删除。 - Statement 每次执行sql语句,数据库都要执行sql语句的编译 。 - 最好用于仅执行一次查询并返回结果的情形,效率高于 PreparedStatement。 - PREPARED - 在 Java 中 PreparedStatement 继承自 Statement,添加了处理 IN 参数的方法。 - 可变参数的SQL,编译一次,执行多次,效率高。 - 安全性好,有效防止Sql注入等问题。 - 支持批量更新,批量删除。 - PreparedStatement 是预编译的,使用 PreparedStatement 有几个好处: - 在执行可变参数的一条SQL时,PreparedStatement 比 Statement 的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高。 - 安全性好,有效防止Sql注入等问题。 - 对于多次重复执行的语句,使用 PreparedStatement 效率会更高一点,并且在这种情况下也比较适合使用batch; - 代码的可读性和可维护性。 - CALLABLE - 在 Java 中 CallableStatement 继承自 PreparedStatement ,添加了处理 OUT 参数的方法。 - 继承自 PreparedStatement ,支持带参数的 SQL 操作; - 支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持; */ // 判断 select 标签中的 StatementType 属性 switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: // 返回一个PreparedStatementHandler 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()); } } }
prepare
- 参数预编译前的准备工作中
- 在调用 stmt = handler.prepare(connection, transaction.getTimeout()) 方法时,handler对象是 RoutingStatementHandler,而RoutingStatementHandler 类的中定义了 private final StatementHandler delegate
public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; // 构造方法 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 此处调用编译前的准备 -> stmt = handler.prepare(connection, transaction.getTimeout()) // 这里 delegate 类型则是 StatementHandler @Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { return delegate.prepare(connection, transactionTimeout); } // 同上,都是调用了 StatementHandler 的方法进行处理 @Override public void parameterize(Statement statement) throws SQLException { delegate.parameterize(statement); }
- StatementHandler 是一个接口,它的另一个实现则是 BaseStatementHandler,该实现类中有对 prepare 方法的具体实现
BaseStatementHandler implements StatementHandler { protected final Configuration configuration; protected final ObjectFactory objectFactory; protected final TypeHandlerRegistry typeHandlerRegistry; protected final ResultSetHandler resultSetHandler; protected final ParameterHandler parameterHandler; protected final Executor executor; protected final MappedStatement mappedStatement; protected final RowBounds rowBounds; protected BoundSql boundSql; protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { this.configuration = mappedStatement.getConfiguration(); this.executor = executor; this.mappedStatement = mappedStatement; this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql; // 以下两个对象,创建的过程同等于 newStatementHandler 标题中的内容 // 构建 parameterHandler 对象,parameterHandler实现类中DefaultParameterHandler的setParameters方法是用于 参数 于 #{} 的替换, this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); // 构建 resultSetHandler 对象,用于处理返回结果 this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); } @Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { // 使用 connection 初始化 Statement 类 statement = instantiateStatement(connection); // 设置语句超时实际 setStatementTimeout(statement, transactionTimeout); // FetchSize: 在解析内有设置此属性的值,标明一次性获取数据的条数,查看下面标题 parameterize setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } }
parameterize
- 使用 ParameterHandler 处理参数
public class DefaultParameterHandler implements ParameterHandler { private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; private final Object parameterObject; private final BoundSql boundSql; private final Configuration configuration; public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { this.mappedStatement = mappedStatement; this.configuration = mappedStatement.getConfiguration(); this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry(); this.parameterObject = parameterObject; this.boundSql = boundSql; } @Override public Object getParameterObject() { return parameterObject; } @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 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); } } } } } }
handler.query
-
query方法调用 RoutingStatementHandler 的 query 方法,和上述一样,而 delegate 的类型是 StatementHandler ,StatementHandler 是一个接口,具体实现在 PreparedStatementHandler 类中(光标选中 StatementHandler 。 ctrl + H 查看类关系图)
- RoutingStatementHandler
public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.query(statement, resultHandler); } - 具体实现 query 方法
public class PreparedStatementHandler extends BaseStatementHandler { public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); } @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); // 查询结果处理 查看 handleResultSets(ps) 标题 return resultSetHandler.handleResultSets(ps); } }
handleResultSets(ps)
public class DefaultResultSetHandler implements ResultSetHandler { @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); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } return collapseSingleResultList(multipleResults); } private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException { 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; } private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) { if (rsw != null && resultMapCount < 1) { throw new ExecutorException( "A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId() + "'. 'resultType' or 'resultMap' must be specified when there is no corresponding method."); } } private List<Object> collapseSingleResultList(List<Object> multipleResults) { return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults; } }
尾声
- 学习源码可以帮你更好的使用该框架,最重要的是可以学习里面的编程思想(编程思想这个说法可能会有点抽象,说白了就是编程方式)
文字版总结
-
学习源码可以帮你更好的使用该框架,最重要的是可以学习里面的编程思想(编程思想这个说法可能会有点抽象,说白了就是编程方式)
-
构建
XMLConfigBuilder
对象,在构造方法中调用parser
方法解析mybatis-config.xml
文件- 解析
configuration
标签,获取所有配件信息,如果没有配置则设置其默认值(在SpringBoot框架,MybatisProperties类用于所有配置属性) - 解析
mybatis-config.xml
文件的mappers标签,获取标签下所有的Mapper.xml
配置路径,遍历配置路径,通过路径获取Mapper.xml
文件,获取Mapper.xml
文件下的所有标签(真的是解析了所有标签,但是笔者下面就记录两个重要的标签) - 解析
namespace
的值,添加到Configuration
类中的MapperRegistry
对象中,换而言之MapperRegistry
对象存储着项目所有的namespace
- 解析
select|insert|update|delete
标签时,每一个MappedStatement
对象就是一个select|insert|update|delete
标签(如果你的Mapper.xml
文件中只有三select
标签,Mybatis就会生成三个MappedStatemen
对象
- 解析
-
其实
在SqlSession
中包含configuration
对象,而configuration
对象中包含所有的配置信息 -
构建
SqlSession
对象- 通过
configuration.getEnvironment()
方法获取构建TransactionFactory
对象 - 通过数据库配置信息,事物隔离级别,是否自动提交构建
Transaction
类 - 通过
Transaction
类和枚举对象ExecutorType
(默认SIMPLE) 构建Executor
类 - 使用
configuration
&&executor
&&autoCommit
构建DefaultSqlSession
类并且返回DefaultSqlSession
(DefaultSqlSession
实现SqlSession
对象)
- 通过
-
获取
Mapper.xml
代理类对象- 调用
DefaultSqlSession
类中的getMapper
方法 - 在
DefaultSqlSession
类中,getMapper
方法的实现是configuration.getMapper
,在configuration.getMapper
中又调用``mapperRegistry.getMapper(在前面有写,
MapperRegistry对象存储着项目所有
namespace`的值) - 使用
sqlSession
、mapperInterface
、methodCache
构建MapperProxy
类
- 调用
-
执行
Mapper.xml
中的方法-
判读方法的
select|insert|update|delete
标签 -
判断方法是否有返回值
-
获取使用
@Parame
注解的参数,如有参数则设置key
,将SQL
参数为value
并存入map
集合 -
获取
BoundSql
:代表SQL
语句的详细信息:SQ
L语句、参数、以及参数在SQL
语句中的映射关系 -
创建缓存的
key
: 当前的hashcode
、当前的检查码、增删改查的方法名、参数的hashcode
、执行的SQL语句,传入的参数值和当前环境信息 -
获取二级缓存对象,在这里不多叙述,本次
debug
没有开启二级缓存 -
用缓存Key查询一级缓存,如果有则处理缓存中的数据作为返回结果,本次
debug
没有则再去从数据库中查询 -
使用当前查询标签的详细信息、
SQL
参数、Mybatis
封装的逻辑分页、resultHandlerboundSql
、boundSql
、构建StatementHandler
对象- 创建数据库连接对象
Connection
- 解析
Connection
,使用Connection
初始化Statement
类,设置语句超时实际。- FetchSize::标明一次性获取数据的条数
- 创建数据库连接对象
-
使用
TypeHandler
处理预编译参数,大胆猜测是SQL
参数#{}
的替换,笔者对这段源码没有debug -
使用
ResultSetHandler.handleResultSets
方法对查询结果的处理
-
面试版
- 解析
mybatis-config.xml
和Mapper.xml
文件。获取相关信息存入MapperRegistry
和MappedStatement
,创建configuration
对象 - 构建
SqlSession
中通过configuration
对象创建TransactionFactory
、Transaction
、Executor
、使用configuration
&&executor
&&autoCommit
构建DefaultSqlSession
类并且返回 - 使用
sqlSession
、mapperInterface
、methodCache
构建MapperProxy
类 - 执行方法时对方法标签、返回值的判断,创建
BoundSql
、创建缓存的key
,先查二级缓存如果二级缓存没有,再查一级如果一级缓存没有,再查数据库,使用TypeHandler
处理预编译参数,再用ResultSetHandler.handleResultSets
对查询结果的处理
疑惑
- 在 configuration 类中有一个 buildAllStatements 方法,此方法在笔者debug时,由于没有参数,进不去判断,看方法名和注释,感觉这个方法对理解源码肯定有帮助。
- 在 CachingExecutor 的 query 方法中,由于没有开启二级缓存,查看不到对缓存的处理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南