天天看點

Mybatis——初始化過程,基本原理,配置檔案詳解

目錄

1、mybatis 基本原理

2、Mybatis 配置檔案

2.1、properties 配置

2.2、settings 配置

2.3、typeAliases 别名配置

2.4、typeHandlers 類型處理器配置

2.5、objectFactory 對象工廠

2.6、插件

2.7、environments 資料源配置

2.8、databaseIdProvider 資料庫廠商辨別

2.9、mappers 配置映射檔案

1、mybatis 基本原理

在項目裡需要配置 Mybatis.xml 配置檔案,裡面包含了各種相關的配置資訊(比如,資料庫url,連接配接使用者名、密碼、連接配接池等等),

1、讀取配置檔案

InputStream input = Resources.getResourceAsStream("mybatis.xml");  将檔案以輸入位元組流的形式打開,可以通過 input 去以位元組流的形式擷取檔案裡的資訊。

2、擷取sqlsession工廠

SqlSessionFactoryBuilder 類的作用就是用于擷取指定的 mybatis 配置資訊,然後建立 SqlSessionFactory,是以,SqlSessionFactoryBuilder 的作用僅在于此,是以聲明周期短,建立完了,基本就不用了。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);

SqlSessionFactory 是接口,真正的實作類是 DefaultSqlSessionFactory 類,DefaultSqlSessionFactory 的作用就是用于建立 SqlSession ,DefaultSqlSessionFactory 内部包含了一個 Configuration 對象(final 修飾的),這個對象就是Mybatis.xml 裡面的配置資訊。 建立出來的 SqlSession 對象都包含了這個 Configuration 對象的引用,是以 SqlSession 對象才知道連接配接哪個資料庫,怎麼連接配接等等。

Configuration 類中還包含了所有 mapper.xml 的資訊,将 xml 中的資訊封裝成代理對象,Configuration 就用一個Map 結構存儲(這叫 Mapper 注冊),key 是指 mapper 接口, value 就是這個代理對象了,是以雖然我們 mapper 是接口,在調用接口的某個方法時,實際上是在Map 結構中找到對應的代理對象,調用的是代理對象的具體 sql 執行。

SqlSessionFactory 生命周期是整個Mybatis的生命周期,如果項目裡有多個資料庫,每個資料庫應該有一個SqlSessionFactory 對象,且 SqlSessionFactory 是單例模式。   資料庫的最大連接配接數有限,如果一個資料庫對應多個 SqlSessionFactory 對象的話,會導緻連接配接不夠用(因為這些SqlSessionFactory 建立很多 SqlSession ),而且也不好管理。

3、SqlSession

SqlSession 是一個接口,真正的實作類預設是  DefaultSqlSession 類。 相當于對資料庫的一次連接配接。

DefaultSqlSession 内部包含了 各種 select 方法,update 方法,insert 方法,delete 方法,這些方法都是要指定 sql 字元串的,基本上和 jdbc 差不多。 但是有個 getMapper(Class<T> type)  ,參數是指定 Mapper 接口的,比如 

RoleMapper rolemapper = sqlSession.getMapper(RoleMapper.class);

明明是個接口,怎麼就有執行個體化對象了呢?其實是用 java 反射機制實作的, mapper.xml 檔案中定義了各種 sql 語句,而且和 mapper 接口相對應,是以可以根據 mapper.class 的類型,根據 xml 中的 sql 語句動态地生成一個執行個體化對象,rolemapper 調用相應的方法,就會将相應的 sql 語句發送給資料庫了。

SqlSession 的生命周期很短,如果在一個使用者線程中,如果将幾個sql語句定義為一個事務,那麼隻需要建立一個 SqlSession 對象,然後利用這個對象将幾個sql語句都發送給資料庫,然後接收傳回結果,用完了之後 SqlSession 會被銷毀。但是如果沒有定義事務,則調用了多少次 mapper 接口,就建立多少個 SqlSession 對象,用完之後也要銷毀。

Mybatis——初始化過程,基本原理,配置檔案詳解

4、Configuration 類

Configuration 類是非常重要的,基本上 mybatis 配置的各種資訊都存儲在這個類裡面,

