MyBatis 底层源码解析 (详细)
MyBatis
一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。(摘抄至官网)
前言:如果您对MyBatis的底层感兴趣,想知道发起一条Sql语句执行,底层走了什么操作,那您可以花点时间,认真阅读下本篇文章,相信会给你不少收获的。本篇文章步骤条例很清晰的。
MyBatis底层是怎么运行的呢?
public static void main(String[] args) throws IOException {
String configName = "mybatis_config.xml";
Reader reader = Resources.getResourceAsReader(configName);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
1:启动,加载配置文件
- new SqlSessionFactoryBuilder().build(reader),SqlSessionFactoryBuilder创建出SqlSessionFactory,reader参数接收一个mybatis-config.xml的流文件。
- 创建 XMLConfigBuilder:config.xml解析器。
- 实例化 XMLConfigBuilder父类(BaseBuilder)的Configuration类。
- 解析config.xml数据,加载到Configuration对象中。
- new DefaultSqlSessionFactory(config) 创建一个SqlSessionFactory实例,默认是DefaultSqlSessionFactory。
如图所示
1.1:new SqlSessionFactoryBuilder().build(reader)
创建SqlSessionFactory对象实例
1.2:new XMLConfigBuilder(reader, environment, properties);
解析 mybatis-config.xml文件
public class XMLConfigBuilder extends BaseBuilder {
/* 标记是否已经解析过配置文件 */
private boolean parsed;
/* 解析器 */
private final XPathParser parser;
/**
* 数据源,SqlSessionFactoryBuilder.build(Reader reader, String environment, Properties properties)
* 不指定为空
*/
private String environment;
public XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
/* 初始化 Configuration */
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
/* 设置格外的属性 */
this.configuration.setVariables(props);
/* 标记初始化为 false */
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
}
1.3:new Configuration()
创建 Configuration 实例
public class Configuration {
/**
* 类型别名注册
* 比如 <dataSource type="POOLED">
* 其中的 type="POOLED" 会使用 PooledDataSourceFactory类创建数据源连接池
*/
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
public Configuration() {
/**
* JDBC 对应使用的 事务工厂类
* <transactionManager type="JDBC"></transactionManager>
*/
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
/**
* MANAGED 对应使用的 事务工厂类
*/
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
/**
* JNDI 对应使用的 数据源
*/
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
/**
* POOLED 对应使用的 数据源
* <dataSource type="POOLED">
*/
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
/**
* UNPOOLED 对应使用的 数据源
*/
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
/**
* 缓存策略
*/
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
/**
* 日志
*/
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
}
1.4 XMLConfigBuilder.parse()
解析 mybatis-config.xml
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
/**
* 解析 <properties resource="my.properties" /> 配置文件
*/
propertiesElement(root.evalNode("properties"));
/**
* 解析settings配置文件
* <settings>
* <setting name="logImpl" value="STDOUT_LOGGING"/>
* </settings>
*/
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
/**
* 解析 typeAliases配置文件
*/
typeAliasesElement(root.evalNode("typeAliases"));
/**
* 解析 plugins 配置文件
* 这个是插件,可以动态的拦截sql执行
*/
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"));
/**
* 加载 mapper.xml 文件
*/
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
1.4.1: mapperElement(root.evalNode(“mappers”));
解析mapper.xml文件,以及接口方法的注解
public class XMLConfigBuilder extends BaseBuilder {
/**
* mapper隐射文件解析
* @param parent
* @throws Exception
*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
/**
* 解析 <package
* <mappers>
* <package name="com.mapper"/>
* </mappers>
*/
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
/**
* 添加所有包下的接口
* 实际调用 configuration.addMapper(mapperInterface);
*/
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
/**
* 解析 <mapper resource
* <mappers>
* <mapper resource="mapper/UserMapper.xml"/>
* </mappers>
*/
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
/**
* mapper.xml 解析器
*/
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
/**
* 开始解析 mapper.xml文件
* 重点分析这个 同样也会调用 configuration.addMapper(mapperInterface); 这个方法
*/
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
/**
* 解析 <mapper url
*/
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
/**
* 解析 <mapper class
*/
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
/**
* 以下代码可以看出 url resource class 三个属性只能选择一个,否则就会报错
*/
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
}
1.4.2:mapperParser.parse();解析
我的UserMapper.xml文件内容为:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.UserMapper">
<select id="findById" resultType="com.entity.User">
select * from `user` where userId = #{userId}
</select>
</mapper>
UserMapper接口内容为:
package com.mapper;
import com.entity.User;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Select("select * from `user` where userId = 2")
User findById(int userId);
}
疑问?UserMapper.xml有<select id=“findById”,而在接口中的findById方法我又加了一个@Select注解;那么执行会选择哪一条Sql执行还是报错呢?
以UserMapper.xml为例子解析,可以看到 resource = mapper/UserMapper.xml
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
/**
* 解析 mapper.xml文件内容
*/
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
/**
* 解析 mapper.xml的<mapper namespace="com.mapper.UserMapper">
* namespace指定的{UserMapper}接口的注解信息
*/
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
}
1.4.3:configurationElement(parser.evalNode("/mapper"));
解析 mapper.xml文件内容
public class XMLMapperBuilder extends BaseBuilder {
private void configurationElement(XNode context) {
try {
/**
* namespace属性
*/
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
/**
* 不指定 namespace会报错哦 由此得知 namespace属性是必须指定的
*/
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
/**
* 解析 cache-ref
*/
cacheRefElement(context.evalNode("cache-ref"));
/**
* 解析 cache
*/
cacheElement(context.evalNode("cache"));
/**
* 解析 parameterMap
*/
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
/**
* 解析 resultMap
*/
resultMapElements(context.evalNodes("/mapper/resultMap"));
/**
* 解析 sql
*/
sqlElement(context.evalNodes("/mapper/sql"));
/**
* 解析 sql语句 select|insert|update|delete
* 重点分析这里,这里的解析会关联到 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);
}
}
}
1.4.4:buildStatementFromContext(context.evalNodes(“select|insert|update|delete”));
解析 sql语句 select|insert|update|delete;list参数内容是select|insert|update|delete的Sql语句
XMLStatementBuilder Sql语句的解析器
1.4.5:statementParser.parseStatementNode();解析Sql语句
builderAssistant.addMappedStatement,并不是添加一个mapper.xml文件隐射的实例,而是为每一个Sql语句创建一个实例
public class XMLStatementBuilder extends BaseBuilder {
private final MapperBuilderAssistant builderAssistant;
public void parseStatementNode() {
/**
* 此处省略一大推代码...
*/
/**
* <select id="findById" resultType="com.entity.User">
* select * from `user` where userId = #{userId}
* </select>
* 参数解析
*
* id:标签指定的id = findById
* sqlSource:Sql语句,Sql参数占位
* statementType:sql执行类型 参考{@link StatementType}
* STATEMENT: 直接操作sql,不进行预编译 ${}
* PREPARED: 预处理,参数,进行预编译 #{}
* CALLABLE: 执行存储过程
* sqlCommandType:sql语句类型 参考{@link SqlCommandType}
* UNKNOWN:未知,INSERT:新增,UPDATE:修改,DELETE:删除,SELECT:查询,FLUSH:刷新
*
* 其他参数可查看官网:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
*/
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
1.4.6:builderAssistant.addMappedStatement();
创建一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中
public class XMLStatementBuilder extends BaseBuilder {
public MappedStatement addMappedStatement() {
/**
* 此处省略一大推代码...
*/
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
/**
* 建造者模式
* 用于设置 MappedStatement的属性
* 此处省略一大推代码...
*/
/**
* 设置参数入参类型 parameterType属性
* <select id="findById" parameterType="int" resultType="com.entity.User">
* select * from `user` where userId = #{userId}
* </select>
*/
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
/**
* 创建一个 {@link MappedStatement} 实例
*/
MappedStatement statement = statementBuilder.build();
/**
* MappedStatement实例添加到 {@link #configuration.mappedStatements} Map集合中
* MappedStatement 是对应一个Sql语句的实例对象
*
* configuration.mappedStatements 存放所有的MappedStatement实例,后面会详细介绍
*/
configuration.addMappedStatement(statement);
return statement;
}
}
以上流程执行完后,又回到1.4.2:mapperParser.parse();解析,现在开始解析 namespace指定的接口的注解信息,并创建该接口的代理工厂对象,UserMapper接口。
1.4.7:bindMapperForNamespace();
开始解析接口注解,并添加一个MapperProxyFactory代理工厂的对象到configuration.mapperRegistry.knownMappers;key是Mapper接口
public class XMLMapperBuilder extends BaseBuilder {
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
/**
* java 反射 Class.classForName
*/
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
/**
* 这里并没有抛出异常,说明 namespace 可以指定一个不存在的接口
*/
//ignore, bound type is not required 忽略,绑定类型不是必需的
}
if (boundType != null) {
if (!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
configuration.addLoadedResource("namespace:" + namespace);
/**
* 添加一个Mapper接口的代理工厂对象到configuration.mapperRegistry.knownMappers集合中
* 参考 {@link Configuration#mapperRegistry},
* {@link MapperRegistry#knownMappers}
*/
configuration.addMapper(boundType);
}
}
}
}
}
1.4.8:configuration.addMapper(boundType);
public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> void addMapper(Class<T> type) {
/**
* mapperRegistry = {@link MapperRegistry} mapper接口注册器,存放所有的mapper接口信息
*/
mapperRegistry.addMapper(type);
}
}
mapperRegistry.addMapper(type);
为Mapper接口创建一个代理工厂,方便后期使用Mapper接口时创建代理类
解析mapper接口的注解信息
public class MapperRegistry {
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
/**
* {@link MapperProxyFactory} 代理接口工厂
*/
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
/**
* {@link MapperAnnotationBuilder} mapper接口注解解析器
*/
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
/* 开始解析 */
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
1.4.9 parser.parse(); MapperAnnotationBuilder.parse()
解析mapper接口的注解信息,parseStatement(method)主要在这个方法中完成
public class MapperAnnotationBuilder {
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
/**
* 解析 @CacheNamespace 注解
*/
parseCache();
/**
* 解析 CacheNamespaceRef 注解
*/
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
/**
* 解析Sql相关注解 列如 @Select|@Update 之类的注解
*
* 重点关注
*/
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
/**
* 解析待定方法
*/
parsePendingMethods();
}
}
1.5.0:parseStatement(method);
创建一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中
public class MapperAnnotationBuilder {
private final MapperBuilderAssistant assistant;
void parseStatement(Method method) {
/**
* 此处省略一大推代码...
*/
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}
1.5.1:assistant.addMappedStatement();
这里可以参考1.4.6:builderAssistant.addMappedStatement();一模一样的操作
都调用了configuration.addMappedStatement(statement);
这里重点分析Configuration.addMappedStatement(statement);在做什么操作,并且解决 1.4.2留下的疑点;UserMapper.xml和UserMapper接口都有findById的Sql语句定义
public class Configuration {
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
}
mappedStatements.put(ms.getId(), ms); 实际调用 Configuration.StrictMap.put()方法
Configuration.StrictMap是一个重写的HashMap,put方法会先校验key是否存在
public class Configuration {
/**
* mappedStatements Sql语句的对象
*
* Configuration.StrictMap 实现了HashMap
*/
protected final Map<String, MappedStatement> mappedStatements = new Configuration.StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
protected static class StrictMap<V> extends HashMap<String, V> {
@Override
@SuppressWarnings("unchecked")
public V put(String key, V value) {
/**
* key 是否存在 存在就抛出异常
*
* 由此得知,方法映射Sql语句只能定义一个,要么在 mapper.xml中定义,要么就注解定义
*/
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key
+ (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
}
if (key.contains(".")) {
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
super.put(shortKey, value);
} else {
super.put(shortKey, (V) new org.apache.ibatis.session.Configuration.StrictMap.Ambiguity(shortKey));
}
}
return super.put(key, value);
}
}
}
debug调试,key已经存在,就会报错。由此得知,方法映射Sql语句只能定义一个,要么在 mapper.xml中定义,要么就注解定义
到此,MyBatis的启动流程就走完了。
2:接下来就来看下是如何执行Sql查询的。
public class Main {
public static void main(String[] args) throws IOException {
String configName = "mybatis_config.xml";
Reader reader = Resources.getResourceAsReader(configName);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
/**
* 获取一个会话连接
*/
SqlSession sqlSession = sqlSessionFactory.openSession();
/**
* 拿到 UserMapper 的代理类
*/
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
/**
* 执行Sql查询
*/
User user = userMapper.findById(1);
System.out.println(user);
}
}
输出结果:User{userId=1, username=‘张三’, sex=‘男’, age=12}
一行代码的查询,底层既然走了那么多流程;
流程图:
2.1:sqlSessionFactory.openSession();打开会话连接
调用DefaultSqlSessionFactory.openSessionFromDataSource();
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
/**
* mybatis_config.xml配置的
* <environment>数据源<environment/>
*/
final Environment environment = configuration.getEnvironment();
/**
* transactionManager 配置的事务管理器工厂 type="JDBC" {@link JdbcTransactionFactory}
*<environments default="developmentss">
* <environment id="developmentss">
* <transactionManager type="JDBC"></transactionManager>
* </environment>
* </environments>
*/
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
/**
* 创建事务管理器,由于上面指定的事务管理器工厂是 {@link JdbcTransactionFactory}
* 所以创建的事务管理器是 {@link JdbcTransaction}
*
* @param level 事务隔离级别
* @param autoCommit 是否自动提交事务
*/
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
/**
* 创建Sql执行器
*
* @param execType 创建执行器类型 defaultExecutorType如果不指定 默认就是 SIMPLE
* <settings>
* <setting name="defaultExecutorType" value="SIMPLE"/>
* </settings>
*/
final Executor executor = configuration.newExecutor(tx, execType);
/**
* 创建一个默认的 SqlSession实例
*/
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
2.2:configuration.newExecutor(tx, execType);创建执行器
public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
/**
* 执行器不仅重用语句还会执行批量更新
*/
executor = new BatchExecutor(this, transaction);
/**
* 执行器会重用预处理语句(PreparedStatement)
*/
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
/**
* 普通的执行器 也是默认的执行器
*/
executor = new SimpleExecutor(this, transaction);
}
/**
* 如果开启了二级缓存 cacheEnabled,创建一个CachingExecutor缓存执行器
* cacheEnabled 默认为true
* <settings>
* <setting name="cacheEnabled" value="true"/>
* </settings>
*/
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
2.3:sqlSession.getMapper(UserMapper.class);
获取Mapper接口代理类实例
public class DefaultSqlSession implements SqlSession {
@Override
public <T> T getMapper(Class<T> type) {
/**
* 用 Configuration 类的 getMapper方法
*/
return configuration.getMapper(type, this);
}
}
public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
/**
* 调用 MapperRegistry Mapper接口注册器
*/
return mapperRegistry.getMapper(type, sqlSession);
}
}
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
/**
* knownMappers 从缓存中获取 MapperProxyFactory Mapper接口代理工厂
* 如果没有找到就会抛出异常,
* 说明获取Mapper接口代理实例时,需要事先定义好 --> 相当于Spring的扫包
*/
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
/**
* 创建代理实例
*/
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
2.3.1:使用JDK代理创建Mapper接口代理
InvocationHandler是MapperProxy
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
/**
* MapperMethod 缓存
*/
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
/**
* JDK 生产代理类
*/
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
/**
* 代理类回调接口
*/
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
2.4:userMapper.findById(1); 调用Mapper接口方法Sql查询
会走代理类 MapperProxy.invoke
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
/**
* 根据方法全限名 获取一个MapperMethod实例,并且缓存
*
* 注意:这里的 methodCache 只是一个引用,缓存的所有对象都在 {@link MapperProxyFactory#methodCache}中
*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
/**
* 开始执行
*/
return mapperMethod.execute(sqlSession, args);
}
/**
* 添加到缓存 methodCache {@link MapperProxyFactory#methodCache}中
* computeIfAbsent HashMap 存在就获取,不存在就新增
* @param method
* @return
*/
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
}
2.5:mapperMethod.execute(sqlSession, args);
执行Sql语句查询,由于我的返回结果是一个 User对象,所以会走到
result = sqlSession.selectOne(command.getName(), param);这一行,查询一条记录
实际走到 DefaultSqlSession.selectOne()
public class MapperMethod {
private final org.apache.ibatis.binding.MapperMethod.SqlCommand command;
private final org.apache.ibatis.binding.MapperMethod.MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new org.apache.ibatis.binding.MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new org.apache.ibatis.binding.MapperMethod.MethodSignature(config, mapperInterface, method);
}
/**
* 开始执行Sql查询
*/
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
// 省略代码... 执行 insert语句 <insert>/@Insert
break;
}
case UPDATE: {
// 省略代码... 执行 insert语句 <update>/@Update
break;
}
case DELETE: {
// 省略代码... 执行 delete语句 <delete>/@Delete
break;
}
case SELECT: // 执行 select语句 <select>/@Select
if (method.returnsVoid() && method.hasResultHandler()) {
// 返回类型是否为空 一般情况做Sql操作都要有返回结果的
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
/**
* 是否返回多个结果集 {@link Collection}集合/数组
*/
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
/**
* 返回类型是否为 Map 集合
*/
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
/**
* 返回类型是否是 游标 {@link org.apache.ibatis.cursor.Cursor}
*/
result = executeForCursor(sqlSession, args);
} else {
/**
* 将参数转换为Sql命令参数
*/
Object param = method.convertArgsToSqlCommandParam(args);
/**
* 发起查询 调用的是 sqlSession中的方法
*/
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;
}
}
DefaultSqlSession.selectOne()可以看出实际是调用selectList(),而且如果返回了多个结果集就会报错。错误信息如下
Expected one result (or null) to be returned by selectOne(), but found: 2
2.6:DefaultSqlSession.selectList()
查询多结果集
public class DefaultSqlSession implements SqlSession {
/**
*
* @param statement 方法全限名 比如:com.mapper.UserMapper.findById
* @param parameter 参数
* @param rowBounds 分页
* @param <E>
* @return
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/**
* 根据方法全限名在 configuration.mappedStatements缓存集合中拿到方法对应的Sql Statement对象实例
*/
MappedStatement ms = configuration.getMappedStatement(statement);
/**
* 使用执行器执行 由当前设置的执行器执行
* <setting name="defaultExecutorType" value="SIMPLE"/>
*
* <setting name="cacheEnabled" value="true"/>
* 注意:cacheEnabled由于开启二级缓存默认为true,会先使用 {@link CachingExecutor} 缓存执行器查询
*/
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
2.7:executor.query执行器查询
执行器的创建查看 2.2:configuration.newExecutor(tx, execType);创建执行器
/**
* 二级缓存执行器
*/
public class CachingExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
/**
* 获取指定的二级缓存
* mapper.xml 指定的 <cache type="com.domain.something.MyCustomCache"/>
* Mapper接口的 @CacheNamespace
*/
Cache cache = ms.getCache();
if (cache != null) {
/* 是否需要刷新二级缓存 */
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
/**
* 获取二级缓存
*/
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
/**
* 如果没有数据查询Sql
*/
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
/**
* 设置缓存
*/
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
/**
* delegate = SimpleExecutor
*/
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
2.8:delegate.query(); delegate = SimpleExecutor
因为我没有指定二级缓存,所以直接走向delegate.query, 因为SimpleExecutor继承了BaseExecutor但是没有重写query方法,所以走的是BaseExecutor.query()
public abstract class BaseExecutor implements Executor {
@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.");
}
/**
* 是否需要清空一级缓存 flushCache设置为true生效
* <select flushCache="true"></select>
*/
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
// 防止多线程重复调用处理
queryStack++;
/**
* localCache.getObject(key) 获取一级缓存
* {@link BaseExecutor.localCache} 类型 org.apache.ibatis.cache.impl.PerpetualCache
*/
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 (org.apache.ibatis.executor.BaseExecutor.DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
}
2.9:queryFromDatabase(),
数据库查询 会执行到子类的doQuery()方法,这里的字类是SimpleExecutor 所以执行 SimpleExecutor.doQuery()
public abstract class BaseExecutor implements Executor {
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
/**
* 一级缓存占位
*/
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
/**
* 调用子类的 doQuery()方法
*
* 这里的字类是SimpleExecutor 所以执行 SimpleExecutor.doQuery()
*/
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
/**
* 添加一级缓存
*/
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
}
2.9.1:SimpleExecutor.doQuery()
执行数据库查询
public class SimpleExecutor extends BaseExecutor {
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();
/**
* 创建 RoutingStatementHandler Sql语句执行类型处理器
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
/**
* 创建 java.sql.Statement
*/
stmt = prepareStatement(handler, ms.getStatementLog());
/**
* 发起数据库查询
*/
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
}
2.9.2:configuration.newStatementHandler();
创建 RoutingStatementHandler Sql语句执行类型处理器
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
/**
* 简单的Sql执行处理器
*/
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
/**
* 预编译的Sql执行处理器
* 这是默认的
*/
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
/**
* 存储过程执行的Sql执行处理器
*/
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}
2.9.3:prepareStatement()
创建 java.sql.Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
3.0:handler.query(stmt, resultHandler);
delegate 参考2.9.2:configuration.newStatementHandler()
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
/**
* 当前的 delegate = PreparedStatementHandler
*/
return delegate.query(statement, resultHandler);
}
}
3.1:delegate.query(statement, resultHandler);
执行 PreparedStatementHandler.query()
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
/**
* 发起数据库查询
*/
ps.execute();
/**
* 封装结果集 resultSetHandler = DefaultResultSetHandler
*/
return resultSetHandler.handleResultSets(ps);
}
}
3.2:resultSetHandler.handleResultSets(ps);
封装结果集
public class DefaultResultSetHandler implements ResultSetHandler {
/**
* 封装结果集
* @param stmt
* @return
* @throws SQLException
*/
@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;
/**
* 包装 ResultSet
*/
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++;
}
}
/**
* 包装成 List
*/
return collapseSingleResultList(multipleResults);
}
}
到这里MyBatis的Sql查询就结束了,同理添加,修改,删除也是同样的流程。
可以自己跟着以上步骤,断点调试追踪下源码,一切迷雾都将会拨开的。
总结:
- 核心依赖于动态代理模式创建Mapper接口代理实例
- SqlSessionFactory负责创建SqlSession,全局唯一实例(类似于单例)
- Configuration 核心中的核心,走到哪里都有它的存在,而且是全局唯一实例(类似于单例)
下一篇分析下MyBatis的核心类,及它们的作用。
觉得对您有帮助,就点个赞呗。????
本文地址:https://blog.csdn.net/liuqi199707/article/details/109712299
推荐阅读
-
Semaphore底层源码实现原理解析
-
【MyBatis源码全面解析】MyBatis一二级缓存介绍
-
【MyBatis源码全面解析】MyBatis一二级缓存介绍
-
Mybatis中的config.xml配置文件详细解析
-
Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?
-
Mybaits 源码解析 (五)----- 面试源码系列:Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)
-
Mybaits 源码解析 (十)----- 全网最详细,没有之一:Spring-Mybatis框架使用与源码解析
-
Mybaits 源码解析 (八)----- 全网最详细,没有之一:结果集 ResultSet 自动映射成实体类对象(上篇)
-
Mybaits 源码解析 (九)----- 全网最详细,没有之一:一级缓存和二级缓存源码分析
-
Mybaits 源码解析 (六)----- 全网最详细:Select 语句的执行过程分析(上篇)(Mapper方法是如何调用到XML中的SQL的?)