簡(jiǎn)介
上一章我們大概了解了MyBatis初始化過程纳本,本章主要了解SqlSessionFactoryBuilder开镣、Configuration焕济,它是構(gòu)建SqlSessionFactory的主要工具嘴瓤,所有MyBatis配置信息都可以在Configuration中找到,SqlSessionFactoryBuilder的主要作用作用就是構(gòu)建Configuration汰蜘,然后使用Configuration構(gòu)建SqlSessionFactory。
SqlSessionFactoryBuilder 類
public class SqlSessionFactoryBuilder
它不繼承任何類腮猖,也不實(shí)現(xiàn)任何接口鉴扫,內(nèi)部只有一些build方法
SqlSessionFactoryBuilder 方法
這里省略多態(tài)方法赞枕,所有方法最終都只是調(diào)用build(Configuration config) 方法初始化
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 加載mybatis.xml文件澈缺、校驗(yàn),初始化Configuration
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// parser.parse()解析并加載mapper炕婶、解析mapper姐赡、構(gòu)建代理類(沒有初始化)
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {}
}
}
使用Reader初試化的有4個(gè)方法,其他三個(gè)是多態(tài)方法被省略柠掂,Reader一定不能為空
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 加載mybatis.xml文件项滑、校驗(yàn),初始化Configuration
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// parser.parse()解析并加載mapper涯贞、解析mapper枪狂、構(gòu)建代理類(沒有初始化)
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {}
}
}
使用InputStream初試化的有4個(gè)方法,其他三個(gè)是多態(tài)方法被省略宋渔,InputStream一定不能為空
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
無論是通過Reader還是InputStream最終都調(diào)用build(Configuration config)初始化
這里可以看出SqlSessionFactoryBuilder結(jié)構(gòu)還是很簡(jiǎn)單的州疾,至于詳細(xì)的XMLConfigBuilder下一章節(jié)中主要盤它。
Configuration 類
public class Configuration
Configuration主要存放配置信息皇拣,所有mybatis.xml和mapper.xml解析后都會(huì)在這里面严蓖,甚至mapper代理工廠,sql緩存器氧急,jdbc跟java類型對(duì)照表等颗胡,太多了看完屬性你就明白了
Configuration 屬性
// 環(huán)境(dev、test吩坝、gray毒姨、prod等自己配置)
protected Environment environment;
// 允許在嵌套語句中使用分頁(RowBounds)
protected boolean safeRowBoundsEnabled;
// 允許在嵌套語句中使用分頁(ResultHandler)
protected boolean safeResultHandlerEnabled = true;
// 是否開啟自動(dòng)駝峰命名規(guī)則映射
protected boolean mapUnderscoreToCamelCase;
// 和lazyLoadingEnabled配合使用。lazyLoadingEnabled為true時(shí)钉寝,
// aggressiveLazyLoading為true則加載所有懶加載對(duì)象手素,
// 為false則按需加載懶加載對(duì)象
protected boolean aggressiveLazyLoading;
// 延時(shí)加載
protected boolean lazyLoadingEnabled = false;
// 是否允許返回多個(gè)結(jié)果集
protected boolean multipleResultSetsEnabled = true;
// 是否允許JDBC 生成主鍵
protected boolean useGeneratedKeys;
// 使用列標(biāo)簽代替列名
protected boolean useColumnLabel = true;
// 是否開啟全局二級(jí)緩存,如果配置為false瘩蚪,
// 會(huì)覆蓋各個(gè)Mapper文件中的cache配置
protected boolean cacheEnabled = true;
// 指定當(dāng)結(jié)果集中值為 null 的時(shí)候是否調(diào)用映射對(duì)象的
// setter(map 對(duì)象時(shí)為 put)方法泉懦,這對(duì)于有
// Map.keySet() 依賴或 null 值初始化的時(shí)候是有用的。
// 注意原始類型(int疹瘦、boolean等)是不能設(shè)置成 null 的崩哩。
protected boolean callSettersOnNulls;
// 允許SQL語句中使用mapper接口聲明的變量名稱,前提
// 是你的項(xiàng)目是用Java編譯的,并且在mapper接口的方法
// 上使用@param
protected boolean useActualParamName = true;
// 當(dāng)結(jié)果集為NULL的時(shí)候返回null邓嘹。設(shè)置為true酣栈,返回空屬性實(shí)例
protected boolean returnInstanceForEmptyRow;
// 指定 MyBatis 增加到日志名稱的前綴
protected String logPrefix;
// 指定 MyBatis 所用日志的具體實(shí)現(xiàn),未指定時(shí)將自動(dòng)查找汹押。
protected Class<? extends Log> logImpl;
// 指定VFS的實(shí)現(xiàn)類
protected Class<? extends VFS> vfsImpl;
// 一級(jí)緩存矿筝,mybatis利用本地緩存來防止重復(fù)查詢、加速
// 重復(fù)的內(nèi)嵌查詢棚贾。默認(rèn)的作用域是Session窖维,一次回話中的
// 所有查詢都會(huì)被緩存。當(dāng)作用域是STATEMENT時(shí)妙痹,本地緩
// 存僅作用域語句執(zhí)行上铸史,即便是同一會(huì)話的不同的調(diào)用,
// 也不會(huì)有任何數(shù)據(jù)會(huì)共享
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
// 沒有指定jdbcType的參數(shù)怯伊,其返回值為NULL時(shí)琳轿,指定 jdbcType。
// 某些驅(qū)動(dòng)需要指定jdbcType耿芹,其他驅(qū)動(dòng)直接用一般類型即可崭篡,比如 NULL、VARCHAR 或 OTHER吧秕。
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
// 指定某些方法懶加載
protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
// SQL執(zhí)行超時(shí)時(shí)間
protected Integer defaultStatementTimeout;
// 設(shè)置最大的抓取數(shù)量琉闪,會(huì)被query方法上設(shè)置的覆蓋
protected Integer defaultFetchSize;
// 默認(rèn)滾動(dòng)策略
protected ResultSetType defaultResultSetType;
// 配置默認(rèn)的執(zhí)行器。SIMPLE執(zhí)行器沒有什么特別之處寇甸。
// REUSE執(zhí)行器重用預(yù)處理語句塘偎。BATCH執(zhí)行器重用語句和批量更新
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
// 指定MyBatis如何自動(dòng)映射列到字段/屬性。PARTIAL只會(huì)自動(dòng)映射簡(jiǎn)單拿霉,
// 沒有嵌套的結(jié)果吟秩。FULL會(huì)自動(dòng)映射任意復(fù)雜的結(jié)果(嵌套的或其他情況)
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
// 當(dāng)檢測(cè)出未知列(或未知屬性)時(shí),如何處理绽淘,默認(rèn)情況下沒有任何提示涵防,
// 這在測(cè)試的時(shí)候很不方便,不容易找到錯(cuò)誤沪铭。 NONE : 不做任何處理
// (默認(rèn)值) WARNING : 警告日志形式的詳細(xì)信息 FAILING : 映射失敗壮池,拋出異常和詳細(xì)信息
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
// settings下的properties屬性
protected Properties variables = new Properties();
// 反射工廠(MyBatis 反射的核心類Reflector)
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
// 對(duì)象工廠, 所有的類resultMap類都需要依賴于對(duì)象工廠來實(shí)例化
protected ObjectFactory objectFactory = new DefaultObjectFactory();
// MyBatis 提供在構(gòu)造對(duì)象的時(shí)候,對(duì)于指定的對(duì)象進(jìn)行特殊的加工
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
// 代理工廠杀怠,跟CglibProxyFactory一樣
protected ProxyFactory proxyFactory = new JavassistProxyFactory();
// 數(shù)據(jù)源ID
protected String databaseId;
// 指定一個(gè)提供Configuration實(shí)例的類. 這個(gè)被返回的Configuration實(shí)例是
// 用來加載被反序列化對(duì)象的懶加載屬性值. 這個(gè)類必須包含一個(gè)簽名方法
// static Configuration getConfiguration(). (從 3.2.3 版本開始)
protected Class<?> configurationFactory;
// Mapper注冊(cè)器
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
// 攔截器
protected final InterceptorChain interceptorChain = new InterceptorChain();
// typeHandler注冊(cè)器
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
// 類型別名注冊(cè)器
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
// 方言驅(qū)動(dòng)注冊(cè)器
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
// 所有mapper節(jié)點(diǎn)集合椰憋,一個(gè)MappedStatement對(duì)應(yīng)一個(gè)mapper所有節(jié)點(diǎn)
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
// 二級(jí)緩存,又叫自定義緩存赔退,實(shí)現(xiàn)了Cache接口的類都可以作為二級(jí)緩存橙依,
// 所以可配置如encache等的第三方緩存证舟。二級(jí)緩存以namespace名稱空間為其唯一標(biāo)識(shí)
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
// 所有resultMap
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
// 參數(shù)集合
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節(jié)點(diǎn)
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<>();
// 映射包含緩存-引用關(guān)系
protected final Map<String, String> cacheRefMap = new HashMap<>();
Configuration 屬性相當(dāng)?shù)亩啵桓掖虬倍紝?duì)窗骑。[捂臉][捂臉][捂臉]
Configuration 構(gòu)造函數(shù)
public Configuration(Environment environment) {
// 調(diào)用無參構(gòu)造函數(shù)
this();
// 初始化環(huán)境
this.environment = environment;
}
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
這里內(nèi)置了很多類型別名
Configuration重要方法
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);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
Mapper的添加和讀取女责,后面將mapper解析會(huì)用到
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 {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
SIMPLE執(zhí)行器,簡(jiǎn)單執(zhí)行器的實(shí)現(xiàn)非常的簡(jiǎn)單创译,我們就不展開詳述了抵知。
REUSE執(zhí)行器,REUSE和SIMPLE在doUpdate/doQuery上有個(gè)差別软族,不再是每執(zhí)行一個(gè)語句就close掉了刷喜,而是盡可能的根據(jù)SQL文本進(jìn)行緩存并重用,但是由于數(shù)據(jù)庫服務(wù)器端通常對(duì)每個(gè)連接以及全局的語句(oracle稱為游標(biāo))handler的數(shù)量有限制互订,oracle中是open_cursors參數(shù)控制吱肌,mysql中是mysql_stmt_close參數(shù)控制痘拆,這就會(huì)導(dǎo)致如果sql都是靠if各種拼接出來仰禽,日積月累可能會(huì)導(dǎo)致數(shù)據(jù)庫資源耗盡。其是否有足夠價(jià)值纺蛆,視創(chuàng)建Statement語句消耗的資源占整體資源的比例吐葵、以及一共有多少完全不同的Statement數(shù)量而定,一般來說桥氏,純粹的OLTP且非自動(dòng)生成的sqlmap温峭,它會(huì)比SIMPLE執(zhí)行器更好。
BATCH執(zhí)行器字支,批量執(zhí)行器是JDBC Statement.addBatch的實(shí)現(xiàn),對(duì)于批量insert而言比如導(dǎo)入大量數(shù)據(jù)的ETL,驅(qū)動(dòng)器如果支持的話凤藏,能夠大幅度的提高DML語句的性能(首先最重要的是,網(wǎng)絡(luò)交互就大幅度減少了)堕伪,比如對(duì)于mysql而言揖庄,在5.1.13以上版本的驅(qū)動(dòng),在連接字符串上rewriteBatchedStatements參數(shù)也就是jdbc:mysql://localhost:3306/testdb?rewriteBatchedStatements=true后欠雌,性能可以提高幾十倍