MyBatis基础2(XML)

XML模式开发顺序:

  1. 创建DO对象
  2. 创建DAO对象,配置@Mapper注解
  3. 创建XML文件,并完成resultMap配置
  4. 创建对应的XML语句

在MyBatis中使用XML首先需要在 application.properties 文件中添加配置。

mybatis.mapper-locations=classpath:com/youkeda/dao/*.xml,classpath:com/youkeda/comment/dao/*.xml

一般来说这个路径和DAO的包路径一致,一个DAO类对应一个XML文件

<?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="com.youkeda.comment.dao.UserDAO">
</mapper>

XML在写入头信息后需要继续添加mapper节点,节点存在 namespace 属性表示命名空间,一般这里所对应的DAO的全称。

resultMap

resultMap用于处理表和DO对象的属性映射,确保表中的每一个字段都有属性可以匹配

<mapper namespace="com.youkeda.comment.dao.UserDAO">

  <resultMap id="userResultMap" type="com.youkeda.comment.dataobject.UserDO">
    <id column="id" property="id"/>
    <result column="user_name" property="userName"/>
  </resultMap>

</mapper>

resultMap中的属性如下:

  • id 唯一标识,一般命名为 xxxResultMap
  • type 对应的DO类完整路径

resultMap的子节点:

  • id 设置数据库主键字段信息,column属性对应的是表的字段名,property对应的是DO属性名称
  • result 设置数据库其他字段信息,column属性对应的是表的字段名,property对应的是DO属性名称

resultMap也存在子节点 <association> 用于聚合其他对象:

              <association property="author" javaType="com.youkeda.comment.model.User">
            <id column="user_id" property="id"/>
            <result column="user_name" property="userName"></result>
            <result column="nick_name" property="nickName"></result>
            <result column="avatar" property="avatar"></result>
        </association>

其中 javaType 表示对应是数据模型,property 对应的是表中字段名

XML实现Insert功能

<insert id="add" parameterType="com.youkeda.comment.dataobject.UserDO" >
    INSERT INTO user (user_name, pwd, nick_name,avatar,gmt_created,gmt_modified)
    VALUES(#{userName}, #{pwd}, #{nickName}, #{avatar},now(),now())
  </insert>

属性:

  • id 同DAO类的方法名,同一个xml内是要唯一的。
  • parameterType 用于传递参数类型,一般是和DAO内对应方法的参数类型一致
  • 以上两个属性也适用于 update、delete、select 语句中

如之前的DAO内写入Insert方法,XML也可以修改主键设置

<insert id="add" parameterType="com.youkeda.comment.dataobject.UserDO" useGeneratedKeys="true" keyProperty="id">

与DAO内 @Options 注解效果相同

Update/Delete语句同理

<update id="update" parameterType="com.youkeda.comment.dataobject.UserDO">
        update user set nick_name=#{nickName},gmt_modified=now() where id=#{id}
</update>
 <delete id="delete">
        delete from user where id=#{id}
 </delete>

需要注意的是,因为我们在写删除语句的DAO时,语句为

int delete(@Param("id") long id);

@Param传入的参数(详见注解整理)是普通参数,默认情况下这类数据会被当做 Map 类型传入,而parameterType默认的类型就是 Map 因此这里可以把类型指定省略。

Select语句

select语句由于是获取数据,与上面几个语句不同,所以语法也不同

<select id="findByUserName" resultMap="userResultMap">
  select * from user where user_name=#{userName} limit 1
</select>

这里使用了 resultMap 属性,它的值一般为XML文件下 resultMap 节点的id值,可以将获取的数据通过 resultMap 节点集体映射。

if语句

XML模式是支持添加if语句的如:

<update id="update" parameterType="com.youkeda.comment.dataobject.UserDO">
  update user set
   <if test="nickName != null">
    nick_name=#{nickName},gmt_modified=now()
   </if>
   where id=#{id}
</update>

其中 if 存在属性 test 表明 if 的条件

set语句

在实际使用if语句中,可能会产生由于条件不满足而导致SQL语句错误,我们一般配合 set标签来完成 update

<update id="update" parameterType="com.youkeda.comment.dataobject.UserDO">
  update user
  <set>
    <if test="nickName != null">
      nick_name=#{nickName},
    </if>
    <if test="avatar != null">
      avatar=#{avatar},
    </if>
    gmt_modified=now()
  </set>
   where id=#{id}
</update>

这里通过 set 标签代替了SQL语句中的set ,但注意的是其中的逗号不能漏掉,且使用 <set> 系统会自动去除最后一个逗号,而不用担心哪一列才是最后一个。

if+select

先看例子

<select id="search" resultMap="userResultMap">
  select * from user where
    <if test="keyWord != null">
      user_name like CONCAT('%',#{keyWord},'%')
        or nick_name like CONCAT('%',#{keyWord},'%')
    </if>
    <if test="time != null">
      and  gmt_created <![CDATA[ >= ]]> #{time}
    </if>
</select>

这里 >=、<、<=、>、&、| 这类表达式会导致MyBatis解析失败,所以我们需要使用 <![CDATA[ key ]]> 来包裹

类似于 Insert中的 set 可以用 <set> 语句代替,select中的 where 也可以用 <where> 语句代替

   <where>
      <if test="keyWord != null">
          user_name like CONCAT('%',#{keyWord},'%')
            or nick_name like CONCAT('%',#{keyWord},'%')
      </if>
      <if test="time != null">
        and  gmt_created <![CDATA[ >= ]]> #{time}
      </if>
   </where>

循环语句

XML存在循环语句 <foreach>

<insert id="batchAdd" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user (user_name, pwd, nick_name,avatar,gmt_created,gmt_modified)
    VALUES
    <foreach collection="list" item="it" index="index" separator =",">
        (#{it.userName}, #{it.pwd}, #{it.nickName}, #{it.avatar},now(),now())
    </foreach >
</insert>

属性:

  • collection 指定集合的上下文参数名称,比如这里的list 对应 @Param(“list”)
  • item 指定遍历中每一个数据的变量的指代名,一边用it命名
  • index 集合的索引值名称指定
  • separator 遍历每条记录并添加分隔符
  • open 表示节点开始时的分隔符即所有数据前面的符号
  • close 表示节点结束时的分隔符即所有数据之后的符号

上面代码结果为:

INSERT INTO user (user_name, pwd, nick_name,avatar,gmt_created,gmt_modified)
    VALUES
    (?, ?, ?,?,now(),now()),
    (?, ?, ?,?,now(),now()),
    (?, ?, ?,?,now(),now())

MyBatis会自动优化最后一个逗号,确保SQL正确。

添加上open和close:

<select id="findByIds" resultMap="userResultMap">
    select * from user
    <where>
        id in
        <foreach item="item" index="index" collection="ids"
                    open="(" separator="," close=")">
            #{item}
        </foreach>
    </where>
</select>

其结果为:

select * from user where id in (?,?,?)

配置优化

当数据JSON输出时,会存在null数据,我们需要忽略它来节约流量,通过修改配置文件 application.properties 来完成

spring.jackson.deserialization.fail-on-unknown-properties=false
spring.jackson.default-property-inclusion=non_null

两个配置的作用是:

  1. 允许序列化未知字段,可以兼容Java模型和JSON数据不一致的情况
  2. 忽略null字段