字節(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)映射沒(méi)有定義嵌套結(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)沒(méi)有設(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)沒(méi)有為參數(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增加到日志名稱的前綴 | 任何字符串 | 沒(méi)有設(shè)置 |
logImpl | 指定 MyBatis所用日志的具體實(shí)現(xiàn)苫幢,未指定時(shí)將自動(dòng)查找 | SLF4J、LOG4J垫挨、JDK LOGGING韩肝、NO LOGGING | 沒(méi)有設(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ì)象中育特,這樣就沒(méi)有必要運(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)掃描包乾戏,將掃描到的類裝載到上下文中。如果配置了包掃描的路徑三热,而沒(méi)有注解@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í)沒(méi)有什么意義,在實(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>