public class Configuration {
    protected Environment environment; //資料庫環境資訊
    protected boolean safeRowBoundsEnabled; 
    protected boolean safeResultHandlerEnabled;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel;
    protected boolean cacheEnabled;
    protected boolean callSettersOnNulls;
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected LocalCacheScope localCacheScope;
    protected JdbcType jdbcTypeForNull;
    protected Set<String> lazyLoadTriggerMethods;
    protected Integer defaultStatementTimeout;
    protected ExecutorType defaultExecutorType;
    protected AutoMappingBehavior autoMappingBehavior;
    protected Properties variables; // 存儲了properties 變量資訊
    protected ObjectFactory objectFactory;
    protected ObjectWrapperFactory objectWrapperFactory;
    protected MapperRegistry mapperRegistry; // 存儲了所有的<mapper接口,mapper代理對象>
    protected boolean lazyLoadingEnabled;
    protected ProxyFactory proxyFactory; // 用于生産mapper接口的代理類
    protected String databaseId;
    protected Class<?> configurationFactory;
    protected final InterceptorChain interceptorChain;
    protected final TypeHandlerRegistry typeHandlerRegistry; // 類型處理器注冊器,存儲了所有類型處理器,<類型,類型處理器>,類型可以是java類型和JDBC 類型,必須配對,同一個類型處理器,必須對應一個java類型和一個jdbc類型
    protected final TypeAliasRegistry typeAliasRegistry; // 别名注冊器,存儲了所有的别名,<别名,真實類型>
    protected final LanguageDriverRegistry languageRegistry;
    protected final Map<String, MappedStatement> mappedStatements;
    protected final Map<String, Cache> caches;
    protected final Map<String, ResultMap> resultMaps;
    protected final Map<String, ParameterMap> parameterMaps;
    protected final Map<String, KeyGenerator> keyGenerators;
    protected final Set<String> loadedResources;
    protected final Map<String, XNode> sqlFragments;
    protected final Collection<XMLStatementBuilder> incompleteStatements;
    protected final Collection<CacheRefResolver> incompleteCacheRefs;
    protected final Collection<ResultMapResolver> incompleteResultMaps;
    protected final Collection<MethodResolver> incompleteMethods;
    protected final Map<String, String> cacheRefMap;
}
           

2、Mybatis 配置檔案

基本标簽結構如下:

Mybatis——初始化過程,基本原理,配置檔案詳解

2.1、properties 配置

2.1.1、直接在Mybatis-config.xml 檔案中配置變量。

用于聲明變量,在配置檔案裡聲明了之後,其它标簽就可以直接使用了($符号引用即可)。舉個例子。

<properties>
    <property name = "url" value = "jdbc:mysql://localhost:3306/database1"/>
</properties>
<environments>
    <environment id = "database1">
        <transactionManager type = "JDBC"/>
        <dataSource type = "POOLED">
            <property name = "url" value = "${url}"/>
        </dataSource>
    </environment>
</environments>
           

 2.1.2、在 properties 檔案中配置變量。

建立一個檔案 mybatis.properties ,在檔案中聲明變量。舉個例子。

檔案内容為  url=jdbc:mysql://localhost:3306/database1

然後再 mybatis-config.xml 配置檔案中引入,<properties resource = "mybatis.properties"/> 即可,然後再mybatis-config.xml 中的其它标簽裡就可以直接用 $ 符号引用變量了。 

2.2、settings 配置

