框架 - Mybatis 源碼一步步深入(二)

簡(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后欠雌,性能可以提高幾十倍

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蹄梢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子富俄,更是在濱河造成了極大的恐慌禁炒,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霍比,死亡現(xiàn)場(chǎng)離奇詭異幕袱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)悠瞬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門们豌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事玛痊√保” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵擂煞,是天一觀的道長(zhǎng)混弥。 經(jīng)常有香客問我,道長(zhǎng)对省,這世上最難降的妖魔是什么蝗拿? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蒿涎,結(jié)果婚禮上哀托,老公的妹妹穿的比我還像新娘。我一直安慰自己劳秋,他們只是感情好仓手,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著玻淑,像睡著了一般嗽冒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上补履,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天添坊,我揣著相機(jī)與錄音,去河邊找鬼箫锤。 笑死贬蛙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谚攒。 我是一名探鬼主播阳准,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼五鲫!你這毒婦竟也來了溺职?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤位喂,失蹤者是張志新(化名)和其女友劉穎浪耘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塑崖,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡七冲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了规婆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澜躺。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝉稳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掘鄙,到底是詐尸還是另有隱情耘戚,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布操漠,位于F島的核電站收津,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏浊伙。R本人自食惡果不足惜撞秋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嚣鄙。 院中可真熱鬧吻贿,春花似錦、人聲如沸哑子。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赵抢。三九已至剧蹂,卻和暖如春声功,著一層夾襖步出監(jiān)牢的瞬間烦却,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工先巴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留其爵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓伸蚯,卻偏偏與公主長(zhǎng)得像摩渺,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剂邮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容