mybatis-advanced
mybatis源码分析
mybatis整体架构设计

组件之间的调用关系

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

下载之后存放到自定义文件夹中
导入源码工程
在idea中导入源码工程


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

到此,源码工程导入成功
搭建测试环境
将test目录下的resources文件夹进行标记
先进行标记,否则就会找不到这个文件夹下面的资源

编写mybatis核心配置文件

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&useUnicode=true&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映射文件

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>
编写实体类

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 + '\'' +
'}';
}
}
编写测试类

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();
}
}
测试运行

测试环境搭建成功,接下来就可以进行源码分析
源码分析-初始化
配置文件加载
// 加载配置文件
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

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

浙公网安备 33010602011771号