使用 Mapper 文件的好處是便于維護(hù)和管理 SQL,這在團(tuán)隊(duì)協(xié)作時(shí) review sql 代碼比起在程序中用代碼來(lái)拼接要好。
下面是 HasorDB Mapper 文件基本結(jié)構(gòu):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//hasor.net//DTD Mapper 1.0//EN"
"https://www.hasor.net/schema/hasordb-mapper.dtd">
<mapper namespace="...">
...
</mapper>
提示
?namespace
?通常是配置一個(gè)接口類(lèi)名,這個(gè)接口下的每個(gè)方法會(huì)對(duì)應(yīng)到 mapper 文件中一個(gè)具體的 sql 操作上。
在 ?mapper
?根元素下可以使用的頂層 XML 元素有如下幾個(gè):
select 標(biāo)簽是 HasorDB 中最常用的元素之一。對(duì)于簡(jiǎn)單的情況 select 元素非常簡(jiǎn)單。例如:
<select id="queryListByAge">
select * from `test_user` where age = #{age}
</select>
select 標(biāo)簽有很多屬性下面列出的是每個(gè)屬性和含義。
屬性名 | 描述 |
---|---|
id
|
必選,用于標(biāo)識(shí)查詢命令 |
statementType
|
可選,STATEMENT 、PREPARED 、CALLABLE 對(duì)應(yīng)了 Statement , PreparedStatement 或 CallableStatement 中的一種。默認(rèn)值為 PREPARED
|
timeout
|
可選,當(dāng)配置的值大于 0 時(shí)會(huì)被設(shè)置到 Statement.setQueryTimeout ,用于表示查詢最長(zhǎng)等待的超時(shí)時(shí)間。默認(rèn)值是 -1
|
resultMap
|
可選,對(duì)于映射配置的引用。select 標(biāo)簽可以使用 resultMap 和 resultType 其中的一種,不應(yīng)該同時(shí)使用它們。如果沒(méi)有配置將會(huì)按照 map 來(lái)處理 |
resultType
|
可選,將返回的預(yù)期類(lèi)型的完全限定類(lèi)名或別名。注意,在集合的情況下,這應(yīng)該是集合包含的類(lèi)型,而不是集合本身的類(lèi)型,不應(yīng)該同時(shí)使用resultMap 和 resultType 。 |
fetchSize
|
可選,當(dāng)配置的值大于 0 時(shí)會(huì)被設(shè)置到 Statement.setQueryTimeout ,用于表示查詢最長(zhǎng)等待的超時(shí)時(shí)間。默認(rèn)值是 -1
|
resultSetType
|
可選,FORWARD_ONLY 、SCROLL_INSENSITIVE 、SCROLL_SENSITIVE 和 DEFAULT 其中的一種。默認(rèn)值是 DEFAULT 相當(dāng)于未設(shè)置。 |
multipleResult
|
可選,FIRST 、LAST 、ALL 用于處理多結(jié)果集的情況。它們對(duì)應(yīng)的行為是 保留第一個(gè)結(jié)果集 、保留最后一個(gè)結(jié)果集 、全部保留 。默認(rèn)配置是 LAST
|
?insert
?、?update
?、?delete
?標(biāo)簽本質(zhì)上是同一個(gè)標(biāo)簽,只是通過(guò)專(zhuān)門(mén)的名字來(lái)區(qū)分會(huì)讓人更為容易理解。
一個(gè)簡(jiǎn)單的例子如下:
<insert id="insertUser">
insert into `test_user` (
`id`, `name`, `age`, `create_time`
) values (
#{id}, #{name}, #{age}, #{createTime}
)
</insert>
<update id="updateAge">
update `test_user` set age = #{age} where id = #{id}
</update>
<delete id="deleteById">
delete from `test_user` where id = #{id}
</delete>
?insert
?、?update
?、?delete
?標(biāo)簽都有如下共同的屬性。
屬性名 | 描述 |
---|---|
id
|
必選,用于標(biāo)識(shí)查詢命令 |
statementType
|
可選,STATEMENT 、PREPARED 、CALLABLE 對(duì)應(yīng)了 Statement , PreparedStatement 或 CallableStatement 中的一種。默認(rèn)值為 PREPARED
|
timeout
|
可選,當(dāng)配置的值大于 0 時(shí)會(huì)被設(shè)置到 Statement.setQueryTimeout ,用于表示查詢最長(zhǎng)等待的超時(shí)時(shí)間。默認(rèn)值是 -1
|
對(duì)于不支持自增列的數(shù)據(jù)庫(kù),HasorDB 可以使用 ?selectKey
?標(biāo)簽來(lái)通過(guò) SQL 方式生成它,比較常見(jiàn)用處是使用數(shù)據(jù)庫(kù)的 ?sequence
?。 例如:下面 Mapper 配置,在執(zhí)行 ?insert
?之前會(huì)先使用數(shù)據(jù)庫(kù)函數(shù)生成一個(gè)隨機(jī)數(shù)作為主鍵。
<insert id="insertUser">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
SELECT CONCAT('1', CEILING(RAND() * 1000 + 1000))
</selectKey>
insert into `test_user` (
`id`, `name`, `age`, `create_time`
) values (
#{id}, #{name}, #{age}, #{createTime}
)
</insert>
?selectKey
?標(biāo)簽有如下屬性。
屬性名 | 描述 |
---|---|
keyProperty
|
必選,用于將 selectKey 執(zhí)行后的返回值寫(xiě)入到目標(biāo)的屬性名,如果要回寫(xiě)多個(gè)屬性則,可以使用逗號(hào)分割屬性名列表。 |
keyColumn
|
可選,返回結(jié)果集中與屬性匹配的列名,如果需要選擇多個(gè)列,可以使用逗號(hào)分割屬性名列表。列名和屬性名的順序一致。 |
order
|
可選,可以設(shè)置為 BEFORE 或 AFTER 。如果設(shè)置為 BEFORE 它會(huì)在執(zhí)行 insert 之前先執(zhí)行 selectKey ;如果設(shè)置為 AFTER 則會(huì)在運(yùn)行完 insert 之后在執(zhí)行 selectKey 。后運(yùn)行一般用于獲取自增主鍵的返回值。 |
handler
|
可選,用于自定義 selectKey 執(zhí)行邏輯。配置一個(gè)全類(lèi)名,該類(lèi)要求實(shí)現(xiàn)了 net.hasor.db.dal.execute.KeySequenceHolderFactory 接口,并且有一個(gè)無(wú)參的構(gòu)造方法。 |
statementType
|
可選,STATEMENT 、PREPARED 、CALLABLE 對(duì)應(yīng)了 Statement , PreparedStatement 或 CallableStatement 中的一種。默認(rèn)值為 PREPARED
|
timeout
|
可選,當(dāng)配置的值大于 0 時(shí)會(huì)被設(shè)置到 Statement.setQueryTimeout ,用于表示查詢最長(zhǎng)等待的超時(shí)時(shí)間。默認(rèn)值是 -1
|
resultType
|
可選,將返回的預(yù)期類(lèi)型的完全限定類(lèi)名或別名。注意,在集合的情況下,這應(yīng)該是集合包含的類(lèi)型,而不是集合本身的類(lèi)型,不應(yīng)該同時(shí)使用resultMap 和 resultType 。 |
fetchSize
|
可選,當(dāng)配置的值大于 0 時(shí)會(huì)被設(shè)置到 Statement.setQueryTimeout ,用于表示查詢最長(zhǎng)等待的超時(shí)時(shí)間。默認(rèn)值是 -1
|
resultSetType
|
可選,FORWARD_ONLY 、SCROLL_INSENSITIVE 、SCROLL_SENSITIVE 和 DEFAULT 其中的一種。默認(rèn)值是 DEFAULT 相當(dāng)于未設(shè)置。 |
此標(biāo)簽可用于定義一段在其它語(yǔ)句中被包含的重用代碼片段。例如定義列名。
<sql id="testuser_columns">
name,age,create_time
</sql>
<insert id="insertUser">
insert into `test_user` (
<include refid="testuser_columns"/>
) values (
#{name}, #{age}, now()
)
</insert>
并不是每一個(gè) ?select
?都必須要求配置 ?resultMap
?默認(rèn)情況下會(huì)使用 Map 來(lái)承裝返回的數(shù)據(jù)。
<select id="queryListByAge">
select * from `test_user` where age = #{age}
</select>
但通常 Map 并不是一個(gè)很好的模型設(shè)計(jì),應(yīng)該使用一些有意義的 pojo 充當(dāng)數(shù)據(jù)對(duì)象。HasorDB 支持將一個(gè)普通的 pojo 映射到一個(gè)結(jié)果集上。例如下面這個(gè) Bean:
?class net.hasor.db.example.mapper.TestUser
?
public class TestUser {
private Integer id;
private String name;
private Integer age;
private Date createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
可以使用 ?resultType
?屬性將查詢結(jié)果映射到這個(gè) Bean 上。
<select id="queryById" resultType="net.hasor.db.example.mapper.TestUser">
select
id, name, age, create_time
from
test_user
where
id = #{id}
</select>
使用這種方式,HasorDB 會(huì)在加載配置文件的時(shí)候自動(dòng)創(chuàng)建一個(gè) ?resultMap
?,根據(jù)名稱(chēng)將列自動(dòng)映射到 pojo 的屬性上。如果列名不完全匹配,可以在列名上使用 ?as
?子句來(lái)匹配映射。例如:
<select id="queryById" resultType="net.hasor.db.example.mapper.TestUser">
select
id, name, age, create_time as createTime
from
test_user
where
id = #{id}
</select>
直接使用 ?resultMap
?的好處是可以更加精細(xì)化的控制每一個(gè)屬性映射,以剛才的映射為例。在不通過(guò) ?as
?改變列名的情況下映射這個(gè) pojo:
<resultMap id="testuser_resultMap" type="net.hasor.db.example.mapper.TestUser">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<result column="create_time" property="createTime"/>
</resultMap>
<select id="queryById" resultMap="testuser_resultMap">
select
id, name, age, create_time
from
test_user
where
id = #{id}
</select>
?resultMap
?標(biāo)簽有如下屬性。
屬性名 | 描述 |
---|---|
type
|
必選,類(lèi)型全名,用于決定映射到的具體類(lèi)型。 |
id
|
可選,如果為空那么將會(huì)以 type 屬性為替代。主要是用于標(biāo)識(shí) resultMap。 |
schema
|
可選,一個(gè)補(bǔ)充選項(xiàng),通常在使用通用 Mapper 時(shí)候用到。它可以決定 映射到的數(shù)據(jù)庫(kù) schema 名字。 |
table
|
可選,一個(gè)補(bǔ)充選項(xiàng),通常在使用通用 Mapper 時(shí)候用到。它可以決定 映射到的數(shù)據(jù)庫(kù) table 名字。 |
caseInsensitive
|
可選,在處理映射列名和屬性名時(shí)是否對(duì)大小寫(xiě)不敏感,默認(rèn)是 true 不敏感。對(duì)于某些數(shù)據(jù)庫(kù)查詢結(jié)果始終返回大寫(xiě),利用這個(gè)功能可以方便的映射到屬性上。 |
mapUnderscoreToCamelCase
|
可選,用于決定屬性名在映射到列名時(shí),是否按照駝峰命名法轉(zhuǎn)換為下劃線命名法,例如:屬性名 createTime 被轉(zhuǎn)換為 create_time 。默認(rèn)是 false 不轉(zhuǎn)換 |
autoMapping
|
可選,用于決定是否進(jìn)行 自動(dòng)映射。默認(rèn)是 true 自動(dòng)映射。 |
id &
?id
?和 ?result
?都是用于映射列和屬性之間的映射關(guān)系。不同的是 id 可以方便的指出列在數(shù)據(jù)中是否為主鍵。
在使用通用 Mapper 時(shí),CURD 操作將會(huì)依賴(lài) ?id
?、?table
?、?schema
?這些屬性用以生成 SQL。
它們每個(gè)標(biāo)簽都有下面這些屬性
屬性名 | 描述 |
---|---|
property
|
必選,pojo 的屬性名。 |
column
|
必選,查詢結(jié)果的列名。 |
javaType
|
可選,通常 HasorDB 會(huì)識(shí)別到具體類(lèi)型,但如果 pojo 的屬性是一個(gè)抽象類(lèi)或者接口,則可以配置 javaType 來(lái)指定具體的實(shí)現(xiàn)類(lèi)。 |
jdbcType
|
可選,對(duì)應(yīng)的 JDBC 類(lèi)型。HasorDB 將會(huì)遵循 Java 和 JDBC 類(lèi)型關(guān)系 進(jìn)行映射 |
typeHandler
|
可選,通常 HasorDB 會(huì)根據(jù) 類(lèi)型映射 自動(dòng)尋找列的讀寫(xiě)器。該屬性允許自定義屬性讀寫(xiě)器。 |
例如,一個(gè) ?select
?配置了兩個(gè) 查詢語(yǔ)句?;蛘哒{(diào)用的存儲(chǔ)過(guò)程中執(zhí)行了兩條 查詢 SQL。?resultType
?中以逗號(hào)作為分割將兩個(gè)結(jié)果分別映射到兩個(gè)類(lèi)型上。
提示
使用 ?resultMap
?同樣也可以通過(guò)逗號(hào)作為分割,映射多個(gè)結(jié)果。
<select id="multipleListByAge" multipleResult="ALL"
resultType="net.hasor.db.example.mapper.TestUserPojo,net.hasor.db.example.mapper.TestUser">
select id, name, age, create_time as createTime from `test_user` where age = #{age};
select * from `test_user`;
</select>
上面已經(jīng)介紹過(guò) ?resultMap
?和 ?resultType
?的能力,在本節(jié)可以了解通過(guò)自動(dòng)映射機(jī)制來(lái)混合兩種操作。
HasorDB 會(huì)嘗試將結(jié)果集中的列名作為屬性名進(jìn)行寫(xiě)入,匹配的時(shí)會(huì)忽略大小寫(xiě)。 這意味著,如果找到名為 ?ID
?的列和名為 ?ID
?的屬性,HasorDB 將使用 ?ID
?列值設(shè)置 ?ID
?屬性。
<resultMap id="testuser_resultMap" type="net.hasor.db.example.mapper.TestUser">
<result property="createTime" column="create_time"/>
</resultMap>
<select id="queryById" resultMap="testuser_resultMap">
select
id, name, age, create_time
from
test_user
where
id = #{id}
</select>
通常數(shù)據(jù)庫(kù)列的命名使用大寫(xiě)字母和下劃線,這與 java 通常遵循駝峰命名約定有一定的差異。 若想使它們之間自定映射需要設(shè)置 ?mapUnderscoreToCamelCase
?為 ?true
?
<resultMap id="testuser_resultMap" type="net.hasor.db.example.mapper.TestUser"
mapUnderscoreToCamelCase="true"/>
<select id="queryById" resultMap="testuser_resultMap">
select
id, name, age, create_time
from
test_user
where
id = #{id}
</select>
提示
默認(rèn)情況下 HasorDB 是啟用自動(dòng)映射的,如果你不像使用這一特性。則可以通過(guò)設(shè)置 ?autoMapping
?為 ?false
?來(lái)關(guān)閉它。
更多建議: