mybatis 必备手册

前言

  • 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

  • 使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

  • 这篇文章总结了日常的增删改查,重点关注@Param的注解以及批量操作相关动作。

  • 这篇文章总结了比较通用的自定义SQL的语法,包含的标签包含if、choose (when, otherwise)、trim (where, set)、foreach等。


准备

CREATE TABLE `t_student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(255) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `clazz_id` int(11) DEFAULT NULL COMMENT '班级id',
  `number` varchar(6) DEFAULT NULL COMMENT '学号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC


public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private Integer clazzId;
    private String number;
}
  • 前置准备的数据库表字段和对应的类对象定义。


常用SQL

select语句

  <sql id="base_columns">
    id, name, age, clazz_id, number
  </sql>

  <select id="selectById" resultMap="BaseResultMap">
    select <include refid="base_columns"/> from t_student where id = #{id}
  </select>

  Student selectById(@Param("id") int id);
  • 根据单个id进行查询的例子。


  <sql id="base_columns">
    id, name, age, clazz_id, number
  </sql>

  <select id="selectByIds" resultMap="BaseResultMap">
    SELECT <include refid="base_columns"/> from t_student where id in
    <foreach collection="ids" separator="," item="id" open="(" close=")">
      #{id}
    </foreach>
    AND age=#{age}
  </select>

  List<Student> selectByIds(@Param("ids") List<Integer> idList, @Param("age") Integer age);
  • 根据多个ids进行查询例子,前提是保证ids不为空。


  <sql id="base_columns">
    id, name, age, clazz_id, number
  </sql>

  <select id="selectByIdsV2" resultMap="BaseResultMap">
    SELECT <include refid="base_columns"/> from t_student
    <where>
      <if test="ids != null and ids.size() > 0">
        id in
        <foreach collection="ids" open="(" close=")" separator=",">
          #{id}
        </foreach>
      </if>
      <if test="age != null">
        AND age = #{age}
      </if>
    </where>
  </select>

  List<Student> selectByIdsV2(@Param("ids") List<Integer> idList, @Param("age") Integer age);
  • <foreach>的collection等价于@Param注解的变量,如例子中的ids。
  • 通过<where>注解解决idList为空的场景。
  • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。


  <sql id="base_columns">
    id, name, age, clazz_id, number
  </sql>

  <select id="selectByIdsV3" resultMap="BaseResultMap">
    SELECT <include refid="base_columns"/> from t_student
    <where>
      <if test="studentList != null and studentList.size() > 0">
        id in
        <foreach collection="studentList" item="item" open="(" close=")" separator=",">
          #{item.id}
        </foreach>
      </if>
      <if test="age != null">
        AND age = #{age}
      </if>
    </where>
  </select>

  List<Student> selectByIdsV3(@Param("studentList") List<Student> studentList, @Param("age") Integer age);
  • <foreach>的collection等价于@Param注解的变量,如例子中的studentList。
  • 通过<where>注解解决studentList为空的场景。
  • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。


  <sql id="base_columns">
    id, name, age, clazz_id, number
  </sql>

  <select id="selectByIdsV4" resultMap="BaseResultMap">
    SELECT <include refid="base_columns"/> from t_student
    <trim prefix="where" prefixOverrides="and|or">
      <if test="studentList != null and studentList.size() > 0">
        id in
        <foreach collection="studentList" item="item" open="(" close=")" separator=",">
          #{item.id}
        </foreach>
      </if>
      <if test="age != null">
        AND age = #{age}
      </if>
    </trim>
  </select>

  List<Student> selectByIdsV4(@Param("studentList") List<Student> studentList, @Param("age") Integer age);
  • 过自定义 trim 元素来定制 where 元素的功能。
  • prefix 属性中指定前置插入的内容
  • prefixOverrides 属性会忽略前置的通过管道符分隔的文本序列。
  • suffix 属性中指定后置插入的内容
  • suffixOverrides属性会忽略后置的通过管道符分隔的文本序列。


insert语句

    <insert id="insertV1" parameterType="com.example.model.Student" keyProperty="id" useGeneratedKeys="true">
      insert into t_student (`name`, age, clazz_id, `number`)
      values (#{name}, #{age}, #{clazzId}, #{number})
    </insert>

    Integer insertV1(Student student);
  • 通用的插入单个对象的例子。


    <insert id="insertV2" parameterType="com.example.model.Student" keyProperty="id" useGeneratedKeys="true">
        insert into t_student (`name`, age, clazz_id, `number`)
        values (#{student.name}, #{student.age}, #{student.clazzId}, #{student.number})
    </insert>

    Integer insertV2(@Param("student") Student student);
  • 通过@param指定别名student来进行单个对象的插入。


  <insert id="batchInsertV1">
    insert into t_student (`name`, age, clazz_id, `number`)
    values
    <foreach collection="list" item="item" separator=",">
      (#{item.name}, #{item.age}, #{item.clazzId}, #{item.number})
    </foreach>
  </insert>

  Integer batchInsertV1(List<Student> studentList);
  • 批量插入多个对象的例子,没有指定别名通用的collection是list,注意foreach没有open和close属性。


  <insert id="batchInsertV2">
    insert into t_student (`name`, age, clazz_id, `number`)
    values
    <foreach collection="studentList" item="item" separator=",">
      (#{item.name}, #{item.age}, #{item.clazzId}, #{item.number})
    </foreach>
  </insert>

  Integer batchInsertV2(@Param("studentList") List<Student> studentList);
  • 批量插入多个对象的例子,通过@Param指定别名studentList,对应的collection为别名变量。


delete语句

  <delete id="deleteById" parameterType="java.lang.Integer">
    delete from t_student where id = #{id}
  </delete>

  int deleteById(@Param("id") int id);
  • 通用的单个对象的删除例子。


  <delete id="batchDetele">
    delete from t_student
    <if test="idList != null and idList.size() > 0">
        where id in
        <foreach collection="idList" item="item" separator="," open="(" close=")">
            #{item}
        </foreach>
    </if>
  </delete>

  int batchDetele(@Param("idList") List<Integer> idList);
  • 批量删除的例子,通过@Param执行别名idList,注意<foreach>在这种场景指定open和close对应的值。


update语句

    <update id="updateV1" parameterType="com.example.model.Student">
        update t_student
        <set>
            <if test="name != null">`name` = #{name},</if>
            <if test="age != null" >age = #{age},</if>
            <if test="clazzId != null">clazzId = #{clazzId},</if>
            <if test="number != null">`number` = #{number}</if>
        </set>
        where id = #{id}
    </update>

    Integer updateV1(Student student);
  • 用于动态更新语句的类似解决方案叫做 set。
  • set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
  • set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。


    <update id="updateV2" parameterType="com.example.model.Student">
        update t_student
        <trim prefix="set" suffixOverrides=",">
            <if test="name != null">`name` = #{name},</if>
            <if test="age != null" >age = #{age},</if>
            <if test="clazzId != null">clazzId = #{clazzId},</if>
            <if test="number != null">`number` = #{number},</if>
        </trim>
        where id = #{id}
    </update>

    Integer updateV2(Student student);
  • 通过自定义 trim 元素来实现等价的set操作。


    <update id="batchUpdate" parameterType="java.util.List">
        update t_student
        <trim prefix="set" suffixOverrides=",">

          <trim prefix="name = case " suffix="end,">
            <foreach collection="studentList" item="item">
              <if test="item.name != null">
                  when id = #{item.id} then #{item.name}
              </if>
            </foreach>
          </trim>

          <trim prefix="age = case " suffix="end,">
              <foreach collection="studentList" item="item">
                  <if test="item.age != null">
                      when id = #{item.id} then #{item.age}
                  </if>
              </foreach>
          </trim>

          <trim prefix="clazzId = case " suffix="end,">
              <foreach collection="studentList" item="item">
                  <if test="item.clazzId != null">
                      when id = #{item.id} then #{item.clazzId}
                  </if>
              </foreach>
          </trim>

          <trim prefix="number = case " suffix="end,">
              <foreach collection="studentList" item="item">
                  <if test="item.number != null">
                      when id = #{item.id} then #{item.number}
                  </if>
              </foreach>
          </trim>
        </trim>

        where id in
        <foreach collection="studentList" item="item" open="(" close=")" separator=",">
            #{item.id}
        </foreach>
    </update>

    Integer batchUpdate(@Param("studentList") List<Student> studentList);
  • 通过自定义trim标签来实现批量更新操作。


参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,074评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,181评论 3 410
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 173,579评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,741评论 1 304
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,675评论 6 404
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,144评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,344评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,429评论 0 282
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,984评论 1 328
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,960评论 3 351
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,057评论 1 359
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,658评论 5 352
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,349评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,757评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,943评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,670评论 3 384
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,116评论 2 368

推荐阅读更多精彩内容