cacheEnabled 全局性地開啟或關閉所有映射器配置檔案中已配置的任何緩存。 true | false true
lazyLoadingEnabled 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設定 fetchType 屬性來覆寫該項的開關狀态。 true | false false
aggressiveLazyLoading 開啟時,任一方法的調用都會加載該對象的所有延遲加載屬性。 否則,每個延遲加載屬性會按需加載(參考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中預設為 true)
multipleResultSetsEnabled 是否允許單個語句傳回多結果集(需要資料庫驅動支援)。 true | false true
useColumnLabel 使用列标簽代替列名。實際表現依賴于資料庫驅動,具體可參考資料庫驅動的相關文檔,或通過對比測試來觀察。 true | false true
useGeneratedKeys 允許 JDBC 支援自動生成主鍵,需要資料庫驅動支援。如果設定為 true,将強制使用自動生成主鍵。盡管一些資料庫驅動不支援此特性,但仍可正常工作(如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 應如何自動映射列到字段或屬性。 NONE 表示關閉自動映射;PARTIAL 隻會自動映射沒有定義嵌套結果映射的字段。 FULL 會自動映射任何複雜的結果集(無論是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior 指定發現自動映射目标未知列(或未知屬性類型)的行為。
  • NONE: 不做任何反應
  • WARNING: 輸出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等級必須設定為 WARN)
  • FAILING: 映射失敗 (抛出 SqlSessionException)
NONE, WARNING, FAILING NONE
defaultExecutorType 配置預設的執行器。SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(PreparedStatement); BATCH 執行器不僅重用語句還會執行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 設定逾時時間,它決定資料庫驅動等待資料庫響應的秒數。 任意正整數 未設定 (null)
defaultFetchSize 為驅動的結果集擷取數量(fetchSize)設定一個建議值。此參數隻可以在查詢設定中被覆寫。 任意正整數 未設定 (null)
defaultResultSetType 指定語句預設的滾動政策。(新增于 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未設定) 未設定 (null)
safeRowBoundsEnabled 是否允許在嵌套語句中使用分頁(RowBounds)。如果允許使用則設定為 false。 true | false False
safeResultHandlerEnabled 是否允許在嵌套語句中使用結果處理器(ResultHandler)。如果允許使用則設定為 false。 true | false True
mapUnderscoreToCamelCase 是否開啟駝峰命名自動映射,即從經典資料庫列名 A_COLUMN 映射到經典 Java 屬性名 aColumn。 true | false False
localCacheScope MyBatis 利用本地緩存機制(Local Cache)防止循環引用和加速重複的嵌套查詢。 預設值為 SESSION,會緩存一個會話中執行的所有查詢。 若設定值為 STATEMENT,本地緩存将僅用于執行語句,對相同 SqlSession 的不同查詢将不會進行緩存。 SESSION | STATEMENT SESSION
jdbcTypeForNull 當沒有為參數指定特定的 JDBC 類型時,空值的預設 JDBC 類型。 某些資料庫驅動需要指定列的 JDBC 類型,多數情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 OTHER
lazyLoadTriggerMethods 指定對象的哪些方法觸發一次延遲加載。 用逗号分隔的方法清單。 equals,clone,hashCode,toString
defaultScriptingLanguage 指定動态 SQL 生成使用的預設腳本語言。 一個類型别名或全限定類名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler 指定 Enum 使用的預設 TypeHandler 。(新增于 3.4.5) 一個類型别名或全限定類名。 org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 指定當結果集中值為 null 的時候是否調用映射對象的 setter(map 對象時為 put)方法,這在依賴于 Map.keySet() 或 null 值進行初始化時比較有用。注意基本類型(int、boolean 等)是不能設定成 null 的。 true | false false
returnInstanceForEmptyRow 當傳回行的所有列都是空時,MyBatis預設傳回 null。 當開啟這個設定時,MyBatis會傳回一個空執行個體。 請注意,它也适用于嵌套的結果集(如集合或關聯)。(新增于 3.4.2) true | false false
logPrefix 指定 MyBatis 增加到日志名稱的字首。 任何字元串 未設定
logImpl 指定 MyBatis 所用日志的具體實作,未指定時将自動查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未設定
proxyFactory 指定 Mybatis 建立可延遲加載對象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 以上)
vfsImpl 指定 VFS 的實作 自定義 VFS 的實作的類全限定名,以逗号分隔。 未設定
useActualParamName 允許使用方法簽名中的名稱作為語句參數名稱。 為了使用該特性,你的項目必須采用 Java 8 編譯,并且加上 -parameters 選項。(新增于 3.4.1) true | false true
configurationFactory 指定一個提供 Configuration 執行個體的類。 這個被傳回的 Configuration 執行個體用來加載被反序列化對象的延遲加載屬性值。 這個類必須包含一個簽名為static Configuration getConfiguration() 的方法。(新增于 3.2.3) 一個類型别名或完全限定類名。 未設定
shrinkWhitespacesInSql Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. (Since 3.5.5) true | false false

2.3、typeAliases 别名配置

之是以有别名這個機制,是因為有些名字或者類需要很長的字元串才能表達清楚,是以非常麻煩,于是有了别名機制,用簡單易懂的字元串成為長的且複雜的名字。

别名有2種,一種是Mybatis自帶的别名(本身自帶的),另一種是自定義的。

Configuration 類中有個 TypeAliasRegistry 對象,這個對象專門用于注冊别名,如果使用java代碼的方式注冊别名的話,就使用 typeAliasRegistry.registerAlias("别名", 真實名字) 語句就可以完成别名注冊。

但是一般推薦使用xml配置的方式注冊别名,1、單獨的<别名,真名> 一對一地配置,2,掃描某個路徑,為路徑下的所有類預設生成别名,生成的别名就是類名首字母變為小寫。下面分别是兩種方式:

<typeAliases>
    <typeAlias alias = "user" type = "com.example.domain.User"/>
</typeAliases>
           
<typeAliases>
    <package name = "com.example.domain"/>
</typeAliases>
           

掃描的方式來生成别名,有時候容易出現别名重複,可以在某個類名前面用 @Alias("自定義的名字") 來為這個類定一個名字,避免重複。

2.4、typeHandlers 類型處理器配置

