HasorDB 映射文件
使用 Mapper 文件的好处是便于维护和管理 SQL,这在团队协作时 review sql 代码比起在程序中用代码来拼接要好。
文档结构
下面是 HasorDB Mapper 文件基本结构:
<?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 元素有如下几个:
- resultMap 用于描述如何从查询结果集中加载数据。
- sql 用于定义一小段可以复用的 SQL 片段。
- insert 映射 INSERT 语句。
- update 映射 UPDATE 语句。
- delete 映射 DELETE 语句。
- select 映射 SELECT 语句。
select 操作
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 时会被设置到 Statement.setQueryTimeout ,用于表示查询最长等待的超时时间。默认值是 -1
|
resultMap
|
可选,对于映射配置的引用。select 标签可以使用 resultMap 和 resultType 其中的一种,不应该同时使用它们。如果没有配置将会按照 map 来处理 |
resultType
|
可选,将返回的预期类型的完全限定类名或别名。注意,在集合的情况下,这应该是集合包含的类型,而不是集合本身的类型,不应该同时使用resultMap 和 resultType 。 |
fetchSize
|
可选,当配置的值大于 0 时会被设置到 Statement.setQueryTimeout ,用于表示查询最长等待的超时时间。默认值是 -1
|
resultSetType
|
可选,FORWARD_ONLY 、SCROLL_INSENSITIVE 、SCROLL_SENSITIVE 和 DEFAULT 其中的一种。默认值是 DEFAULT 相当于未设置。 |
multipleResult
|
可选,FIRST 、LAST 、ALL 用于处理多结果集的情况。它们对应的行为是 保留第一个结果集 、保留最后一个结果集 、全部保留 。默认配置是 LAST
|
insert, update 和 delete 操作
insert
、update
、delete
标签本质上是同一个标签,只是通过专门的名字来区分会让人更为容易理解。
一个简单的例子如下:
<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 时会被设置到 Statement.setQueryTimeout ,用于表示查询最长等待的超时时间。默认值是 -1
|
selectKey
对于不支持自增列的数据库,HasorDB 可以使用 selectKey
标签来通过 SQL 方式生成它,比较常见用处是使用数据库的 sequence
。 例如:下面 Mapper 配置,在执行 insert
之前会先使用数据库函数生成一个随机数作为主键。
<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 执行后的返回值写入到目标的属性名,如果要回写多个属性则,可以使用逗号分割属性名列表。 |
keyColumn
|
可选,返回结果集中与属性匹配的列名,如果需要选择多个列,可以使用逗号分割属性名列表。列名和属性名的顺序一致。 |
order
|
可选,可以设置为 BEFORE 或 AFTER 。如果设置为 BEFORE 它会在执行 insert 之前先执行 selectKey ;如果设置为 AFTER 则会在运行完 insert 之后在执行 selectKey 。后运行一般用于获取自增主键的返回值。 |
handler
|
可选,用于自定义 selectKey 执行逻辑。配置一个全类名,该类要求实现了 net.hasor.db.dal.execute.KeySequenceHolderFactory 接口,并且有一个无参的构造方法。 |
statementType
|
可选,STATEMENT 、PREPARED 、CALLABLE 对应了 Statement , PreparedStatement 或 CallableStatement 中的一种。默认值为 PREPARED
|
timeout
|
可选,当配置的值大于 0 时会被设置到 Statement.setQueryTimeout ,用于表示查询最长等待的超时时间。默认值是 -1
|
resultType
|
可选,将返回的预期类型的完全限定类名或别名。注意,在集合的情况下,这应该是集合包含的类型,而不是集合本身的类型,不应该同时使用resultMap 和 resultType 。 |
fetchSize
|
可选,当配置的值大于 0 时会被设置到 Statement.setQueryTimeout ,用于表示查询最长等待的超时时间。默认值是 -1
|
resultSetType
|
可选,FORWARD_ONLY 、SCROLL_INSENSITIVE 、SCROLL_SENSITIVE 和 DEFAULT 其中的一种。默认值是 DEFAULT 相当于未设置。 |
sql 代码片段
此标签可用于定义一段在其它语句中被包含的重用代码片段。例如定义列名。
<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 来承装返回的数据。
<select id="queryListByAge">
select * from `test_user` where age = #{age}
</select>
但通常 Map 并不是一个很好的模型设计,应该使用一些有意义的 pojo 充当数据对象。HasorDB 支持将一个普通的 pojo 映射到一个结果集上。例如下面这个 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
属性将查询结果映射到这个 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 会在加载配置文件的时候自动创建一个 resultMap
,根据名称将列自动映射到 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 时候用到。它可以决定 映射到的数据库 schema 名字。 |
table
|
可选,一个补充选项,通常在使用通用 Mapper 时候用到。它可以决定 映射到的数据库 table 名字。 |
caseInsensitive
|
可选,在处理映射列名和属性名时是否对大小写不敏感,默认是 true 不敏感。对于某些数据库查询结果始终返回大写,利用这个功能可以方便的映射到属性上。 |
mapUnderscoreToCamelCase
|
可选,用于决定属性名在映射到列名时,是否按照驼峰命名法转换为下划线命名法,例如:属性名 createTime 被转换为 create_time 。默认是 false 不转换 |
autoMapping
|
可选,用于决定是否进行 自动映射。默认是 true 自动映射。 |
id &
id
和 result
都是用于映射列和属性之间的映射关系。不同的是 id 可以方便的指出列在数据中是否为主键。
在使用通用 Mapper 时,CURD 操作将会依赖 id
、table
、schema
这些属性用以生成 SQL。
它们每个标签都有下面这些属性
属性名 | 描述 |
---|---|
property
|
必选,pojo 的属性名。 |
column
|
必选,查询结果的列名。 |
javaType
|
可选,通常 HasorDB 会识别到具体类型,但如果 pojo 的属性是一个抽象类或者接口,则可以配置 javaType 来指定具体的实现类。 |
jdbcType
|
可选,对应的 JDBC 类型。HasorDB 将会遵循 Java 和 JDBC 类型关系 进行映射 |
typeHandler
|
可选,通常 HasorDB 会根据 类型映射 自动寻找列的读写器。该属性允许自定义属性读写器。 |
多结果映射
例如,一个 select
配置了两个 查询语句。或者调用的存储过程中执行了两条 查询 SQL。resultType
中以逗号作为分割将两个结果分别映射到两个类型上。
提示
使用 resultMap
同样也可以通过逗号作为分割,映射多个结果。
<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>
自动映射
上面已经介绍过 resultMap
和 resultType
的能力,在本节可以了解通过自动映射机制来混合两种操作。
HasorDB 会尝试将结果集中的列名作为属性名进行写入,匹配的时会忽略大小写。 这意味着,如果找到名为 ID
的列和名为 ID
的属性,HasorDB 将使用 ID
列值设置 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>
通常数据库列的命名使用大写字母和下划线,这与 java 通常遵循驼峰命名约定有一定的差异。 若想使它们之间自定映射需要设置 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 是启用自动映射的,如果你不像使用这一特性。则可以通过设置 autoMapping
为 false
来关闭它。