HasorDB 映射文件

2022-01-10 10:23 更新

使用 Mapper 文件的好處是便于維護(hù)和管理 SQL,這在團(tuán)隊(duì)協(xié)作時(shí) review sql 代碼比起在程序中用代碼來(lái)拼接要好。

文檔結(jié)構(gòu)?

下面是 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è):

  • resultMap 用于描述如何從查詢結(jié)果集中加載數(shù)據(jù)。
  • sql 用于定義一小段可以復(fù)用的 SQL 片段。
  • insert 映射 INSERT 語(yǔ)句。
  • update 映射 UPDATE 語(yǔ)句。
  • delete 映射 DELETE 語(yǔ)句。
  • select 映射 SELECT 語(yǔ)句。

select 操作?

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、PREPAREDCALLABLE 對(duì)應(yīng)了 StatementPreparedStatement 或 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_INSENSITIVESCROLL_SENSITIVE 和 DEFAULT 其中的一種。默認(rèn)值是 DEFAULT 相當(dāng)于未設(shè)置。
multipleResult 可選,FIRST、LASTALL 用于處理多結(jié)果集的情況。它們對(duì)應(yīng)的行為是 保留第一個(gè)結(jié)果集、保留最后一個(gè)結(jié)果集全部保留。默認(rèn)配置是 LAST

insert, update 和 delete 操作?

?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 可選,STATEMENTPREPARED、CALLABLE 對(duì)應(yīng)了 StatementPreparedStatement 或 CallableStatement 中的一種。默認(rèn)值為 PREPARED
timeout 可選,當(dāng)配置的值大于 0 時(shí)會(huì)被設(shè)置到 Statement.setQueryTimeout,用于表示查詢最長(zhǎng)等待的超時(shí)時(shí)間。默認(rèn)值是 -1

selectKey?

對(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、PREPAREDCALLABLE 對(duì)應(yīng)了 StatementPreparedStatement 或 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è)置。

sql 代碼片段?

此標(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>

結(jié)果集映射?

并不是每一個(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ě)器。

多結(jié)果映射?

例如,一個(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>

自動(dòng)映射?

上面已經(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)閉它。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)