我們的背景程式是 java 的,java 的資料類型和 MySQL 資料庫的類型不一樣,這就需要有一個機制來自動地做類型轉換,這個機制就是 typeHandler。

Mybatis 本身自帶有很多類型處理器,基本上可以滿足平時開發的需要,但是就怕遇到有些特殊場景,比如枚舉類型,可能Mybatis 的枚舉轉換規則不是我們想要的,是以我們還需要自定義一個類型處理器,并注冊進 Mybatis 裡面,為我們的特殊類型轉換做工作。

如何自定義類型處理器呢? 1、自定義的類型處理器要實作 TypeHandler<T> 接口,T 是泛型,對應java類型,2、将自定義的類型處理器注冊到 Mybatis 中,怎麼注冊呢,其實就是在 Mybatis-config.xml 檔案中配置好即可,3、按理說,到這個步驟就已經可以使用類型處理器了,因為 Mybatis 會因為 javaType 和 jdbcType 自動比對對應的處理器,但是我們如果要100%确定使用某個類型處理器,最好在 mapper.xml 中顯示指定類型處理器。

如果針對某種類型,Mybatis 自身有一個類型處理器,然後我們再自定義一個類型處理器,Mybatis 會用哪個呢? 用我們自定義的,因為 TypeHandlerRegistry 内部是用 Map 存儲<類型, 類型處理器>的,是以後注冊的類型處理器會覆寫掉之前的類型處理器。

舉個例子,假設資料庫的類型為 XXX,java 類型為 YYY。 類型處理器隻是個空殼子,什麼也沒有實作,這裡隻說明一下怎麼使用。

public class YYYTypeHandler implements TypeHandler<YYY> {
    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, YYY yyy, JdbcType jdbcType) throws SQLException {
        
    }

    @Override
    public YYY getResult(ResultSet resultSet, String s) throws SQLException {
        return null;
    }

    @Override
    public YYY getResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public YYY getResult(CallableStatement callableStatement, int i) throws SQLException {
        return null;
    }
}
           
<typeHandlers>
    <typeHandler javaType = "YYY" jdbcType = "XXX" handler = "com.example.handler.YYYTypeHandler"/>
</typeHandlers>
           

2.5、objectFactory 對象工廠

準确的說,是資料庫傳回的結果集的執行個體對象工廠,不管是查詢資料庫,還是插入資料庫,隻要是有傳回結果的,都需要有一個 java 對象存儲它,但是傳回的資料類型多種多樣,你怎麼知道建立一個什麼樣類型的對象去存儲傳回結果呢?這種場景就是該 java 反射機制出場了,是以,objectFactory 的底層原理就是java反射機制,當然,ObjectFactory 是一個接口,預設實作類是 DefaultObjectFactory。一般來講,我們不需要自定義對象工廠了,預設用 Mybatis 的就可以了。

如果某一天碰到了一個場景,就需要自定義對象工廠,那怎麼辦呢?

1、自定義一個工廠類,實作 ObjectFactory 接口,或者繼承 DefaultObjectFactory 類。

各個方法的實作可以參考 DefaultObjectFactory 類的。 第一個 create 方法是使用預設的構造方法去建立執行個體化對象,第二個 create 方法是可以設定構造方法的參數,使用有參構造方法去建立執行個體化對象, isCollection 方法是判斷要建立的類型是不是集合類型, setProperties 方法是将 <objectFactory> 标簽内的所有 property 内容擷取到,即此方法的參數,然後如何處理這些 property 就是我們自己的事了。 Mybatis 并沒有實作 setProperties 方法,是以,若要想進行一些額外的擴充,就需要自定義對象工廠,重新實作 setProperties 方法。

public class MyObjectFactory implements ObjectFactory {

    @Override
    public void setProperties(Properties properties) {
        
    }

    @Override
    public <T> T create(Class<T> aClass) {
        return null;
    }

    @Override
    public <T> T create(Class<T> aClass, List<Class<?>> list, List<Object> list1) {
        return null;
    }

    @Override
    public <T> boolean isCollection(Class<T> aClass) {
        return false;
    }
}
           

2、在 mybatis-config.xml 配置檔案中配置好我們的自定義對象工廠

<objectFactory type = "com.example.objectFactory.MyObjectFactory">
    這裡面可以定義一些 property
</objectFactory>
           

2.6、插件

 要想把插件弄透徹點,就需要再深入了解 mybatis 的底層原理。

請閱讀部落格:

2.7、environments 資料源配置

environments 是配置資料庫資訊的地方,可以配置多個資料庫的資訊,但是隻能使用其中的一個(除非自己實作了多資料庫的代碼),default 屬性就指定了使用哪一個資料庫,default 的值就是下面 environment 标簽的 id。

下面隻是給了一個簡單的例子,每個 environment 可以配置 2 個屬性,分别是 transactionManager 和 dataSource,其中 dataSource 還可以有很多屬性可以配置的。

<environments default="development">  
    <environment id="development">  
        <transactionManager type="JDBC" />  
        <dataSource type="POOLED">  
            <property name="driver" value="${driver}"/>  
            <property name="url" value="${url}"/>  
            <property name="username" value="${username}"/>  
            <property name="password" value="${password}"/>  
        </dataSource>  
    </environment>  
</environments>
           

 transactionManager 是事務管理器,主要作用就是可以送出事務,復原事務,關閉事務,預設是使用 JDBC 方式,還有另一個方式是 MANAGED,它是将事務交給容器處理。

dataSource 就是配置資料庫資訊的地方了,type 用于指定資料源的類型,有 3 種類型:

1、POOLED表示這是一個帶有連接配接池的資料源,具體用什麼連接配接池,可以自己定,也可以使用spring提供的預設連接配接池

2、UNPOOLED表示這是一個沒有連接配接池的資料源,每次都是建立連接配接,執行完sql,就關閉連接配接

3、JNDI表示這是使用容器裡的資料源,是Tomcat容器中的機制,用于管理資料源,是以mybatis隻需要拿着資料源的id就可以了,因為JNDI能知道你要通路的是哪個資料源,是以跟連接配接池也沒有關系,隻不過tomcat有預設的資料庫連接配接池,若要更改連接配接池,可以研究一下。

另外,資料庫可以配置的資訊非常多,什麼連接配接池相關的配置就一大堆,什麼資料庫連接配接逾時了,異常了,等等。

2.8、databaseIdProvider 資料庫廠商辨別

這是針對不同類型的資料庫,比如我們的項目在開發之前,已經預料到了項目要能夠相容 MySQL 和 Oracle ,即項目直接與Oracle 相連行,也 MySQL 相連也是可以的,一個是 MySQL,另一個是 Oracle,sql 文法不一樣,需要用到的資料庫驅動程式也不一樣,是以,為了解決這個問題,就有了這個資料庫廠商辨別。

舉個例子,需要用到 MySQL 和 Oracle 資料庫,下面就配置上,“Oracle” 和 “MySQL” 是 Mybatis 本身自帶的别名,是以 Mybatis 知道這兩個代表什麼資料庫,“oracle” 和 “mysql” 又是這兩個的資料庫類型的别名,是以我們在指定資料庫類型時,隻需指定“oracle” 或者 “mysql” 即可。

<databaseIdProvider>
    <property name = "Oracle" value = "oracle"/>
    <property name = "MySQL" value = "mysql"/>
</databaseIdProvider>
           

然後在 mapper.xml 映射檔案中寫sql的時候,隻需要指定 sql 标簽的 databaseId 屬性為目标資料庫類型即可,也可以不指定,不指定的話,就會使用預設的資料庫,什麼是預設的資料庫類型呢?environments 标簽已經配置了。

Mybatis 在執行某個 sql 的時候,會通過調用 dao 層接口來實作,按照 dao 層的 mapper 接口,又找到對應的 mapper.xml 映射檔案,根據 mapper 接口裡被調用的方法名,又可以确定執行映射檔案裡哪個 sql 了,此時,很有可能同一個sql 的 id 會有多條 sql,隻是每條 sql 的 databaseId 和 sql 文法不同而已,那麼糾結執行哪些 sql 呢? 如果有比對到 databaseId 的 sql,就隻執行它,如果沒有比對到,即隻有未指定 databaseId 的 sql,那就隻執行它,即按照預設的資料庫類型去執行,如果什麼都沒有比對到,就抛出異常。

databaseIdProvider 隻是解決了應用于不同類型資料庫的相容問題,不是說 Mybatis 可以憑借這個配置同時與多個資料庫互動,它仍然是和一個資料庫在互動。

2.9、mappers 配置映射檔案

配置我們的各種 mappers.xml,配置方式有 4 種。

1、<mappers><mapper resource = "相對路徑"/></mappers> 

2、<mappers><mapper url = "絕對路徑"/></mappers> 

3、<mappers><package name = "包的相對路徑"/></mappers>

4、<mappers><mapper class = "mapper接口的相對路徑"/></mappers>   // 這有個條件,就是此 mapper 接口和對應的 mapper.xml 在同一個檔案夾下面,且接口名和映射檔案名一樣。

繼續閱讀