mybaits sqlSession 源碼解讀
SqlSession
org.apache.ibatis.session.SqlSession 是 mybatis 操作sql、 獲取mapper、處理事務的基本單元。一般意義上我們運用mybatis 都是在操作 sqlSession
類圖如下:
- org.apache.ibatis.session.defaults.DefaultSqlSession 默認實現。
- DefaultSqlSession 的初始化過程 SqlSessionFactoryBuilder --> DefaultSqlSessionFactory--> SqlSession
SqlSession模式一
- 所有 <T> T selectOne、<E> List<E> selectList 均由<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)實現。源碼:
[@Override](https://my.oschina.net/u/1162528)
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}
- 源碼分析: 該方法接受 3個參數
- - statement sql語句的標識符,就是常用的 namespace.sqlId組合,具有唯一性
- 通過此id 可以從mybatis配置類org.apache.ibatis.session.Configuration中獲取 映射語句對象MappedStatement, id 跟語句的映射關係儲存在 Configuration 下的內部數據結構(hashmap 字類) Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection") 中 - parameter 顧名思義 要傳遞給statement標識的sql語句的參數對象 - org.apache.ibatis.session.RowBounds 限制對象檢索的界限。比方說 上面2個參數聯合查出來200條,rowBounds的offset 和limit 形成一個區間 [20,150],截取在此區間的數據集。就是mybatis 邏輯分頁 由 org.apache.ibatis.executor.Executor 操作返回查詢結果
[@Override](https://my.oschina.net/u/1162528)
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
SqlSession模式二
sqlSession 操作mapper 介面分析
- 獲取Mapper
public <T> T getMapper(Class<T> type)的底層方法為 Configuration.<T>getMapper(type, this),也就是說獲取Mapper介面是由配置類來完成的,具體是由其持有的 MapperRegistry 來維護。
MapperRegistry中的成員Map<Class<?>, MapperProxyFactory<?>> knownMappers 是Mapper 介面的註冊表,所有註冊的Mapper 將與之對應的MapperProxyFactory存儲在該註冊表中。底層是通過JDK 代理來進行代理
我們的業務介面通過MapperProxy 代理,代理對象通過MapperMethod執行方法
[@Override](https://my.oschina.net/u/1162528)
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 (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 如果是抽象 沒有實現構造 方法的代理
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
流程如下:
- 註冊Mapper
- 在分析了如何獲取Mapper之後,再搞一搞如何註冊Mapper,通過上面知道Mapper的獲取是Configuration委託 MapperRegistry獲取,同樣的流程,提供了三種方式來註冊
// 將指定包下面的指定介面進行載入
public void addMappers(String packageName, Class<?> superType) {
mapperRegistry.addMappers(packageName, superType);
}
//載入指定包下面的所有介面
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
//載入指定的介面
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
- 核心基於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 {
knownMappers.put(type, new MapperProxyFactory<T>(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.
//通過MapperAnnotationBuilder來對xml或者類進行解析
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
- 批量註冊某個包下基於某個超類的 是對上面進行循環遍歷進行
/**
* @since 3.2.2
*/
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
// 判斷包下是否有該超類的實現 沒有會拋出異常 主要起到檢測載入作用
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
// 獲取清單並進行註冊
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
- 註冊包下所有的介面
/**
* @since 3.2.2
*/
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
- 由上面發現 重點是 MapperAnnotationBuilder,該類也十分重要的轉換類,後面會繼續分析
作者:NotFound403
原文:https://my.oschina.net/u/2948566/blog/2995801
著作權歸作者所有
※解讀gcc和g++編譯器分別對c與c++文件影響
※node+express+mongoDB寫簡單介面,Vue獲取介面
TAG:程序員小新人學習 |