MyBatis使用XML映射文件(mybatis的xml映射文件)
1.概述
這一次想把MyBatis的XML聲明SQL的方式大概說一下。使用的demo可以參考:
《spring boot整合Mybatis3.5.4使用XML定義SQL》
MyBatis可以通過注解使用聲明,也可以xml文件來聲明SQL。前者簡(jiǎn)單,不靈活,后者不僅方便靈活,還方便優(yōu)。通過XML來編寫映射的SQL也是MyBatis所推薦的。MyBatis的一個(gè)映射器類就對(duì)象一個(gè)xml文件,xml文件寫SQL語句。
xml文件的根元素是mapper,mppper元素可以包含以下子元素:
cache:指定的命名空間的緩存配置
cache-ref:引用其他命名空間緩存配置
resultMap:描述如何從數(shù)據(jù)庫結(jié)果集中加載到對(duì)象中
parameterMap:在MyBatis3.5.4中棄用了!
sql:定義可重用的SQL塊
insert:用于聲明INSERT語句
update:用于聲明UPDATE語句
delete:用于聲明DELETE語句
select:用于聲明SELECT語句
下面逐一講解。
2.元素講解
2.1.cache
默認(rèn)情況下:
1
只啟用本地會(huì)話緩存,只在會(huì)話期間緩存數(shù)據(jù)。有如下特點(diǎn):
所有查詢出來的結(jié)果集都會(huì)被緩存起來;
所有在映射聲明文件中的insert、update、delete語句都會(huì)被放到緩存里;
使用最近最少使用的(LRU)逐出算法 ;
緩存不會(huì)按任何基于時(shí)間的計(jì)劃刷新
緩存可以存儲(chǔ)1024個(gè)列表或?qū)ο蟮膫€(gè)引用;
緩存可讀可寫,意味著緩存的對(duì)象是不共享的,因此能夠被調(diào)用者安全地修改,因?yàn)椴淮嬖谄渌{(diào)用者或線程潛在的修改。
緩存配置只會(huì)對(duì)cache標(biāo)記所在的映射文件中的語句有起作用。如果不想用默認(rèn)的配置,可以修改以上緩存的屬性,如:
1
2
3
4
5
創(chuàng)建一個(gè)FIFO緩存,并且每隔60秒就刷新一次,最多可以存儲(chǔ)512個(gè)返回的結(jié)果對(duì)象或列表或?qū)ο蟮囊茫揖彺嬷荒茏x不能寫。 因?yàn)閷懣赡軙?huì)在多個(gè)調(diào)用者或線程之間才生沖突。
緩存用的逐出策略有以下這些:
LRU(默認(rèn)的):最近最少使用,移除那些長(zhǎng)時(shí)間不使用的對(duì)象
FIFO:先進(jìn)先出, 按照他們進(jìn)入緩存的順序移除對(duì)象
SOFT:軟引用,基于垃圾收集器狀態(tài)和軟引用移除對(duì)象。
WEAK:弱引用, 更積極地基于垃圾收集器狀態(tài)和弱引用規(guī)則移除對(duì)象
cache元素的屬性:
使用自定義的cache:
1
自定義cache要實(shí)現(xiàn)org.apache.ibatis.cache.Cache接口,MyBatis的Cache接口是這樣的:
public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); }
1
2
3
4
5
6
7
8
9
實(shí)現(xiàn)Cache接口:
public class MyCustomCache implements Cache{ // ... }
1
2
3
點(diǎn)到為止,更多內(nèi)容請(qǐng)到網(wǎng)上查閱!
2.2.cache-ref
引用其他命名空間的緩存配置。
1
2.3.resultMap
resultMap是MyBatis中最重要的元素,因?yàn)閷⒔Y(jié)果集映射到對(duì)象中,基本都用resultMap來完成。使用resultMap可以讓你省去90%的JDBC代碼,resultMap其甚至可以實(shí)現(xiàn)一些JDBC做不到的事。MyBatis自動(dòng)創(chuàng)建ResultMap,基于名稱自動(dòng)映射列到JavaBean的屬性上, 如果列名和JavaBean的屬性匹配不上,我們可以在列名上使用select子句別名(標(biāo)準(zhǔn)SQL特性)來創(chuàng)建標(biāo)簽匹配:
1
2
3
4
5
6
7
8
除了上面這種給列名取個(gè)與JavaBean屬性匹配得了的別名外,還可以使用
(1)第一步:使用resultMap標(biāo)簽建立列名與屬性名的對(duì)應(yīng)關(guān)系
1
2
3
4
5
(2)第二步:引用resultMap
1
2
3
4
5
resultMap不能和resultType同時(shí)使用,繼續(xù)閱讀你就明白的了。
數(shù)據(jù)庫很難做到時(shí)時(shí)刻刻都能夠和我們的JavaBean的屬性匹配上。而且不是所有數(shù)據(jù)庫都能很好的實(shí)現(xiàn)數(shù)據(jù)庫設(shè)計(jì)第三范式或BCNF范式。這些問題都使處有時(shí)并不能簡(jiǎn)單地通過自動(dòng)映射來完成,對(duì)于這些復(fù)雜的映射關(guān)系的處理可以用resultMap來解決表列名與JavaBean字段映射的問題。這也是它存在的原因。舉例說明,下面這個(gè)復(fù)雜的SQL的映射問題:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
從上面這個(gè)例子,我們可以看到resultMap標(biāo)簽有很多子標(biāo)簽,下面我們一個(gè)一個(gè)來看。
(1)constructor :在類實(shí)例化時(shí)將結(jié)果注入到類的構(gòu)造函數(shù)中,如將結(jié)果果集的內(nèi)容注入到下面這個(gè)類的構(gòu)造函數(shù)中:
public class User { //... public User(Integer id, String username, int age) { //... } //... }
1
2
3
4
5
6
7
使用constructor時(shí),要保證結(jié)果集內(nèi)容的順序要和構(gòu)造函數(shù)的參數(shù)順序一致,如下面的映射,MyBatis會(huì)搜索定義了三個(gè)參數(shù)(java.lang.Integer ,java.lang.String ,int)且出現(xiàn)順序一樣的構(gòu)造函數(shù):
1
2
3
4
5
如果不想維護(hù)arg元素的順序,可以為每一個(gè)arg元素指定一個(gè)名稱,使其與構(gòu)造函數(shù)名稱對(duì)應(yīng)上(對(duì)應(yīng)的注解方式是使用@Param ):
1
2
3
4
5
constructor元素的子元素:
(2)id & result:
1
2
這兩個(gè)是結(jié)果映射中最基本的元素。id和result都是映射一個(gè)列值到一個(gè)簡(jiǎn)單類型(String, int, double, Date等)的屬性或字段。 它們唯一的區(qū)別是id結(jié)果標(biāo)識(shí)為一個(gè)標(biāo)識(shí)符屬性,在比較對(duì)象實(shí)例時(shí)使用,有助于提高總體性能。特別是提高緩存和嵌套result映射的性能,如SQL中帶join語句的映射。
它們有以下屬性:
JDBC支持的類型:
(3)association:復(fù)雜類型的組合,它是處理 “has-one” 這種關(guān)系的,例如Blog類中有個(gè)Author類型時(shí)的屬性。MyBatis有以下兩種方式來實(shí)現(xiàn)association加載:
嵌套Select:通過執(zhí)行另一個(gè)SQL語句來返回這種嵌套的復(fù)雜類型,但這種式對(duì)大數(shù)量的查詢的性能不是很好。
1
2
3
4
5
6
7
8
9
10
11
嵌套result:
先介紹association的一些屬性:
下面是嵌套result的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
上面我們把a(bǔ)uthor獨(dú)立寫了個(gè)resultMap這樣author部分就可以重用了,如果我們不想重用,可以將其寫在一起,如:
1
2
3
4
5
6
7
8
9
10
11
如果Blog中有兩個(gè)字段author、coauthor,它們都是Author對(duì)象,resultMap又該如何寫呢?select語句如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
resultMap可以這樣寫:
1
2
3
4
5
6
association處理多結(jié)果集(調(diào)用存儲(chǔ)過程):從MyBatis 3.2.3開始支持。有一些數(shù)據(jù)庫允許調(diào)存儲(chǔ)過程來執(zhí)行一或多條語句,并返回一或多個(gè)結(jié)果集。這種可以不使用join連接查詢。假設(shè)我們調(diào)用數(shù)據(jù)庫里存儲(chǔ)過程來執(zhí)行以下查詢并返回兩個(gè)結(jié)果集(blogs,authors):
SELECT * FROM BLOG WHERE ID = #{id} SELECT * FROM AUTHOR WHERE ID = #{id}
1
2
在xml映射文件中,使用resultSets屬性指定結(jié)果集名稱,多個(gè)結(jié)果集名稱之間用逗號(hào)分隔:
1
2
3
現(xiàn)在我們可以把a(bǔ)uthors結(jié)果集數(shù)據(jù)填充到 “author” association中:
1
2
3
4
5
6
7
8
9
10
11
(4)collection:復(fù)雜類型的集合。collection元素與association元素很像,但后者是解決“has-one",前者是解決”has-many“這種關(guān)系。舉個(gè)例子:
Blog有許多Posts(帖子),在Blog類里,帖子被定義列表:
private List
1
那么resultMap就應(yīng)該這樣寫:
1
2
3
4
5
與association元素一樣,collection元素也有兩種方式來處理結(jié)果集:
嵌套select:
1
2
3
4
5
6
7
8
9
10
嵌套result:
首先,來看看根據(jù)blog id拿帖子的SQL:
1
2
3
4
5
6
7
8
9
10
11
12
resultMap我們可以這樣寫:
1
2
3
4
5
6
7
8
9
10
也可以將Post的resultMap單獨(dú)寫,以便重用:
1
2
3
4
5
6
7
8
9
10
11
collection處理多個(gè)resultSet的情況(調(diào)用存儲(chǔ)過程):
和前面association一樣,我們可以調(diào)用一個(gè)存儲(chǔ)過程來執(zhí)行兩個(gè)查詢,并返回兩個(gè)結(jié)果集,一個(gè)是Blogs的,別一個(gè)是Posts的:
SELECT * FROM BLOG WHERE ID = #{id} SELECT * FROM POST WHERE BLOG_ID = #{id}
1
2
在xml映射文件中,使用resultSets指定每個(gè)結(jié)果集的名字,多個(gè)結(jié)果集名稱之間用逗號(hào)隔開:
1
2
3
將posts集合填充到對(duì)象:
1
2
3
4
5
6
7
8
9
collection的相關(guān)屬性:
(5)discriminator :
1
2
3
有時(shí)候一個(gè)數(shù)據(jù)庫查詢可能會(huì)返回許多不同的數(shù)據(jù)類型的結(jié)果集。discriminator元素就是設(shè)計(jì)用來處理這種情況的。discriminator有點(diǎn)java中的switch語句。discriminator的定義指定column和javaType屬性,column就是MyBatis將要對(duì)比的值所在的地方。javaType是確保對(duì)比的類型正確。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
與下面是等價(jià)的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在上面這個(gè)例子里,MyBatis會(huì)收到結(jié)果集中每條記錄,然后對(duì)比它的vehicle_ type的值,如果它匹配上了discriminator cases,那么就用指定的resultMap。如果沒有一個(gè)匹配上,那么MyBatis將使用定義在discriminator塊外的resultMap。carResult定義如下,如果vehicle_type是1,那么它就被使用:
1
2
3
我們知道car也是vehicle,所以我們想使用carResult的同時(shí)也把vehicleResult的字段加載進(jìn)來,這其實(shí)就是car繼承vehicle,那么resultMap也是可以繼承的,上面的carResult可以繼承vehicleResult,這樣也會(huì)把vehicleResult的字段加載進(jìn)來:
1
2
3
2.4.sql
這個(gè)元素是用于定義一些可重用的SQL片段,這些片段可以被包含在其他的語句中。如定義以下SQL片段:
1
在select語句中包含此片段:
1
2
3
4
5
6
7
8
9
10
11
屬性值也可以用于include refid屬性里,或者include子句內(nèi)的屬性值,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2.5.insert、update、delete
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
insert、update、delete的屬性:
列子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
insert語句自動(dòng)生成id列:
1
2
3
4
5
如果數(shù)據(jù)庫支持同時(shí)多行插入,那么我們可以給對(duì)象傳個(gè)list或數(shù)組:
1
2
3
4
5
6
如果數(shù)據(jù)庫不支持自動(dòng)生成列類型或者還不支持JDBC驅(qū)動(dòng)自動(dòng)生成keys,那么可以使用以下selectKey方式來生成key,下面這個(gè)列子是隨機(jī)生成一個(gè)ID:
1
2
3
4
5
6
7
8
9
selectKey子元素:
1
2
3
4
5
2.6.select
1
2
3
4
5
6
7
8
9
10
11
12
select元素的屬性:
select舉列:
1
2
3
2.7.參數(shù)類型
一般來說,我們可以通過parameterType指定參數(shù)類型,如果指定的類型與數(shù)據(jù)庫中的類型不一樣的話,MyBatis提供一種方式給我們指定參數(shù)類型,格式:
#{property,javaType=int,jdbcType=NUMERIC}
1
javaType通常由參數(shù)對(duì)象決定,除非對(duì)象是一個(gè)HashMap。指定javaType可以確保使用正確的TypeHandler。jdbcType是 JDBC需要的,用于所有可以為空的列,如果null作為一個(gè)值傳遞的話,就應(yīng)該指定jdbcType。
我們還可以指定TypeHandler類或別名:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
1
對(duì)于數(shù)字類型,有一個(gè)numericScale可以決定精確到小數(shù)點(diǎn)后幾位:
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
1
mode模式屬性可以是IN 、OUT、INOUT,如果是OUT或INOUT,那么參數(shù)對(duì)象屬性的真實(shí)值會(huì)被改變。如果模式是OUT或INOUT ,并且jdbcType=CURSOR
,那么你必須指定一個(gè)resultMap來映射ResultSet到參數(shù)類型,此時(shí)javaType屬性是可選的:
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentRes}
1
MyBatis也支持高級(jí)數(shù)據(jù)類型,如結(jié)構(gòu)體structs,但你必須告知語句類型名字:
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentRes}
1
大多數(shù)時(shí)候,我們都只簡(jiǎn)單地指定屬性名,然后MyBatis就會(huì)幫我們搞掂剩下的這些。因此,我們只需要為可空的列指定jdbcType就可以了,如:
#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}
1
2
3
2.8.置換字符串
默認(rèn)情況下,使用#{}語法,會(huì)讓MyBatis生成PreparedStatement屬性,并將值安全地設(shè)置到PreparedStatement的參數(shù)里。這種方式既安全又快速。有時(shí),我們想直接在SQL語句注入未修改的字符串,當(dāng)SQL語句中的元數(shù)據(jù)(如表名,列名)是動(dòng)態(tài)變化的,字符串置換就顯得很有用了。例如,你要從一張表中通過任意一個(gè)列來選擇:
@Select("select * from user where ${column} = #{value}") User findByColumn(@Param("column") String column, @Param("value") String value);
1
2
接受從用戶輸入或提供SQL語句中未修改的元數(shù)據(jù)名稱是不安全的。這存在潛在的SQL注入攻擊。因此如果使用這種方式,我們要做到不允許用戶輸入這些字段,我們自己應(yīng)該做好轉(zhuǎn)義和檢查,以此來提高安全性。
3.別名
使用別名,你就不需要寫全限定路徑了。
在springboot的application.yml加入以下配置:
mybatis: # 指定MyBatis的配置文件位置 config-location: classpath:config/mybatis-mapper-config.xml # 指定映射器的xml文件的位置 mapper-locations: classpath:mybatis/mapper/*.xml
1
2
3
4
5
~/Desktop/MyBatisXMLDemo$ touch src/main/resources/config/mybatis-mapper-config.xml ~/Desktop/MyBatisXMLDemo$ touch src/main/resources/mybatis/mapper/PersonMapper.xml
1
2
1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
9
10
11
12
還可以把數(shù)據(jù)庫連接的信息從application.yml文件中移到MyBatis的配置文件mybatis-mapper-config.xml中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
4.動(dòng)態(tài)SQL
MyBatis可以實(shí)現(xiàn)動(dòng)態(tài)SQL。
if
choose (when, otherwise)
trim (where, set)
foreach
4.1.if
1
2
3
4
5
6
7
再如:
1
2
3
4
5
6
7
8
9
4.2.choose (when, otherwise)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
4.3.trim (where, set)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
where元素可以很方便處理SQL的where子句,當(dāng)where元素中的if元素有符合的即需要插入where子句的),where元素就會(huì)在SQL語句后面添加where子句。如果where子句是以 “AND” 或 "OR"開頭,它會(huì)把它去掉,再添加到where后面。如果where元素都還滿足不了你的需求,Mybatis提供trim元素讓你來自定義,如下面的trim等價(jià)于where元素:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
prefix是要插入的,prefixOverrides是要移除的。prefixOverrides屬性接受要重寫的以管道分隔的文本列表,注意空格的地方,它是必要的。簡(jiǎn)單點(diǎn)來說,trim的結(jié)果就是prefix屬性的值加上trim元素里的值,如果trim里沒有值,則不在SQL里插入,如果trim里的值是以prefixOverrides里的值開頭的則先移除再和prefix屬性值一起插入到SQL語句里。
有一種相似的動(dòng)態(tài)update語句的解決方案叫set。set元素可以用來動(dòng)態(tài)包含要更新的列,而排除不需要更新的列:
1
2
3
4
5
6
7
8
9
10
上面也可以用trim來自定義,如:
1
2
3
4
5
6
7
8
9
10
11
4.4.foreach
1
2
3
4
5
6
7
8
foreach元素允許我們指定一個(gè)集合collection,聲明item和index變量,這兩個(gè)變量可以在foreach元素內(nèi)部使用。foreach也允許我們指定開始和結(jié)束的字符串,和添加在迭代之間的分隔符 。foreach元素不會(huì)意外地附加額外的分隔符,這一點(diǎn)可以放心。
我們可以傳遞任何迭代對(duì)象,如List、Set等,同樣也可傳遞Map或Array對(duì)象到foreach作為集合參數(shù)。當(dāng)使用Iterable 或 Array,index表示當(dāng)前迭代的下標(biāo),item表示當(dāng)前的值。當(dāng)使用Map,如 Map.Entry 對(duì)象的集合,index就是key對(duì)象,item就是值對(duì)象。
4.5.script
在映射器類里使用注解的方式使用動(dòng)態(tài)SQL,需要使用script 元素:
@Update({""}) void updateAuthorValues(Author author);
1
2
3
4
5
6
7
8
9
10
11
4.6.bind
bind元素可以讓你在OGNL表達(dá)式之外,創(chuàng)建一個(gè)變量,并將其綁定到上下文中:
1
2
3
4
5
5. 多數(shù)據(jù)庫供應(yīng)商支持
說白了就是連接多個(gè)數(shù)據(jù)庫。如果有一個(gè)databaseIdProvider配置了“_databaseId"變量并且對(duì)于動(dòng)態(tài)代碼來說是可用的。那么,我們可以根據(jù)不同的數(shù)據(jù)庫提供商來建不同的語句,如:
1
2
3
4
5
6
7
8
9
10
11
上面大概把MyBatis使用xml映射文件說了一遍。
MyBatis XML
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。