MyBatis 映射配置文件详解

1 映射配置文件结构

 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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="UserMapper">

    <!--查询所有-->
    <select id="findAll" resultType="io.maling.User">
        select * from user
    </select>

    <!--插入数据-->
    <insert id="insertUser">
        insert into user (id,username,password,email)
        values (#{id},#{username},#{password},#{email})
    </insert>

    <!--更新数据-->
    <update id="updateUser">
        update user set
            username = #{username},
            password = #{password},
            email = #{email}
        where id = #{id}
    </update>

    <!--删除数据-->
    <delete id="deleteUser">
        delete from user where id = #{id}
    </delete>
</mapper>
  • <!DOCTYPE mapper ... > :映射文件的 DTD 约束头。
  • <mapper namespace=...>
    • mapper:是映射文件根元素
    • namespace:命名空间,与 <select id > 一起用于确定坐标
  • <select id=... resultType=...>
    • select: 元素指定 SQL 执行类型,可选的还有 insertupdatedelete
    • id:与 <mapper namespace > 一起构成查询坐标。
    • resultType:查询结果对应的实体类型。

2 select 元素

select 元素支持的完整属性如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<select
  id="findAll"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">
</select>
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。
这个属性是可选的,因为 MyBatis 可以根据语句中实际传入的参数计算出应该使用的类型处理器 (TypeHandler)。
默认值:未设置。
resultType期望从这条语句中返回结果的类全限定名或别名。
注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。
resultMap对外部 resultMap 的命名引用。
resultTyperesultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来。
默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数(依赖数据库驱动)。
默认值:未设置。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值(依赖驱动)。
默认值:未设置。
statementType可选 STATEMENTPREPAREDCALLABLE。这会让 MyBatis 分别使用 StatementPreparedStatementCallableStatement,默认值:PREPARED
resultSetTypeFORWARD_ONLYSCROLL_SENSITIVE, SCROLL_INSENSITIVEDEFAULT(等价于未设置) 中的一个(依赖数据库驱动),默认值:未设置。
databaseId如果配置了数据库厂商标识 (databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;
如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:
如果为 true,则假设结果集以正确顺序(排序后)执行映射,当返回新的主结果行时,将不再发生对以前结果行的引用。这样可以减少内存消耗。
默认值:false。
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。
affectData将其设置为 true,用于编写返回数据的 INSERT、UPDATE 或 DELETE 语句,以便正确地控制事务。默认值:false (since 3.5.12)。

3 insert, update 和 delete 元素

数据变更语句 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
24
25
26
<insert
    id="insertUser"
    parameterType="domain.blog.User"
    flushCache="true"
    statementType="PREPARED"
    keyProperty=""
    keyColumn=""
    useGeneratedKeys=""
    timeout="20">
</insert>

<update
    id="updateUser"
    parameterType="domain.blog.User"
    flushCache="true"
    statementType="PREPARED"
    timeout="20">
</update>

<delete
    id="deleteUser"
    parameterType="domain.blog.User"
    flushCache="true"
    statementType="PREPARED"
    timeout="20">
</delete>
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。
这个属性是可选的,因为 MyBatis 可以根据语句中实际传入的参数计算出应该使用的类型处理器 (TypeHandler),默认值:未设置。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空。
默认值:(对 insertupdatedelete 语句)true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数(依赖数据库驱动)。
默认值:未设置。
statementType可选 STATEMENTPREPAREDCALLABLE。这会让 MyBatis 分别使用 StatementPreparedStatementCallableStatement,默认值:PREPARED
useGeneratedKeys(仅适用于 insertupdate)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insertupdate)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置。
如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insertupdate)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId如果配置了数据库厂商标识 (databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。

3.1 插入数据后,立刻返回主键

很多时候,我们希望向数据库插入一条记录后,能立即拿到这条记录的主键值。对于支持自增主键的数据库,我们可以使用上文表格中介绍的 useGeneratedKeys 属性:

1
2
3
4
5
6
<insert id="save" parameterType="io.maling.pojo.User" 
        useGeneratedKeys="true" 
        keyProperty="id">
    insert into user(username, birthday, sex, address)
    values(#{username},#{birthday},#{sex},#{address})
</insert>
  • useGeneratedKeys="true" 启用返回主键;
  • keyProperty="id" 把返回主键的值,封装到 Bean 实体的 id 属性中。

如果使用的数据库不支持自动生成主键,也可以使用 selectKey 元素获取插入元素:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<insert id="save" parameterType="io.maling.pojo.User"> 
	<selectKey 
      keyColumn="id" 
      keyProperty="id" 
      resultType="int" 
      order="AFTER">
		SELECT LAST_INSERT_ID();
	</selectKey> 
	INSERT INTO `user` (username,birthday,sex,address) 
	VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
  • keyColumn="id" 指定主键列名;
  • keyProperty="id" 指定主键封装到 Pojo 实体的属性中;
  • resultType="int" 指定主键类型;
  • order="AFTER" 设置在 SQL 语句执行前(后),执行此语句。

selectKey 标签适用范围更广,支持所有类型数据库

4 sql 元素

sql 元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:

1
2
3
<sql id="userColumns"> 
  ${alias}.id,${alias}.username,${alias}.password 
</sql>

这个 SQL 片段可以在其它语句中使用,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns">
      <property name="alias" value="t1"/>
    </include>,
    <include refid="userColumns">
      <property name="alias" value="t2"/>
    </include>
  from some_table t1
    cross join some_table t2
</select>

也可以在 include 元素的 refid 属性或内部语句中使用属性值,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<sql id="sometable">
  ${prefix}Table
</sql>

<sql id="someinclude">
  from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
  select
    field1, field2, field3
  <include refid="someinclude">
    <property name="prefix" value="Some"/>
    <property name="include_target" value="sometable"/>
  </include>
</select>

5 参数传递

5.1 #{} 方式传递参数

MyBatis 默认情况下推荐使用 #{} 语法传递参数,通过这种方式传递参数时,MyBatis 会创建 PreparedStatement 参数占位符安全地设置参数。例如:

1
2
3
4
5
#{height, 
  javaType=double, 
  jdbcType=NUMERIC, 
  typeHandler=MyTypeHandler, 
  numericScale=2}

#{} 传递参数时可以通过多种属性进行自定义:

  • escape:是否转义百分号(%)和下划线(_)。默认情况下,MyBatis 会转义这些字符,以避免 SQL 注入攻击。可以通过设置 escape 属性来禁用转义。
  • map:指定一个 Map 对象,用于将键值对传递给 SQL 语句中的参数。
  • javaType:指定参数的 Java 类型。MyBatis 将根据该类型自动将参数转换为相应的数据库类型。
  • jdbcType:指定参数的 JDBC 类型。这个属性可以用来指定特定的 JDBC 类型,以便正确地设置 PreparedStatement 的参数。
  • mode:指定参数的处理方式。常见的模式包括:INOUTINOUT 等。
  • resultMap:指定结果映射的配置。可以将参数映射到结果集中的列,从而实现对象与数据库列之间的映射。
  • null:指定当参数为 null 时的处理方式。默认情况下,MyBatis 会将 null 参数设置为 SQL 语句中的 NULL 值。

尽管上面这些选项很强大,但大多时候,我们只须简单的指定属性名。

5.2 ${} 字符串替换

${} 语法用于插入未经转义的字符串,当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,${} 字符串替换将会非常有用。举个例子,如果我们想 select 一个表的任意一列数据,不需要明确指定列名,而是可以使用 ${} 语法动态替换列名。这样做可以更加灵活地构建 SQL 语句,适用于更加复杂的应用场景。例如:

1
2
3
4
5
6
7
8
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

/* 调用
User userOfId1 = userMapper.findByColumn("id", 1L);
User userOfNameKid = userMapper.findByColumn("name", "ling");
User userOfEmail = userMapper.findByColumn("email", "me@mmail.io");
*/

其中 ${column} 会被直接替换,而 #{value} 会使用 ? 预处理。

提示
用这种方式接受用户的输入,并用作语句参数是不安全的,会导致潜在的 SQL 注入攻击。因此,要么不允许用户输入这些字段,要么自行转义并检验这些参数。

6 resultMap 结果映射

如果实体的属性名与表中字段名不一致,可以使用 resultMap 实现手动关系映射。示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<mapper namespace="io.maling.UserMapper">
	<resultMap id="userResultMap" type="user">
		<id colum="uid" property="id"/>
		<result column="NAME" property="username"/>
		<result column="PASSWORD" property="password"/>
	</resultMap>

	<select id="findAllResultMap" resultMap="userResultMap">
		SELECT id AS uid,username AS NAME,password AS PASSWORD FROM USER
	</select>
</mapper>
  • resultMap
    • id 元素:表示实体对象的主键。
      • column 属性:表中主键的名称、别名;
      • property 属性:实体对象主键的属性名。
    • result 元素:表示实体对象的普通属性。
      • column 属性:表中普通字段的名称、别名
      • property 属性:实体对象属性名。

通过映射 columnproperty,MyBatis 就可以为我们自动的将查询到的数据正确的封装到 Java 实体对象中。


欢迎关注我的公众号,第一时间获取文章更新:

微信公众号

相关内容