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