MyBatis是我们工作中常见的ORM持久层框架,对于MyBatis并不能仅仅局限于会使用的阶段,更需要了解它的工作原理,想要了解原理,源码是必须要读的,这篇文章是我个人在阅读MyBatis的源码过程中的一些简单的总结,MyBatis的流程大体上可以分为一下几步:加载配置文件 --> 创建SqlSessionFactory --> 创建SqlSession --> 执行SQL语句 --> 返回结果。步骤代码如下:
public static void main(String[] args) {
String resource = "mybatis-config.xml";
SqlSession sqlSession = null;
// 1、读取mybatis-config.xml
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
// 2、解析mybatis-config.xml配置文件,通过SqlSessionFactoryBuilder.build创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3、创建sqlSession
sqlSession = sqlSessionFactory.openSession();
Map<String, Object> paramsMap = new HashMap<>(2);
paramsMap.put("id", 1);
paramsMap.put("name", "jams");
//4、执行sql
List<User> list = sqlSession.selectList("com.c.mybatis.mapper.UserMapper.selectByName", paramsMap);
log.info("响应结果:{}", JSONUtil.toJsonStr(list));
} catch (Exception e) {
e.printStackTrace();
} finally {
assert sqlSession != null;
sqlSession.close();
}
}
这段代码是一个整体的过程展示,下面对这些步骤进行逐步拆解。
1、加载配置信息
加载配置信息的目的是获取数据源信息和Mapper.xml等文件配置信息。
源码如下:
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
这部分是一个文件读取信息,将配置信息读取为InputStream的过程。
2、创建SqlSessionFactory
SqlSessionFactory是MyBatis中一个重要的接口,SqlSessionFactory的创建是通过SqlSessionFactoryBuilder的build方法中进行,其源码如下:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
其中构建的详细逻辑在build方法中,build方法的源码如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//创建XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//创建SqlSessionFactory对象
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
上面的源码中有两处是需要继续阅读的,一个是new XMLConfigBuilder,一个是pase()方法,new XMLConfigBuilder将读取的文件流转成XMLConfigBuilder对象并进行了一系列的初始化操作,其中最重要的是进行了Configuration的创建,Configuration是一个极为重要的上下文对象。我们对这两个步骤的源码进行继续跟进。new XMLConfigBuilder的源码如下:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//创建Configuration对象
super(new Configuration());
this.localReflectorFactory = new DefaultReflectorFactory();
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
其中super(new Configuration());操作进行了各类的属性初始话,源码如下:
public Configuration() {
this.safeResultHandlerEnabled = true;
this.multipleResultSetsEnabled = true;
this.useColumnLabel = true;
this.cacheEnabled = true;
this.useActualParamName = true;
this.localCacheScope = LocalCacheScope.SESSION;
this.jdbcTypeForNull = JdbcType.OTHER;
this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
this.defaultExecutorType = ExecutorType.SIMPLE;
this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
this.variables = new Properties();
this.reflectorFactory = new DefaultReflectorFactory();
this.objectFactory = new DefaultObjectFactory();
this.objectWrapperFactory = new DefaultObjectWrapperFactory();
this.lazyLoadingEnabled = false;
this.proxyFactory = new JavassistProxyFactory();
this.mapperRegistry = new MapperRegistry(this);
this.interceptorChain = new InterceptorChain();
this.typeHandlerRegistry = new TypeHandlerRegistry();
this.typeAliasRegistry = new TypeAliasRegistry();
this.languageRegistry = new LanguageDriverRegistry();
this.mappedStatements = (new Configuration.StrictMap("Mapped Statements collection")).conflictMessageProducer((savedValue, targetValue) -> {
return ". please check " + savedValue.getResource() + " and " + targetValue.getResource();
});
this.caches = new Configuration.StrictMap("Caches collection");
this.resultMaps = new Configuration.StrictMap("Result Maps collection");
this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection");
this.keyGenerators = new Configuration.StrictMap("Key Generators collection");
this.loadedResources = new HashSet();
this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers");
this.incompleteStatements = new LinkedList();
this.incompleteCacheRefs = new LinkedList();
this.incompleteResultMaps = new LinkedList();
this.incompleteMethods = new LinkedList();
this.cacheRefMap = new HashMap();
this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
this.languageRegistry.register(RawLanguageDriver.class);
}
到这里,做的最重要的操作是创建了一个Configuration对象,XMLConfigBuilder对象创建完成后,调用XMLConfigBuilder的pase()方法,该方法的主要作用是解析配置文件,并对配置文件中的各个节点进行处理,例如生成数据源对象DataSource,将Mapper.xml文件和持久层接口映射,保存Mapper.xml中的各个sql语句,以便后续执行sql时,获取对应的sql。其源码如下:
public Configuration parse() {
//parsed用来标识是否已经在解析中,parsed = true表示已经有线程在启动了,无需再次加载信息,抛出异常
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
//标识正在加载
this.parsed = true;
//进行配置文件信息的解析,处理配置文件中的各个节点
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
继续跟踪parseConfiguration源码,其源码如下:
//XNode是配置文件流转化的对象,里面保存了配置文件中的各个节点信息,然后依次解析
private void parseConfiguration(XNode root) {
try {
//解析properties信息
this.propertiesElement(root.evalNode("properties"));
//解析settings信息
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
//解析typeAliases信息
this.typeAliasesElement(root.evalNode("typeAliases"));
//解析plugins信息
this.pluginElement(root.evalNode("plugins"));
//解析objectFactory信息
this.objectFactoryElement(root.evalNode("objectFactory"));
//解析objectWrapperFactory信息
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析reflectorFactory信息
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
//解析environments信息,得到数据源对象DataSource,后面执行sql语句连接数据库时会用到(重点理解)
this.environmentsElement(root.evalNode("environments"));
//解析databaseIdProvider信息
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析typeHandlers信息
this.typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers信息,用于接口和Mapper.xml映射,解析xml中的sql语句(重点理解)
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
这些文件的解析方法中this.environmentsElement(root.evalNode("environments"))和this.mapperElement(root.evalNode("mappers"))是需要重点关注的地方,源码分别如下:
this.environmentsElement(root.evalNode("environments"))源码:
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (this.environment == null) {
this.environment = context.getStringAttribute("default");
}
Iterator var2 = context.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String id = child.getStringAttribute("id");
if (this.isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
//获取dataSource信息
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
//创建DataSource
DataSource dataSource = dsFactory.getDataSource();
Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
//将数据源信息保存在Configuration对象中
this.configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
//解析dataSource节点信息
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
//将dataSource节点转化为Properties对象
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
} else {
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
}
public Properties getChildrenAsProperties() {
Properties properties = new Properties();
Iterator var2 = this.getChildren().iterator();
//解析数据源属性
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
执行的效果如下:
到这里可以看出来,我们配置在cogfig.xml文件中的数据库信息已经被解析出来了,并保存在上下文对象configuration中,解析到了数据源信息,后续在连接数据库的时候可以使用了。但是最终的目的是为了执行sql。sql语句必然也需要进行解析并保存起来,这部分操作就在this.mapperElement(root.evalNode("mappers"))中进行,其源码如下:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
//进行mapper的解析
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
//优先解析package的配置形式,如果没有使用package的配置形式,再解析其他的配置形式
//所以package形式的优先级是最高的
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
//解析resource,url,class的配置形式,三者人选其一进行配置,我使用的是resourc的形式
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
//resource形式的配置方式解析
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
由于我使用的是resource的形式,我就继续看resource的解析相关的源码,其解析源码如下:
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
//读取为流信息
inputStream = Resources.getResourceAsStream(resource);
//先将Mapper.xml转InputStream流,在将流为XMLMapperBuilder对象
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
//对Mapper.xml进行解析
mapperParser.parse();
}
继续跟进源码,进入mapperParser.parse()的源码进行阅读,源码如下:
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
//解析sql的xml信息
this.configurationElement(this.parser.evalNode("/mapper"));
//将解析后的这个路径添加到loadedResources中
this.configuration.addLoadedResource(this.resource);
//将xml中namespace的接口和xml进行关联绑定,保存到mapperRegistry(map),
//key是namespace反射得到的Class信息,value是一个MapperProxyFactory代理对象
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
这个方法只有几行代码,但是每一行都作用很大,重点看this.configurationElement的源码,详情如下:
//对Mapper.xml进行解析
private void configurationElement(XNode context) {
try {
//得到namespace的值
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
this.builderAssistant.setCurrentNamespace(namespace);
//获取二级缓存信息
this.cacheRefElement(context.evalNode("cache-ref"));
//一级缓存信息
this.cacheElement(context.evalNode("cache"));
//配置的parameterMap信息
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//配置的resultMap信息
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
//返回sql字段配置信息
this.sqlElement(context.evalNodes("/mapper/sql"));
//sql语句信息
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
}
}
private void buildStatementFromContext(List<XNode> list) {
if (this.configuration.getDatabaseId() != null) {
this.buildStatementFromContext(list, this.configuration.getDatabaseId());
}
//解析逻辑
this.buildStatementFromContext(list, (String)null);
}
private void buildStatementFromContext(List<XNode> list) {
if (this.configuration.getDatabaseId() != null) {
this.buildStatementFromContext(list, this.configuration.getDatabaseId());
}
//解析逻辑
this.buildStatementFromContext(list, (String)null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
Iterator var3 = list.iterator();
while(var3.hasNext()) {
//sql语句信息
XNode context = (XNode)var3.next();
//创建XMLStatementBuilder对象
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
//构建MappedStatement执行对象,用于存储sql语句的各种信息
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
}
parseStatementNode是sql语句信息解析方法,将每一个sql中的信息都解析出来,生成一个MappedStatement对象,并保存到configuration中,源码如下:
//解析sql语句信息,生成MappedStatement对象
public void parseStatementNode() {
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
includeParser.applyIncludes(this.context.getNode());
String parameterType = this.context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = this.resolveClass(parameterType);
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = this.context.getIntAttribute("fetchSize");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String resultType = this.context.getStringAttribute("resultType");
Class<?> resultTypeClass = this.resolveClass(resultType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultSetType = this.context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = this.configuration.getDefaultResultSetType();
}
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
String resultSets = this.context.getStringAttribute("resultSets");
//生成MappedStatement对象,并保存在configuration中
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
将MappedStatement对象保存到configuration中,过程如下:
public MappedStatement addMappedStatement(String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (this.unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
} else {
id = this.applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = (new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType)).resource(this.resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(this.getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired((Boolean)this.valueOrDefault(flushCache, !isSelect)).useCache((Boolean)this.valueOrDefault(useCache, isSelect)).cache(this.currentCache);
ParameterMap statementParameterMap = this.getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
//创建sql信息的MappedStatement对象
MappedStatement statement = statementBuilder.build();
//保存configuration中
this.configuration.addMappedStatement(statement);
return statement;
}
}
到此,配置文件和Mapper.xml文件的加载解析,并保存到configuration的流程已经结束,这个过程中,主要进行的工作是:
创建上下文对象configuration,并解析配置文件,Mapper.xml文件,并将解析出来的数据源信息,sql语句信息等保存与configuration对象中,以便后续使用
最后就是根据解析出来的消息创建SqlSessionFactory对象,调用的是build方法,源码如下:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
3、创建SqlSession
创建完SqlSessionFactory之后,就根据SqlSessionFactory进行SqlSession的创建过程了,SqlSession的创建过程比较简单,就是创建SqlSession对象并对一些属性赋值的过程,源码如下:
//第一步:使用SqlSessionFactory的openSession方法进行创建
SqlSession sqlSession = sqlSessionFactory.openSession();
//第二步:
public SqlSession openSession() {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
核心方法是openSessionFromDataSource中的处理过程,需要看一下这个方法的源码,源码如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
//在上下文对象configuration中获取数据源信息
Environment environment = this.configuration.getEnvironment();
//创建事务管理TransactionFactory工厂
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
//创建事务对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//创建sql执行器
Executor executor = this.configuration.newExecutor(tx, execType);
//根据上下文对象,执行器创建SqlSession对象,并返回
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
创建SqlSession的过程就结束了,这里面只要是为了后面执行sql做准备。接下来我们来看看这些准备工作做好之后,怎么执行一条sql的过程。
4、执行sql并返回结果
sql的执行分为两步走,一步是执行sql,一步是将执行后的数据进行映射为Java中的Object进行返回。我们直接开始看源码,源码中,我才用sqlSession.selectList的调用方式来进行源码跟踪,源码如下:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
//获取构建MappedStatement对象,这个MappedStatement在创建SqlSessionFactory的时候创建的
MappedStatement ms = this.configuration.getMappedStatement(statement);
//执行query查询方法
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
继续跟踪executor.query方法的逻辑,这个方法就是进行sql执行逻辑了。
4.1 执行sql语句
我们可以从executor.query()方法的源码中得到执行的是CachingExecutor中query()方法,源码如下:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//从MappedStatement中获取sql语句信息
BoundSql boundSql = ms.getBoundSql(parameterObject);
//创建缓存
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
//执行sql语句
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
跟踪一下创建缓存的源码createCacheKey,详情如下:
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
//创建一个CacheKey对象,记录sql的各种信息,并将CacheKey返回
CacheKey cacheKey = new CacheKey();
//记录sql的id
cacheKey.update(ms.getId());
//记录偏移量
cacheKey.update(rowBounds.getOffset());
//记录分页信息
cacheKey.update(rowBounds.getLimit());
//记录sql信息
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
Iterator var8 = parameterMappings.iterator();
while(var8.hasNext()) {
ParameterMapping parameterMapping = (ParameterMapping)var8.next();
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
Object value;
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 = this.configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//记录sql参数信息
cacheKey.update(value);
}
}
if (this.configuration.getEnvironment() != null) {
cacheKey.update(this.configuration.getEnvironment().getId());
}
return cacheKey;
}
}
创建完缓存信息后,执行query()方法,源码如下:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//获取xml中配置的信息,没有配置的话,这里始终为null
Cache cache = ms.getCache();
if (cache != null) {
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);
}
return list;
}
}
//执行查询
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续跟踪源码,详情如下:
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 (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
//先查询一级缓存中看下是否存在查询结果,如果不存在,则查询数据库,如果存在则直接返回缓存数据
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
//针对StatementType.CALLABLE的类型进行处理,其他类型不进行任何处理
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//查询数据库
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
继续阅读查询数据库时的操作逻辑,方法this.queryFromDatabase的源码如下:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//先将查询sql缓存起来,value是一个EXECUTION_PLACEHOLDER枚举值
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
//处理查询的逻辑
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//如果存在异常,删除缓存
this.localCache.removeObject(key);
}
//更新缓存信息
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
继续跟踪SimpleExecutor中的this.doQuery()方法的源码,如下:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
//获取上下文信息
Configuration configuration = ms.getConfiguration();
//创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//处理数据库连接等操作
stmt = this.prepareStatement(handler, ms.getStatementLog());
//执行sql语句
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
//创建Statement对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
//获取数据库连接对象Connection
Connection connection = this.getConnection(statementLog);
//创建Statement对象
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
//执行sql
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = this.boundSql.getSql();
//执行sql
statement.execute(sql);
//处理返回结果
return this.resultSetHandler.handleResultSets(statement);
}
到这里就执行完sql了,然后在this.resultSetHandler.handleResultSets(statement)中将查询的结果进行映射后返回。
4.2 执行结果返回
结果处理返回的源码在DefaultResultSetHandler的handleResultSets方法中,其中核心点的注释如下:
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);
} else if (this.resultHandler == null) {
//处理逻辑入口
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);
this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);
}
} finally {
this.closeResultSet(rsw.getResultSet());
}
}
debug中可以看到rsw的具体数据信息如下:
继续跟踪handleRowValues方法的源码,如下:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
this.ensureNoRowBounds();
this.checkResultHandler();
this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//处理逻辑的方法
this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
跟踪handleRowValuesForSimpleResultMap方法源码,如下:
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext();
ResultSet resultSet = rsw.getResultSet();
this.skipRows(resultSet, rowBounds);
while(this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(resultSet, resultMap, (String)null);
//处理后的结果
Object rowValue = this.getRowValue(rsw, discriminatedResultMap, (String)null);
this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
结果数据映射结果,结果返回,效果如下:
由结果可以得到,在调用this.getRowValue()方法后,就得到了我们需要的Java对象并且已经赋值了,所以需要跟踪一下this.getRowValue()方法的处理逻辑,源码如下:
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
ResultLoaderMap lazyLoader = new ResultLoaderMap();
//根据resultMap中的yupe属性创建对象,该对象仅仅是创建完成,还未进行属性赋值
Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
MetaObject metaObject = this.configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (this.shouldApplyAutomaticMappings(resultMap, false)) {
//赋值处理
foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
}
return rowValue;
}
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//创建还未映射的字段
List<DefaultResultSetHandler.UnMappedColumnAutoMapping> autoMapping = this.createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
Iterator var7 = autoMapping.iterator();
while(true) {
DefaultResultSetHandler.UnMappedColumnAutoMapping mapping;
Object value;
do {
if (!var7.hasNext()) {
return foundValues;
}
mapping = (DefaultResultSetHandler.UnMappedColumnAutoMapping)var7.next();
//得到数据库字段的值
value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
} while(value == null && (!this.configuration.isCallSettersOnNulls() || mapping.primitive));
//将数据库字段的值赋值给metaObject的对应的属性值,metaObject是有返回对象创建而来,可以理解为是Java对象
//这里就实现了数据库数据和Java对象的映射
metaObject.setValue(mapping.property, value);
}
} else {
return foundValues;
}
}
以上就是mybatis的执行全过程源码的解读,当然,这并不是一个很详细的源码分析,主要在于对mybatis的主流程逻辑的处理分析。