MyBatis 二級(jí)緩存實(shí)現(xiàn)機(jī)制的示例解析
前置準(zhǔn)備
既然要看MyBatis源碼,當(dāng)然是把源碼拉取下來(lái)debug一步一步看才方便呢,這里已經(jīng)拉取下來(lái),并準(zhǔn)備好例子了。
@Slf4j
public class Main {
public static void main(String[] args) {
String resource = "org/apache/ibatis/yyqtest/resource/mybatis-config.xml";
InputStream inputStream = null;
try {
// 將XML配置文件構(gòu)建為Configuration配置類
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 通過(guò)加載配置文件流構(gòu)建一個(gè)SqlSessionFactory DefaultSqlSessionFactory
SqlSessionFactory sqlSessionFactory = null;
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = null;
try {
sqlSession = sqlSessionFactory.openSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
//Role role = roleMapper.testManyParam(1L, "張三");
//Role role = roleMapper.testAnnotation(1L);
Role role = roleMapper.testDynamicParam(1L, "");
log.info(role.getId() + ":" + role.getRoleName() + ":" + role.getNote());
sqlSession.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
sqlSession.rollback();
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
從上訴例子來(lái)看,我們觀察到首先是解析配置文件,再獲取SqlSession,獲取到Sqlsession之后在進(jìn)行各種的CRUD操作,我們先來(lái)看下SqlSession是怎么獲取的。
SqlSession與SqlSessionFactory

?我們根據(jù)時(shí)序圖,一步一步解開(kāi)SqlSession的執(zhí)行流程,首先來(lái)是通過(guò)SqlSessionFactoryBuilder解析配置文件,再根據(jù)配置文件構(gòu)建并返回一個(gè)DefaultSqlSessionFactory,源碼如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 2. 創(chuàng)建XMLConfigBuilder對(duì)象用來(lái)解析XML配置文件,生成Configuration對(duì)象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 3. 將XML配置文件內(nèi)的信息解析成Java對(duì)象Configuration對(duì)象
Configuration config = parser.parse();
// 4. 根據(jù)Configuration對(duì)象創(chuàng)建出SqlSessionFactory對(duì)象
return build(config);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
拿到SqlSessionFactory之后,就可以根據(jù)這個(gè)SqlSessionFactory拿到SqlSession對(duì)象,源碼如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 通過(guò)事務(wù)工廠從一個(gè)數(shù)據(jù)源來(lái)產(chǎn)生一個(gè)事務(wù)
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 獲取執(zhí)行器,這邊獲得的執(zhí)行器已經(jīng)代理攔截器的功能
final Executor executor = configuration.newExecutor(tx, execType);
// 獲取執(zhí)行器后,再將configuration和executor封裝成DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
通過(guò)上面兩個(gè)步驟拿到SqlSession之后,就可以進(jìn)行CRUD了
- 將xml配置文件解析成流
SqlSessionFactory.build將配置文件解析成Configuration,并返回一個(gè)SqlSessionFactory對(duì)象- 拿到
SqlSessionFactory對(duì)象之后,從Environment中拿到DataSource并產(chǎn)生一個(gè)事務(wù) - 根據(jù)事務(wù)
Transaction和執(zhí)行器類型(SIMPLE, REUSE, BATCH,默認(rèn)是SIMPLE)獲取一個(gè)執(zhí)行器Executor(–>該對(duì)象非常重要,事實(shí)上sqlsession的所有操作都是通過(guò)它完成的) - 創(chuàng)建sqlsession對(duì)象。
MapperProxy
拿到Sqlsession對(duì)象之后,就可以拿到我們所寫的xxxMapper,再根據(jù)這個(gè)xxxmapper就可以調(diào)用方法,最終進(jìn)行CRUD。我們來(lái)關(guān)注下我們這個(gè)xxxMapper是一個(gè)接口,接口是不能被實(shí)例化的,為什么可以直接通過(guò)getMapper方式拿到呢?xxxMapper和我們的xxxMapper.xml文件之間又是如何聯(lián)系的?別急,我們接著往下看。
我們先來(lái)看下時(shí)序圖:

通過(guò)這個(gè)時(shí)序圖可以看到是通過(guò)代理的方式來(lái)搞定的,當(dāng)執(zhí)行到自己寫的方法里面的時(shí)候,其實(shí)通過(guò)了MapperProxy在進(jìn)行代理。話不多說(shuō),我們直接來(lái)看下怎么獲取MapperProxy對(duì)象的吧: 哦吼↑ ↓
DefaultSqlSession
/**
* 什么都不做,直接去configuration里面去找
* @param type Mapper interface class
* @param <T>
* @return
*/
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
Configuration
// mapperRegistry實(shí)質(zhì)上是一個(gè)Map,里面注冊(cè)了啟動(dòng)過(guò)程中解析的各種Mapper.xml
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
/**
* configuration也什么都不做,去mapperRegistry里面找
* @param type
* @param sqlSession
* @param <T>
* @return
*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
這里會(huì)發(fā)現(xiàn),這個(gè)mapperRegistry是什么東西啊,mapperRegistry實(shí)質(zhì)上是一個(gè)Map,里面注冊(cè)了啟動(dòng)過(guò)程中解析的各種Mapper.xml。我們點(diǎn)進(jìn)去看看
Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
mapperRegistry的key是接口的Class類型
mapperRegistry的Value是MapperProxyFactory,用于生成對(duì)應(yīng)的MapperProxy(動(dòng)態(tài)代理類)
?這里具體是怎么加進(jìn)去的可以去了解了解解析配置的篇章,我們接著往下走,看看MapperRegistry的源碼:
MapperRegistry
/**
* MapperRegistry標(biāo)識(shí)很無(wú)奈,沒(méi)人做,只有我來(lái)做了
* @param type
* @param sqlSession
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// MapperRegistry表示也要偷懶,偷偷的把粗活交給MapperProxyFactory去做。
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 如果配置文件中沒(méi)有配置相關(guān)Mapper,直接拋異常
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 我們看到mapper都是接口,因?yàn)榻涌谑遣荒軐?shí)例化的,這里通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn),具體可以看下MapperProxy這個(gè)類
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory這里是將接口實(shí)例化的過(guò)程,我們進(jìn)去看看:
MapperProxyFactory
public class MapperProxyFactory<T> {
/**
* 生成Mapper接口的動(dòng)態(tài)代理類MapperProxy,MapperProxy實(shí)現(xiàn)了InvocationHandler接口
*
* @param mapperProxy
* @return
*/
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 動(dòng)態(tài)代理,我們寫的一些接口
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);
}
}
通過(guò)這個(gè)MapperProxyFactory就可以拿到類MapperProxy代理對(duì)象,簡(jiǎn)單來(lái)說(shuō),我們通過(guò)這個(gè)代理就可以很方便的去使用我們寫的一些dao層的接口了,上例子
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); Role role = roleMapper.testManyParam(1L, "張三"); Role role = roleMapper.testAnnotation(1L); Role role = roleMapper.testDynamicParam(1L, "");
拿到這個(gè)對(duì)象之后,接下來(lái)就是sql語(yǔ)句的執(zhí)行了呢,來(lái),上源碼,真正的主菜來(lái)了。
Executor
SqlSession只是一個(gè)門面,前面做的一些事情都只是鋪墊,真正的的干事的其實(shí)是Excutor,SqlSession對(duì)數(shù)據(jù)庫(kù)的操作都是通過(guò)Executor來(lái)完成的。與SqlSession一樣,Executor也是動(dòng)態(tài)創(chuàng)建的,老樣子先上時(shí)序圖:

在看MapperProxy的方法之前,我們先回到獲取Excutor對(duì)象的方法configuration.newExecutor(tx, execType)
/**
* Executor分成兩大類,一類是CacheExecutor,另一類是普通Executor。
*
* @param transaction
* @param executorType
* @return
*/
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
// 這句再做一下保護(hù),囧,防止粗心大意的人將defaultExecutorType設(shè)成null?
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 普通Executor又分為三種基本的Executor執(zhí)行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
// 執(zhí)行update或select,以sql作為key查找Statement對(duì)象,存在就使用,不存在就創(chuàng)建,用完后,不關(guān)閉Statement對(duì)象,而是放置于Map<String,Statement>內(nèi),供下一次使用。簡(jiǎn)言之,就是重復(fù)使用Statement對(duì)象
executor = new ReuseExecutor(this, transaction);
} else {
// 每執(zhí)行一次update或select,就開(kāi)啟一個(gè)Statement對(duì)象,用完立刻關(guān)閉Statement對(duì)象。
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
// CacheExecutor其實(shí)是封裝了普通的Executor,和普通的區(qū)別是在查詢前先會(huì)查詢緩存中
// 是否存在結(jié)果,如果存在就使用緩存中的結(jié)果,如果不存在還是使用普通的Executor進(jìn)行
// 查詢,再將查詢出來(lái)的結(jié)果存入緩存。
executor = new CachingExecutor(executor);
}
// 調(diào)用每個(gè)攔截器的方法,這里的話應(yīng)該是為了方便擴(kuò)展,方便開(kāi)發(fā)者自定義攔截器做一些處理
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
從上訴代碼可以看出,如果不開(kāi)啟緩存cache的話,創(chuàng)建的Executor只是3中基礎(chǔ)類型之一,BatchExecutor專門用于執(zhí)行批量sql操作,ReuseExecutor會(huì)重用statement執(zhí)行sql操作,SimpleExecutor只是簡(jiǎn)單執(zhí)行sql沒(méi)有什么特別的。
?開(kāi)啟了緩存(默認(rèn)開(kāi)啟),就會(huì)去創(chuàng)建CachingExecutor,這個(gè)緩存執(zhí)行器在查詢數(shù)據(jù)庫(kù)前會(huì)先查找緩存是否存在結(jié)果,如果存在就使用緩存中的結(jié)果,如果不存在還是使用普通的Executor進(jìn)行查詢,再將查詢出來(lái)的結(jié)果存入緩存。我們還看到這里有個(gè)加載插件的方法,就說(shuō)明這里是可以被插件攔截的,如果定義了針對(duì)Executor類型的插件,最終生成的Executor對(duì)象是被各個(gè)插件插入后的代理對(duì)象。
接下來(lái),咱們才要真正的去了解sql的執(zhí)行過(guò)程了?;氐缴厦鎭?lái)看,我們拿到了MapperProxy,調(diào)用Mapper接口的所有方法都會(huì)優(yōu)先調(diào)用到這個(gè)代理類的invoke方法(注意由于MyBatis中的Mapper接口沒(méi)有實(shí)現(xiàn)類,所以MpperProxy這個(gè)代理對(duì)象中沒(méi)有委托類,也就是說(shuō)MapperProxy干了代理類和委托類的事情),具體是怎么做的,上源碼:
MapperProxy
MapperProxy的invoke方法本質(zhì)上是交給了MapperMethodInvoker,MapperMethodInvoker實(shí)質(zhì)就是封裝了一層MapperMethod。
/**
* 通過(guò)動(dòng)態(tài)代理后,所有的Mapper方法調(diào)用都會(huì)走這個(gè)invoke方法
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 并不是任何一個(gè)方法都需要執(zhí)行調(diào)用代理對(duì)象進(jìn)行執(zhí)行,如果這個(gè)方法是Object中通用的方法(toString、hashCode等)無(wú)需執(zhí)行
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// cachedInvoker封裝了一個(gè)MapperMethod對(duì)象
// 拆分出來(lái)更好理解
MapperMethodInvoker mapperMethodInvoker = cachedInvoker(method);
return mapperMethodInvoker.invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
MapperMethod
根據(jù)參數(shù)和返回值類型選擇不同的sqlSession來(lái)執(zhí)行。這樣mapper對(duì)象和sqlSession就真正關(guān)聯(lián)起來(lái)了
/**
* 這里其實(shí)就是先判斷CRUD的類型,根據(jù)類型去選擇到底執(zhí)行了sqlSession中的哪個(gè)方法
* @param sqlSession
* @param args
* @return
*/
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()) {
// 如果結(jié)果是map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
// 從字面意義上看,講轉(zhuǎn)換的參數(shù)放到sql里面
// Map<String, Object> param
Object param = method.convertArgsToSqlCommandParam(args);
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;
}
既然又回到SqlSession了,前面提到過(guò),sqlsession只是一個(gè)門面,真正發(fā)揮作用的是executor,對(duì)sqlsession方法的訪問(wèn)最終都會(huì)落到executor的相應(yīng)方法上去。Executor分成兩大類,一類是CacheExecutor,另一類是普通Executor。Executor的創(chuàng)建前面已經(jīng)介紹了,那么咱們就看看SqlSession的CRUD方法了,為了省事,還是就選擇其中的一個(gè)方法來(lái)做分析吧。這兒,咱們選擇了selectList方法。這里有寶寶就好奇了,從上面的代碼來(lái)看只有selectOne方法呢,哪里來(lái)的selectList。其實(shí)selectOne方法里面本質(zhì)就是調(diào)用了selectList方法。
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 轉(zhuǎn)而去調(diào)用selectList,很簡(jiǎn)單的,如果得到0條則返回null,得到1條則返回1條,得到多條報(bào)TooManyResultsException錯(cuò)
// 這里沒(méi)有查詢到結(jié)果的時(shí)候會(huì)返回null。因此一般建議mapper中編寫resultType使用包裝類型
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
石錘了!?。?,接下來(lái)我們來(lái)看下selectList方法。
DefaultSqlSession
/**
*
* @param statement 全限定名稱+方法名 例如org.apache.ibatis.yyqtest.mapper.RoleMapper.testAnnotation
* @param parameter
* @param rowBounds
* @param handler
* @param <E>
* @return
*/
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
// 根據(jù)傳入的全限定命+方法名從映射的map中取出MappedStatement對(duì)象
MappedStatement ms = configuration.getMappedStatement(statement);
// 之類能看到CRUD實(shí)際上是交給Executor處理
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
?這里的statement是全限定名稱+方法名字,例如org.apache.ibatis.yyqtest.mapper.RoleMapper.testAnnotation,具體怎么來(lái)的,可以自己debug看下是怎么得到的。這里的getMapperStatement做了什么呢。話不多說(shuō)上源碼。
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
}
?映射的語(yǔ)句,存在Map里,這里應(yīng)該是掃描配置文件的時(shí)候就已經(jīng)加進(jìn)去了 key是全限定名+方法名 value是對(duì)應(yīng)的mappedStatements。這個(gè)MapperStatements方法到底有什么作用呢,我們先別急。先回到executor.query的方法繼續(xù)往下走。
StatementHandler
可以看出,Executor本質(zhì)上也是個(gè)甩手掌柜,具體的事情原來(lái)是StatementHandler來(lái)完成的。當(dāng)Executor將指揮棒交給StatementHandler后,接下來(lái)的工作就是StatementHandler的事了。我們先看看StatementHandler是如何創(chuàng)建的:
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;
}
可以看到每次創(chuàng)建的StatementHandler都是RoutingStatementHandler,它只是一個(gè)分發(fā)者,他一個(gè)屬性delegate用于指定用哪種具體的StatementHandler。可選的StatementHandler有SimpleStatementHandler、PreparedStatementHandler和CallableStatementHandler三種。選用哪種在mapper配置文件的每個(gè)statement里指定,默認(rèn)的是PreparedStatementHandler。同時(shí)還要注意到StatementHandler是可以被攔截器攔截的,和Executor一樣,被攔截器攔截后的對(duì)像是一個(gè)代理對(duì)象。
tatementHandler創(chuàng)建后需要執(zhí)行一些初始操作,比如statement的開(kāi)啟和參數(shù)設(shè)置、對(duì)于PreparedStatement還需要執(zhí)行參數(shù)的設(shè)置操作等。代碼如下:
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;
}
這里呢會(huì)創(chuàng)建一個(gè)StatementHandler對(duì)象,這個(gè)對(duì)象中同時(shí)會(huì) 封裝ParameterHandler和ResultSetHandler對(duì)象。調(diào)用StatementHandler預(yù)編譯參數(shù) 以及設(shè)置參數(shù)值,使用ParameterHandler來(lái)給sql設(shè)置參數(shù)。
參數(shù)設(shè)置完畢后,執(zhí)行數(shù)據(jù)庫(kù)操作(update或query)。如果是query最后還有個(gè)查詢結(jié)果的處理過(guò)程。接下來(lái),咱們看看StatementHandler 的一個(gè)實(shí)現(xiàn)類 PreparedStatementHandler(這也是我們最常用的,封裝的是PreparedStatement), 看看它使怎么去處理的:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 將處理好的結(jié)果交給了ResultSetHandler
return resultSetHandler.handleResultSets(ps);
}
到這里,一次sql的執(zhí)行流程就執(zhí)行完了!
簡(jiǎn)單總結(jié)
獲取SqlSession流程
SqlSessuibFactoryBuilder解析配置文件,并將這些配置放到一個(gè)Configuration對(duì)象,這個(gè)對(duì)象中包含了MyBatis需要的所有配置,然后會(huì)用這個(gè)Configuration對(duì)象再去創(chuàng)建一個(gè)SqlSessionFactory對(duì)象。- 拿到
SqlSessionFactory對(duì)象后,會(huì)調(diào)用SqlSessionFactory的openSession方法,這個(gè)方法根據(jù)解析配置文件的事務(wù)和執(zhí)行器類型來(lái)創(chuàng)建一個(gè)執(zhí)行器,這個(gè)執(zhí)行器就是真正sql的執(zhí)行者。最后將執(zhí)行器和configuration封裝起來(lái)返回一個(gè)DefaultSqlSession - 拿到
SqlSession之后就能執(zhí)行各種CRUD的方法了。
Sql執(zhí)行流程
- 拿到
SqlSession之后調(diào)用getMapper方法,拿到Maper接口的代理對(duì)象MapperProxy,調(diào)用Mapper接口的所有方法都會(huì)調(diào)用MapperProxy的invoke方法。 - 到達(dá)invoke方法,就會(huì)掉用執(zhí)行器的executer方法。
- 往下,層層調(diào)下來(lái)會(huì)進(jìn)入
Executor組件(如果配置插件會(huì)對(duì)Executor進(jìn)行動(dòng)態(tài)代 理)的query方法,這個(gè)方法中會(huì)創(chuàng)建一個(gè)StatementHandler對(duì)象,這個(gè)對(duì)象中同時(shí)會(huì) 封裝ParameterHandler和ResultSetHandler對(duì)象。調(diào)用StatementHandler預(yù)編譯參數(shù) 以及設(shè)置參數(shù)值,使用ParameterHandler來(lái)給sql設(shè)置參數(shù)。 - 調(diào)用
StatementHandler的增刪改查方法獲得結(jié)果,ResultSetHandler對(duì)結(jié)果進(jìn)行封 裝轉(zhuǎn)換,請(qǐng)求結(jié)束。
MyBatis一些重要的類
MapperRegistry: 本質(zhì)上是一個(gè)Map其中的key是Mapper接口的全限定名, value的MapperProxyFactory;MapperProxyFactory: 這個(gè)類是MapperRegistry中存的value值,在通過(guò) sqlSession獲取Mapper時(shí),其實(shí)先獲取到的是這個(gè)工廠,然后通過(guò)這個(gè)工廠創(chuàng)建 Mapper的動(dòng)態(tài)代理類;MapperProxy: 實(shí)現(xiàn)了InvocationHandler接口,Mapper的動(dòng)態(tài)代理接口方法的調(diào) 用都會(huì)到達(dá)這個(gè)類的invoke方法;MapperMethod: 判斷你當(dāng)前執(zhí)行的方式是增刪改查哪一種,并通過(guò)SqlSession執(zhí) 行相應(yīng)的操作;SqlSession: 作為MyBatis工作的主要頂層API,表示和數(shù)據(jù)庫(kù)交互的會(huì)話,完成必 要數(shù)據(jù)庫(kù)增刪改查功能;Executor: MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語(yǔ)句的生成和查詢緩 存的維護(hù);StatementHandler: 封裝了JDBC Statement操作,負(fù)責(zé)對(duì)JDBC statement 的操作,如設(shè) 置參數(shù)、將Statement結(jié)果集轉(zhuǎn)換成List集合。ParameterHandler: 負(fù)責(zé)對(duì)用戶傳遞的參數(shù)轉(zhuǎn)換成JDBC Statement 所需要的參數(shù)ResultSetHandler: 負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對(duì)象轉(zhuǎn)換成List類型的集合;TypeHandler: 負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的映射和轉(zhuǎn)換MappedStatement: MappedStatement維護(hù)了一條節(jié)點(diǎn) 的封裝SqlSource: 負(fù)責(zé)根據(jù)用戶傳遞的parameterObject,動(dòng)態(tài)地生成SQL語(yǔ)句,將信息封裝到 BoundSql對(duì)象中,并返回BoundSql: 表示動(dòng)態(tài)生成的SQL語(yǔ)句以及相應(yīng)的參數(shù)信息Configuration: MyBatis所有的配置信息都維持在Configuration對(duì)象之中。
到此這篇關(guān)于MyBatis 二級(jí)緩存實(shí)現(xiàn)機(jī)制的示例解析的文章就介紹到這了,更多相關(guān)MyBatis 二級(jí)緩存機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot+Redis執(zhí)行l(wèi)ua腳本的項(xiàng)目實(shí)踐
本文主要介紹了Springboot+Redis執(zhí)行l(wèi)ua腳本的項(xiàng)目實(shí)踐,詳細(xì)的介紹Redis與Lua腳本的結(jié)合應(yīng)用,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
MyBatis-Plus不使用數(shù)據(jù)庫(kù)默認(rèn)值的問(wèn)題及解決
這篇文章主要介紹了MyBatis-Plus不使用數(shù)據(jù)庫(kù)默認(rèn)值的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
eclipse自動(dòng)創(chuàng)建SpringBoot項(xiàng)目報(bào)錯(cuò)的解決
這篇文章主要介紹了eclipse自動(dòng)創(chuàng)建SpringBoot項(xiàng)目報(bào)錯(cuò)的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
java使用DelayQueue實(shí)現(xiàn)延時(shí)任務(wù)
項(xiàng)目中經(jīng)常會(huì)用到類似一些需要延遲執(zhí)行的功能,比如緩存,java提供了DelayQueue來(lái)很輕松的實(shí)現(xiàn)這種功能,下面小編就來(lái)和大家介紹一下如何使用DelayQueue實(shí)現(xiàn)延時(shí)任務(wù)吧2023-10-10
spring boot加載第三方j(luò)ar包的配置文件的方法
本篇文章主要介紹了spring boot加載第三方j(luò)ar包的配置文件的方法,詳細(xì)的介紹了spring boot jar包配置文件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
mybatis查詢數(shù)據(jù)賦值到model里面為空的解決
這篇文章主要介紹了mybatis查詢數(shù)據(jù)賦值到model里面為空的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01

