MyBatis原理
mybatis源碼原理主要是分為三個部分 初始化、創建會話、語句執行
一、初始化
1.讀取配置文件mybatis-config.xml。
2.解析mApper.xml文件。
3.最后將mapper.xml中的sql語句全部保存到了Map mappedStatements中(mappedStatements的key:namespace+sqlId,value:MappedStatement
mappedStatements還會保存namespace和代理工廠的映射關系,存入到knownMappers.put(type, newMapperProxyFactory<>(type));)。
源碼實現
public void prepare() throws IOException {
//加載mybatis-config.xml配置文件
String resource = "mybatis-config.xml";
//將mybatis配置文件轉換成流的形式
InputStream inputStream = Resources.getResourceAsStream(resource);
//new一個SqlSessionFactoryBuilder,然后將流傳進去,是為了得到一個 sqlSessionFactory
//這里使用到了建造者模式
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
我們通過上面的build開始閱讀我們mybatis的源碼
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 開始解析 mybatis-config.xml,并且創建了 Configuration 對象
//Configuration 對象是對應mybatis-config.xml中的一些配置的
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析XML,最終返回一個 DefaultSqlSessionFactory >>
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.
}
}
}
解析mybatis-config.xml 從 configuration 開始解析,也就是我們xml中的最上層配置開始解析
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
是一層一層對mybatis-config.xml進行解析,并將所有的標簽都放到了Configuration中,我們可以看到一下代碼中的參數
private void parseConfiguration(XNode root) {
try {
// 對于全局配置文件各種標簽的解析,將標簽放到Configuration中
propertiesElement(root.evalNode("properties"));
// 解析 settings 標簽
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 類型別名
typeAliasesElement(root.evalNode("typeAliases"));
// 插件
pluginElement(root.evalNode("plugins"));
// 用于創建對象
objectFactoryElement(root.evalNode("objectFactory"));
// 用于對對象進行加工
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射工具箱
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// settings 子標簽賦值,默認值就是在這里提供的 >>
settingsElement(settings);
// 創建數據源
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析引用的Mapper映射器 這個是最重要的操作
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
我們來看一下最重要的方法都做了哪些操作
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 不同的定義方式的掃描,最終都是調用 addMapper()方法(添加到 MapperRegistry)。這個方法和 getMapper() 對應
// <package name="com.xinyu.demo"/> 包含這樣的標簽才會走if操作
//else 走的是 <mapper resource="BlogMapper.xml"/>這樣的標簽
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// resource 相對路徑
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析 Mapper.xml,詳情看下方代碼塊
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// 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) {
// class 單個接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
把sql都封裝成一個個的statement,然后我們再去把namespace+id綁定一個代理的工廠,后面用來調用,最終也是調用的MapperRegistry.addMapper
public void parse() {
// 總體上做了兩件事情,對于語句的注冊和接口的注冊
if (!configuration.isResourceLoaded(resource)) {
// 1、具體增刪改查標簽的解析。
// 一個標簽一個MappedStatement。
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 2、把namespace(接口類型)和工廠類綁定起來,放到一個map。
// 一個namespace 一個 MapperProxyFactory
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
parse方法中的第一步configurationElement()操作參考以下代碼
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
// 添加緩存對象
cacheRefElement(context.evalNode("cache-ref"));
// 解析 cache 屬性,添加緩存對象
cacheElement(context.evalNode("cache"));
// 創建 ParameterMapping 對象
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 創建 List<ResultMapping>
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析可以復用的SQL
sqlElement(context.evalNodes("/mapper/sql"));
// 解析增刪改查標簽,得到 MappedStatement 也是重要的
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);
}
}
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
// 解析 Statement list代表的是XXXMapper.xml中的所有的sql語句
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
// 用來解析增刪改查標簽的 XMLStatementBuilder
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 解析 Statement,添加 MappedStatement對象 詳情看下方代碼
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
public void parseStatementNode() {
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);
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
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");
// 關鍵的一步: MappedStatement 的創建 封裝成MappedStatement對象
//每一個sql變成一個MappedStatement
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
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 (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
//設置id 是namespace+sqlId判斷最終的sql標簽是否重復
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
// 最關鍵的一步,在 Configuration 添加了 MappedStatement
configuration.addMappedStatement(statement);
return statement;
}
public void addMappedStatement(MappedStatement ms) {
//Id 是namespace+sqlId 也是這個map中的key
//key是 namespace+sqlId value是 MappedStatement
mappedStatements.put(ms.getId(), ms);
}
parse方法中的第二步bindMapperForNamespace()操作參考以下代碼
private void bindMapperForNamespace() {
//拿到namespace
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
//拿到相應的class對象
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
//判斷configuration 是否有 如果沒有 就添加到configuration中
if (!configuration.hasMapper(boundType)) {
configuration.addLoadedResource("namespace:" + namespace);
// 添加到 MapperRegistry,本質是一個 map,里面也有 Configuration
configuration.addMapper(boundType);
}
}
}
}
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 {
// !Map<Class<?>, MapperProxyFactory<?>> 存放的是接口類型,和對應的工廠類的關系
//key是namespace那個接口的對象 value 對應的代理工廠類
knownMappers.put(type, new MapperProxyFactory<>(type));
// 注冊了接口之后,根據接口,開始解析所有方法上的注解,例如 @Select >>
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
二、創建會話
拿到執行器(Excutor)如果沒有傳入執行器類型,默認就是SIMPLE,否則就是傳入的類型。如果開啟的緩存,將會把SimpleExecutor執行器封裝成CachingExecutor創建會話最終就是為了得到SqlSession。
源碼實現
@Test
public void testSelect() throws IOException {
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
// 獲取事務工廠
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 創建事務
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根據事務工廠和默認的執行器類型,創建執行器
final Executor executor = configuration.newExecutor(tx, execType);
//將得到的執行器放入到默認的sqlSession中 最后得到一個sqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
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);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
// 默認 SimpleExecutor
executor = new SimpleExecutor(this, transaction);
}
// 二級緩存開關,settings 中的 cacheEnabled 默認是 true
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 植入插件的邏輯,至此,四大對象已經全部攔截完畢
//緩存的執行器用到的是裝飾器模式
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
簡單舉例上面提到的執行器
//傳入執行器的方式
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
public enum ExecutorType {
//執行mapper.save()方法時,就相當于jdbc的stmt.execute(sql)
SIMPLE,
//會重用sql,通過stmt傳入多項參數值
REUSE,
//相當于stmt.addBatch(sql)這樣的,批量
BATCH;
private ExecutorType() {
}
}
三、語句執行
通過接口的類型,在knownMappers中去獲取代理工廠,然后創建代理類,代理類的handler是MapperProxy調用方法,走到MapperProxy中invoke方法,去掉object中的方法,直接調用。如果你在settings全局配置文件中配置了cacheEnabled為true,并且xml中配置了 標簽,并且在sql語句上配置了useCache,就會調用CachingExecutor對象的query方法CachingExecutor類中的query方法會先看二級緩存中有沒有,如果有直接返回,如果沒有,會去走到SimpleExecutor的query方法最后就和調用JDBC的流程一樣,打開連接,查詢數據,返回數據。
源碼實現
@Test
public void testSelect() throws IOException {
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); // ExecutorType.BATCH
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
//走到代理類的invoke對象
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
從knownMappers中拿到代理對象
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
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);
}
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 1:類加載器:2:被代理類實現的接口、3:實現了 InvocationHandler 的觸發管理類
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
執行該方法會走到代理類的invoke對象
Blog blog = mapper.selectBlogById(1);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// toString hashCode equals getClass等方法,無需走到執行SQL的流程
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 提升獲取 mapperMethod 的效率,先到 MapperMethodInvoker(內部接口) 的 invoke
// 普通方法會走到 PlainMethodInvoker(內部類) 的 invoke
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
interface MapperMethodInvoker {
Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// JAVA8 中 Map 的方法,根據 key 獲取值,如果值是 null,則把后面Object 的值賦給 key
// 如果獲取不到,就創建
// 獲取的是 MapperMethodInvoker(接口) 對象,只有一個invoke方法
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {
// 接口的默認方法(Java8),只要實現接口都會繼承接口的默認方法,例如 List.sort()
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 創建了一個 MapperMethod
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// SQL執行的真正起點
return mapperMethod.execute(sqlSession, args);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
// 普通 select 語句的執行入口 >>
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;
}
//舉個查詢多條的例子
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
// 為了提供多種重載(簡化方法使用),和默認值
// 讓參數少的調用參數多的方法,只實現一次
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//從configuration中獲取MappedStatement 這是在初始化的時候存進去的
//這里是通過namespace+sqlId來進行獲取的
MappedStatement ms = configuration.getMappedStatement(statement);
// 如果 cacheEnabled = true(默認),Executor會被 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();
}
}
//我們在這里看的是走緩存的
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 獲取SQL
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 創建CacheKey:什么樣的SQL是同一條SQL?
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
// cache 對象是在哪里創建的? XMLMapperBuilder類 xmlconfigurationElement()
// 由 <cache> 標簽決定
if (cache != null) {
// flushCache="true" 清空一級二級緩存 >>
flushCacheIfRequired(ms);
//是否使用緩存
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
// 獲取二級緩存
// 緩存通過 TransactionalCacheManager、TransactionalCache 管理
@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;
}
}
// 走到 SimpleExecutor | ReuseExecutor | BatchExecutor
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//沒有走緩存的 ,是直接查詢數據庫的操作
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 異常體系之 ErrorContext
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()) {
// flushCache="true"時,即使是查詢,也清空一級緩存
clearLocalCache();
}
List<E> list;
try {
// 防止遞歸查詢重復處理緩存
queryStack++;
// 查詢一級緩存
// ResultHandler 和 ResultSetHandler的區別
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 真正的查詢流程 查看下方代碼
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
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 {
// 三種 Executor 的區別,看doUpdate
// 默認Simple
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;
}
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
//拿到MappedStatement 中的所有信息
Configuration configuration = ms.getConfiguration();
// 注意,已經來到SQL處理的關鍵對象 StatementHandler
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) {
//這里進行的預編譯 詳情看下方代碼
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) {
// StatementType 是怎么來的? 增刪改查標簽中的 statementType="PREPARED",默認值 PREPARED
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
// 創建 StatementHandler 的時候做了什么? >>
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());
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 獲取 Statement 對象
stmt = handler.prepare(connection, transaction.getTimeout());
// 為 Statement 設置參數
handler.parameterize(stmt);
return stmt;
}
//執行查詢 - 執行sql handler.query(stmt, resultHandler);
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 到了JDBC的流程
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 rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
到此我們就已經閱讀完mybatis從初始化到創建會話最后到執行查詢的原理了。。。