mybatis原始碼解讀:session包
技術標籤:mybaits原始碼mybatis
session包是整個mybatis應用的對外介面包。
1.SqlSession及其相關類
1.1 SqlSession的生成鏈
在進行查詢操作時,只需要和SqlSession物件打交道,而SqlSession物件是由SqlSessionFactory生產出來的,而SqlSessionFactory又是由SqlSessionFactoryBuilder建立的。
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(Reader reader) { return build(reader, null, null); } public SqlSessionFactory build(Reader reader, String environment) { return build(reader, environment, null); } public SqlSessionFactory build(Reader reader, Properties properties) { return build(reader, null, properties); } /** * 建造一個SqlSessionFactory物件 * @param reader 讀取字元流的抽象類 * @param environment 環境資訊 * @param properties 配置資訊 * @return SqlSessionFactory物件 */ public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { // 傳入配置檔案,建立一個XMLConfigBuilder類 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); // 分兩步: // 1、解析配置檔案,得到配置檔案對應的Configuration物件 // 2、根據Configuration物件,獲得一個DefaultSqlSessionFactory return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { } } } public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { } } } /** * 根據配置資訊建造一個SqlSessionFactory物件 * @param config 配置資訊 * @return SqlSessionFactory物件 */ public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
DefaultSqlSessionFactory物件可以創建出SqlSession的子類DefualtSqlSession類的物件,該過程由openSessionFromDataSourcce方法完成。
/** * 從資料來源中獲取SqlSession物件 * @param execType 執行器型別 * @param level 事務隔離級別 * @param autoCommit 是否自動提交事務 * @return SqlSession物件 */ 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); // 建立DefaultSqlSession物件 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
至此,整個SqlSession生成鏈的相關操作,經過逐級生成後終於得到DefaultSqlSession物件。
1.2 DefaultSqlSessio類
DefaultSqlSessio類的主要工作是把介面包的工作交給執行器包處理。
public class DefaultSqlSession implements SqlSession { // 配置資訊 private final Configuration configuration; // 執行器 private final Executor executor; // 是否自動提交 private final boolean autoCommit; // 快取是否已經被汙染 private boolean dirty; // 遊標列表 private List<Cursor<?>> cursorList; /** * 查詢結果列表 * @param <E> 返回的列表元素的型別 * @param statement SQL語句 * @param parameter 引數物件 * @param rowBounds 翻頁限制條件 * @return 結果物件列表 */ @Override 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(); } } }
DefaultSqlSession類將主要操作都交給屬性中的Executor物件處理,相關資料庫查詢操作都有Executor物件的query方法來完成。
1.3SqlSessionManager類
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
// 構造方法中傳入的SqlSessionFactory物件
private final SqlSessionFactory sqlSessionFactory;
// 在構造方法中建立的SqlSession代理物件
private final SqlSession sqlSessionProxy;
// 該變數用來儲存被代理的SqlSession物件
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();
/**
* SqlSessionManager構造方法
* @param sqlSessionFactory SqlSession工廠
*/
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 嘗試從當前執行緒中取出SqlSession物件
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) { // 當前執行緒中確實取出了SqlSession物件
try {
// 使用取出的SqlSession物件進行操作
return method.invoke(sqlSession, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else { // 當前執行緒中還沒有SqlSession物件
// 使用屬性中的SqlSessionFactory物件建立一個SqlSession物件
try (SqlSession autoSqlSession = openSession()) {
try {
// 使用新建立的SqlSession物件進行操作
final Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
return result;
} catch (Throwable t) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
}
}
}
SqlSessionManager類主要提供產品複用功能。工廠生成的產品可以放入執行緒ThreadLocal儲存,從而實現產品的複用,這樣即保證了執行緒安全又提升了效率。
1.4Configuration類
配置檔案mybatis-config.xml是mybatis配置主入口,該配置檔案的資訊經過解析後都存入了Configuration物件中,因此Configuration類包含了mybatis執行的所有配置資訊。而且Configuration類還對配置資訊進行了進一步加工,為許多配置項設定了預設值,為許多實體類定義了別名等。
public class Configuration {
// <environment>節點的資訊
protected Environment environment;
// 以下為<settings>節點中的配置資訊
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase;
protected boolean aggressiveLazyLoading;
protected boolean multipleResultSetsEnabled = true;
protected boolean useGeneratedKeys;
protected boolean useColumnLabel = true;
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls;
protected boolean useActualParamName = true;
protected boolean returnInstanceForEmptyRow;
protected String logPrefix;
protected Class<? extends Log> logImpl;
protected Class<? extends VFS> vfsImpl;
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
protected Integer defaultStatementTimeout;
protected Integer defaultFetchSize;
protected ResultSetType defaultResultSetType;
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
// 以上為<settings>節點中的配置資訊
// <properties>節點資訊
protected Properties variables = new Properties();
// 反射工廠
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
// 物件工廠
protected ObjectFactory objectFactory = new DefaultObjectFactory();
// 物件包裝工廠
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
// 是否啟用懶載入,該配置來自<settings>節點
protected boolean lazyLoadingEnabled = false;
// 代理工廠
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
// 資料庫編號
protected String databaseId;
// 配置工廠,用來建立用於載入反序列化的未讀屬性的配置。
protected Class<?> configurationFactory;
// 對映登錄檔
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
// 攔截器鏈(用來支援外掛的插入)
protected final InterceptorChain interceptorChain = new InterceptorChain();
// 型別處理器登錄檔,內建許多,可以通過<typeHandlers>節點補充
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
// 類型別名登錄檔,內建許多,可以通過<typeAliases>節點補充
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
// 語言驅動登錄檔
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
// 對映的資料庫操作語句
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
// 快取
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
// 結果對映,即所有的<resultMap>節點
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
// 引數對映,即所有的<parameterMap>節點
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
// 主鍵生成器對映
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
// 載入的資源,例如對映檔案資源
protected final Set<String> loadedResources = new HashSet<>();
// SQL語句片段,即所有的<sql>節點
protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
// 暫存未處理完成的一些節點
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
// 用來儲存跨namespace的快取共享設定
protected final Map<String, String> cacheRefMap = new HashMap<>();
}
為了便於配置資訊的快速查詢,Configuration類中還設定了一個內部類StrictMap,是HashMap的子類。
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> conflictMessageProducer;
/**
* 向Map中寫入鍵值對
* @param key 鍵
* @param value 值
* @return 舊值,如果不存在舊值則為null。因為StrictMap不允許覆蓋,則只能返回null
*/
@Override
@SuppressWarnings("unchecked")
public V put(String key, V value) {
if (containsKey(key)) {
//如果已經存在此key了,直接報錯
throw new IllegalArgumentException(name + " already contains value for " + key
+ (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
}
if (key.contains(".")) {
// 例如key=“com.github.yeecode.clazzName”,則shortName = “clazzName”,即獲取一個短名稱
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
// 以短名稱為鍵,放置一次
super.put(shortKey, value);
} else {
// 放入該物件,表示短名稱會引發歧義
super.put(shortKey, (V) new Ambiguity(shortKey));
}
}
// 以長名稱為鍵,放置一次
return super.put(key, value);
}
@Override
public V get(Object key) {
V value = super.get(key);
if (value == null) {
throw new IllegalArgumentException(name + " does not contain value for " + key);
}
if (value instanceof Ambiguity) {
throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
+ " (try using the full name including the namespace, or rename one of the entries)");
}
return value;
}
// 含糊,是說短名稱指代不明,引發歧義
// 因此,只要拿到該型別的value,說明:
// 1,使用者一定使用了shortName進行了查詢
// 2, 這個shortName存在重名
protected static class Ambiguity {
final private String subject;
public Ambiguity(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
}
private String getShortName(String key) {
final String[] keyParts = key.split("\\.");
return keyParts[keyParts.length - 1];
}
}
歡迎關注本人公眾號: