字節(jié)跳動(dòng)飛書內(nèi)推!
北京、杭州、武漢、廣州、深圳、上海,六大城市等你來(lái)投。
感興趣的朋友可以私我咨詢&內(nèi)推,也可以通過(guò)鏈接直接投遞!
海量HC,極速響應(yīng),快來(lái)和我成為同事吧。
今日頭條、抖音、Tik Tok也可以內(nèi)推~
點(diǎn)擊進(jìn)入我的博客
MyBatis詳解1.概述
MyBatis詳解2.MyBatis使用入門
MyBatis詳解3.MyBatis配置詳解
MyBatis詳解4.映射器Mapper
MyBatis詳解5.動(dòng)態(tài)SQL
MyBatis詳解6.MyBatis技術(shù)內(nèi)幕
MyBatis詳解7.插件
MyBatis詳解8.集成Spring
1 MyBatis XML配置文件層次結(jié)構(gòu)
以下的圖片展示了MyBatis的全部配置元素

2 properties元素
properties是一個(gè)配置屬性的元素,讓我們能在配置文件的上下文中使用它,MyBatis提供3種配置方式。
- property子元素。
- properties配置文件。
- SqlSessionFactoryBuilder使用Properties文件構(gòu)建。
property子元素
<property name="driver" value="com.mysql.jdbc.Driver"/>
properties配置文件
一般情況下,我們會(huì)使用一個(gè)單獨(dú)的properties配置文件來(lái)配置屬性值,以方便我們?cè)诙鄠€(gè)配置文件中重復(fù)使用它們,也方便日后維護(hù)和隨時(shí)修改。我們可以通過(guò)${key}的形式,取出在配置文件中配置的值。
<configuration>
<!-- 引入配置文件 -->
<properties resource="datasource.properties"/>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 使用配置文件中的屬性 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
</dataSource>
</environment>
</environments>
</configuration>
SqlSessionFactoryBuilder使用Properties文件構(gòu)建
出于安全考慮,properties配置文件中的賬號(hào)密碼等元素可能是加密的,這個(gè)時(shí)候就需要對(duì)加密的元素進(jìn)行處理。
public static void func() throws Exception {
Properties properties = new Properties();
properties.load(Resources.getResourceAsStream("datasource.properties"));
// 對(duì)原賬號(hào)密碼解密
properties.setProperty("username", decode(properties.getProperty("username")));
properties.setProperty("password", decode(properties.getProperty("password")));
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
// SqlSessionFactoryBuilder可以使用一個(gè)InputStream和一個(gè)Properties構(gòu)建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is, properties);
}
三種方式的優(yōu)先級(jí)
MyBatis支持的3種配置方式可能同時(shí)出現(xiàn),并且屬性還會(huì)重復(fù)配置,MyBatis將按照下面的順序來(lái)加載:
- 在properties元素體內(nèi)指定的屬性首先被讀取。
- 根據(jù) properties元素中的resource屬性讀取類路徑下屬性文件,或者根據(jù)ur屬性指定的路徑讀取屬性文件,并覆蓋已讀取的同名屬性。
- 讀取作為build()方法參數(shù)傳遞的屬性,并覆蓋已讀取的同名屬性。
因此,通過(guò)build()方法參數(shù)傳遞的屬性具有最高優(yōu)先級(jí),resource/url屬性中指定的配置文件
次之,最低優(yōu)先級(jí)的是 properties屬性中指定的屬性。因此,我們盡量不要使用混合的方式來(lái)定義配置,首選的方式是使用properties文件。
3 settings設(shè)置
settings(設(shè)置)在MyBatis中是最復(fù)雜的配置,它會(huì)改變 MyBatis運(yùn)行時(shí)的行為。
| 設(shè)置參數(shù) | 描述 | 有效值 | 默認(rèn)值 |
|---|---|---|---|
| cacheEnabled | 該配置影響所有映射器中配置的緩存全局開關(guān) | true、 false | true |
| lazyLoadingEnabled | 延遲加載的全局開關(guān),當(dāng)它開啟時(shí),所有關(guān)聯(lián)對(duì)象都會(huì)延遲加載。特定關(guān)聯(lián)關(guān)系中可通過(guò)設(shè)置fetchType屬性來(lái)覆蓋該項(xiàng)的開關(guān)狀態(tài) | true、false | false |
| aggressiveLazyLoading | 當(dāng)啟用時(shí),對(duì)任意延遲屬性的調(diào)用會(huì)使帶有延遲加載屬性的對(duì)象完整加載;反之,每種屬性將會(huì)按需加載 | true、 false | true |
| multipleresultSetsEnabled | 是否允許單一語(yǔ)句返回多結(jié)果集(需要兼容驅(qū)動(dòng)) | true、 false | true |
| useColumnLabel | 使用列標(biāo)簽代替列名。不同的驅(qū)動(dòng)在這方面會(huì)有不同的表現(xiàn),具體可參考相關(guān)驅(qū)動(dòng)文檔或通過(guò)測(cè)試這兩種不同的模式來(lái)觀察所用驅(qū)動(dòng)的結(jié)果 | true、 false | true |
| useGeneratedKeys | 允許JDBC支持自動(dòng)生成主鍵,需要驅(qū)動(dòng)兼容。如果設(shè)置為true,則這個(gè)設(shè)置強(qiáng)制使用自動(dòng)生成主鍵 | true、fase | false |
| autoMappingBehavior | 指定 MyBatis應(yīng)如何自動(dòng)映射列到字段或?qū)傩裕篘ONE表示取消自動(dòng)映射、PARTIAL只會(huì)自動(dòng)映射沒有定義嵌套結(jié)果集映射的結(jié)果集、FULL會(huì)自動(dòng)映射任意復(fù)雜的結(jié)果集 | NONE、 PARTIAL、FULL | PARTIAL |
| defaultExecutorType | 配置默認(rèn)的執(zhí)行器:SIMPLE是普通的執(zhí)行器、REUSE執(zhí)行器會(huì)重用預(yù)處理語(yǔ)句(prepared statements)、BATCH執(zhí)行器將重用語(yǔ)句并執(zhí)行批量更新 | SIMPLE、 REUSE、SIMPLE | SIMPLE |
| defaultStatementTimeout | 設(shè)置超時(shí)時(shí)間,它決定驅(qū)動(dòng)等待數(shù)據(jù)庫(kù)響應(yīng)的秒數(shù)。當(dāng)沒有設(shè)置的時(shí)候它取的就是驅(qū)動(dòng)默認(rèn)的時(shí)間 | any positive integer | Not set(null) |
| safeRowBoundsEnabled | 允許在嵌套語(yǔ)句中使用分頁(yè)(Row Bounds) | true、 false | False |
| mapUnderscoreToCamelCase | 是否開啟自動(dòng)駝峰命名規(guī)則映射,即從經(jīng)典數(shù)據(jù)庫(kù)列名 A_COLUMN到經(jīng)典Java屬性名 aColumn的類似映射 | true、false | false |
| localCacheScope | MyBatis利用本地緩存機(jī)制(Local Cache)防止循環(huán)引用(circular references)和加速重復(fù)嵌套查詢。默認(rèn)值為 SESSION,這種情況下會(huì)緩存一個(gè)會(huì)話中執(zhí)行的所有查詢。若設(shè)置值為STATEMENT,本地 STATEMENT會(huì)話僅用在語(yǔ)句執(zhí)行上,對(duì)相同 SqlSession的不同調(diào)用將不會(huì)共享數(shù)據(jù) | SESSION、STATEMENT | SESSION |
| jdbcTypeForNull | 當(dāng)沒有為參數(shù)提供特定的JDBC類型時(shí),為空值指定JDBC類型。某些驅(qū)動(dòng)需要指定列的JDBC類型,多數(shù)情況情況直接使用一般類型即可,比如NULL、 VARCHAR、OTHER | JdbcType枚舉 | OTHER |
| lazyLoadTriggerMethods | 指定對(duì)象的方法觸發(fā)一次延遲加載 | 如果是一個(gè)方法列表,我們則用逗號(hào)將它們隔開 | equals, clone, hashCode, toString |
| defaultScriptingLanguage | 指定動(dòng)態(tài)SQL生成的默認(rèn)語(yǔ)言 | 你可以配置類的別名或者類的全限定名 | org....XMLDynamicLanguageRiver |
| callSettersOnNulls | 指定當(dāng)結(jié)果集中值為null的時(shí)候是否調(diào)用映射對(duì)象的setter(map對(duì)象時(shí)為put)方法,這對(duì)于有Map.keySet()依賴或null值初始化的時(shí)候是有用的。注意基本類型是不能設(shè)置成null的 | true、 false | false |
| logPrefix | 指定MyBatis增加到日志名稱的前綴 | 任何字符串 | 沒有設(shè)置 |
| logImpl | 指定 MyBatis所用日志的具體實(shí)現(xiàn),未指定時(shí)將自動(dòng)查找 | SLF4J、LOG4J、JDK LOGGING、NO LOGGING | 沒有設(shè)置 |
| proxyFactory | 指定MyBatis創(chuàng)建具有延遲加載能力的對(duì)象所用到的代理工具 | CGLIB、JAVASSIST | (3.3.0以上版本是)JAVASSIST,否則CGLIB |
4 typeAliases類型命名
別名(typeAliases)是一個(gè)指代的名稱。因?yàn)槲覀冇龅降念惾薅^(guò)長(zhǎng),所以我們希望用一個(gè)簡(jiǎn)短的名稱去指代它,而這個(gè)名稱可以在MyBatis上下文中使用,在 MyBatis中別名是不分大小寫的。一個(gè) typeAliases的實(shí)例是在解析配置文件時(shí)生成的,然后長(zhǎng)期保存在 Configuration對(duì)象中,這樣就沒有必要運(yùn)行的時(shí)候再次生成它的實(shí)例了。
系統(tǒng)定義別名
通過(guò)org.apache.ibatis.type.TypeAliasRegistry可以查看所有系統(tǒng)定義的別名,主要是基本數(shù)據(jù)類型、字符串、基本數(shù)據(jù)類型數(shù)組、日期、容器類。
通過(guò)XML自定義別名
在mybatis-config.xml中通過(guò)<typeAliases>元素可以自定義別名。
<typeAliases>
<typeAlias type="com.ankeetc.spring.domain.UserDomain" alias="user"/>
</typeAliases>
通過(guò)注解的方式自定義別名
<!-- 通過(guò)package掃描包 -->
<typeAliases>
<package name="com.ankeetc.spring.domain"/>
</typeAliases>
@Alias("user")
public class UserDomain {
}
當(dāng)配合上面的配置,MyBatis就會(huì)自動(dòng)掃描包,將掃描到的類裝載到上下文中。如果配置了包掃描的路徑,而沒有注解@Alias的類也會(huì)被MyBatis裝載,MyBatis會(huì)自動(dòng)把類名的第一個(gè)字母變?yōu)樾?/strong>,作為MyBatis的別名。
5 typeHandler類型處理器
MyBatis在預(yù)處理語(yǔ)句(PreparedStatement)中設(shè)置一個(gè)參數(shù)或者從結(jié)果集(ResultSet)中取出一個(gè)值時(shí),都會(huì)用注冊(cè)了的typeHandler進(jìn)行處理。typeHandler常用的配置為Java類型(javaType)、JDBC類型(jdbcType)。typeHandler的作用就是將參數(shù)從javaType轉(zhuǎn)化為 jdbcType,或者從數(shù)據(jù)庫(kù)取出結(jié)果時(shí)把jdbcType轉(zhuǎn)化為javaType。
5.1 系統(tǒng)定義的typeHandler
在org.apache.ibatis.type.TypeHandlerRegistry中MyBatis定義了很多系統(tǒng)內(nèi)部的typeHader,主要是基本類型、字符串、時(shí)間類型、枚舉類型的處理。
5.2 自定義typeHandler
繼承BaseTypeHandler或?qū)崿F(xiàn)TypeHandler
對(duì)于自定義typeHandler的要求是必須實(shí)現(xiàn)接口org.apache.ibatis.type.TypeHandler,更簡(jiǎn)單的方式是繼承已經(jīng)實(shí)現(xiàn)了該接口的實(shí)現(xiàn)類org. apache.ibatis.type.BaseTypeHandler。
確定攔截類型
可以在自定義typeHandler里用注解配置@MappedTypes和@MappedJdbcTypes,也可以在<typeHandler>元素中指定。
-
@MappedTypes定義的是JavaType類型,可以指定哪些Java類型被攔截; -
@MappedJdbcTypes定義的是JdbcType類型,取值范圍在枚舉類org.apache.ibatis.type.JdbcType中找到。
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyStringTypeHandler implements TypeHandler<String> {
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
System.out.println("MyStringTypeHandler#setParameter");
ps.setString(i, parameter);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
System.out.println("MyStringTypeHandler#getResult 1");
return rs.getString(columnName);
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
System.out.println("MyStringTypeHandler#getResult 2");
return rs.getString(columnIndex);
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
System.out.println("MyStringTypeHandler#getResult 3");
return cs.getString(columnIndex);
}
}
啟用自定義的typeHander
- 可以在mybatis-config.xml里面配置,可以選擇配置一個(gè)類或者一個(gè)包。
- 在Mapper.xml的
<resultMap>直接指定typeHandler。 - 在Mapper.xml的參數(shù)中指定typeHandler。
<!-- 1. 在mybatis-config.xml中配置 -->
<typeHandlers>
<typeHandler handler="com.ankeetc.spring.type.MyStringTypeHandler"/>
</typeHandlers>
<!-- 2. 在Mapper.xml中resultMap元素中配置-->
<resultMap id="BaseResultMap" type="com.ankeetc.spring.domain.UserDomain">
<result column="name" property="name" javaType="java.lang.String" jdbcType="VARCHAR" typeHandler="com.ankeetc.spring.type.MyStringTypeHandler"/>
</resultMap>
<!-- 注意必須使用上面的resultMap -->
<select id="select" resultMap="BaseResultMap" parameterType="java.lang.Long">
SELECT * FROM tb_name WHERE id = #{id}
</select>
<!-- 3. 在Mapper.xml參數(shù)中配置-->
<insert id="insert" parameterType="com.ankeetc.spring.domain.UserDomain">
INSERT INTO tb_name (name) VALUES (#{name, jdbcType=VARCHAR, javaType=String, typeHandler=com.ankeetc.spring.type.MyStringTypeHandler})
</insert>
5.3 枚舉類型typeHandler
MyBatis中枚舉類型的typeHandler則有自己特殊的規(guī)則,MyBatis內(nèi)部提供了兩個(gè)轉(zhuǎn)化枚舉類型的typeHandler給我們使用。
- org.apache.ibatis.type.EnumTypeHandler:使用枚舉字符串名稱作為參數(shù)傳遞的
- org.apache.ibatis.type.EnumOrdinalTypeHandler:使用整數(shù)下標(biāo)作為參數(shù)傳遞的
6 objectFactory對(duì)象工廠
當(dāng)MyBatis在構(gòu)建一個(gè)結(jié)果返回的時(shí)候,都會(huì)使用ObjectFactory(對(duì)象工廠)去構(gòu)建POJO,我們可以定制自己的對(duì)象工廠。MyBatis中默認(rèn)的ObjectFactory是由org.apache.ibatis.reflection.factory.DefaultObjectFactory來(lái)提供服務(wù)的。
自定義objectFactory
- 實(shí)現(xiàn)ObjectFactory接口,也可以通過(guò)繼承DefaultObjectFactory擴(kuò)展功能。
- 在mybatis-config.xml中使用
<objectFactory>元素。
7 environments環(huán)境變量
配置環(huán)境可以注冊(cè)多個(gè)環(huán)境,每一個(gè)環(huán)境分為兩大部分:一個(gè)是數(shù)據(jù)庫(kù)源(dataSource)的配置,另外一個(gè)是數(shù)據(jù)庫(kù)事務(wù)(transactionManager)的配置。
7.1 概述
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
</dataSource>
</environment>
</environments>
- default:表示默認(rèn)使用哪個(gè)數(shù)據(jù)源
- id:表示數(shù)據(jù)源的名稱
- transactionManager的事務(wù)類型type一共有三種:JDBC,采用JDBC方式管理事務(wù),獨(dú)立編碼中我們常常使用;MANAGED,采用容器方式管理事務(wù),在JNDI數(shù)據(jù)源中常用;自定義,由使用者自定義數(shù)據(jù)庫(kù)事務(wù)管理辦法,適用于特殊應(yīng)用。
- property元素配置數(shù)據(jù)源的各類屬性
- dataSource的type屬性是提供我們對(duì)數(shù)據(jù)厙連接方式的配置:UNPOOLED(非連接池?cái)?shù)據(jù)庫(kù))、POOLED(連接池?cái)?shù)據(jù)庫(kù))、JNDI(JNDI數(shù)據(jù)源)、自定義數(shù)據(jù)源。
7.2 數(shù)據(jù)庫(kù)事務(wù)
數(shù)據(jù)庫(kù)事務(wù)是交由SqlSession去控制的,我們可以通過(guò)SqlSession提交或者回滾。在大部分的工作環(huán)境下,我們都會(huì)使用 Spring框架來(lái)控制它。
7.3 數(shù)據(jù)源
MyBatis內(nèi)部為我們提供了3種數(shù)據(jù)源的實(shí)現(xiàn)方式:
- UNPOOLED,使用
org.apache.ibatis.datasource.unpooled.UnpooledDataSource實(shí)現(xiàn)。 - POOLED,使用
org.apache.ibatis.datasource.pooled.PooledDataSource實(shí)現(xiàn)。 - JNDI,使用
org.apache.ibatis.datasource.jndi.JndiDataSourceFactory實(shí)現(xiàn)。
7.4 自定義數(shù)據(jù)源
使用自定義數(shù)據(jù)源,它必須實(shí)現(xiàn)org.apache.ibatis.datasource.DataSourceFactory接口。然后在<dataSource>中type屬性指定對(duì)應(yīng)的全限定類名。
public class MyDataSourceFactory implements DataSourceFactory {
DataSource dataSource;
public MyDataSourceFactory() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/test");
druidDataSource.setUsername("root");
dataSource = druidDataSource;
}
private Properties properties;
@Override
public void setProperties(Properties props) {
this.properties = props;
}
@Override
public DataSource getDataSource() {
return dataSource;
}
}
<dataSource type="com.ankeetc.spring.config.MyDataSourceFactory"/>
8 databaseIdProvider數(shù)據(jù)庫(kù)廠商標(biāo)識(shí)
在相同數(shù)據(jù)庫(kù)廠商的環(huán)境下,數(shù)據(jù)庫(kù)廠商標(biāo)識(shí)沒有什么意義,在實(shí)際的應(yīng)用中使用得比較少,因?yàn)槭褂貌煌瑥S商數(shù)據(jù)庫(kù)的系統(tǒng)還是比較少的。 MyBatis可能會(huì)運(yùn)行在不同廠商的數(shù)據(jù)庫(kù)中,它為此提供一個(gè)數(shù)據(jù)庫(kù)標(biāo)識(shí),作用在于指定SQL到對(duì)應(yīng)的數(shù)據(jù)庫(kù)廠商提供的數(shù)據(jù)庫(kù)中運(yùn)行。
8.1 系統(tǒng)默認(rèn)規(guī)則

type="DB VENDOR"是啟動(dòng)MyBatis內(nèi)部注冊(cè)的策略器。首先 MyBatis會(huì)將你的配置讀入Configuration類里面,在連接數(shù)據(jù)庫(kù)后調(diào)用getDatabaseProductName()方法去獲取數(shù)據(jù)庫(kù)的信息,然后用我們配置的name值去做匹配來(lái)得到DatabaseId。
獲得數(shù)據(jù)庫(kù)的Id
可以用下面的代碼來(lái)獲得數(shù)據(jù)庫(kù)的Id:sqlSessionFactory. getConfiguration(). getDatabaseId()
指定數(shù)據(jù)庫(kù)廠商
可以指定SQL在哪個(gè)數(shù)據(jù)庫(kù)廠商執(zhí)行,需要在Mapper.XML中通過(guò)指定。
<insert id="insert" parameterType="com.ankeetc.spring.domain.UserDomain" databaseId="mysql">
INSERT INTO tb_name (name) VALUES (#{name, typeHandler=com.ankeetc.spring.type.MyStringTypeHandler})
</insert>
8.2 自定義規(guī)則
- 實(shí)現(xiàn)
org.apache.ibatis.mapping.DatabaseIdProvider接口 - 配置
<databaseIdProvider>標(biāo)簽。
public class MyDatabaseIdProvider implements DatabaseIdProvider {
private Properties properties;
@Override
public void setProperties(Properties p) {
this.properties = p;
}
@Override
public String getDatabaseId(DataSource dataSource) throws SQLException {
String dbName = dataSource.getConnection().getMetaData().getDatabaseProductName();
return this.properties.getProperty(dbName);
}
}
<databaseIdProvider type="MyDatabaseIdProvider">
<property name="SQL Server" value="sqlserver"/>
<property name="MySQL" value="mysql"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>