6. Mybatis映射文件深入 – 新增数据后返回主键ID – 动态SQL – SQL片段

6. Mybatis映射文件深入 – 新增数据后返回主键ID – 动态SQL – SQL片段

前言

在前面的篇章中,我们已经认识了如何使用 resultMap标签 映射查询的结果集字段、多条件查询、模糊查询。

下面我们继续来深入认识一下 MyBatis 的映射文件。

新增数据后,返回主键 ID

应用场景

向数据库保存一个user对象后, 然后在控制台打印此新增user的主键值(id)

# 点外卖
 1. 点一份饭 -> 产生一个订单, 美团会往订单表插入一条数据(主键)
 2. 需要返回这条记录的主键, 然后给第三方配送平台, 送外卖

实现案例一:基于MyBatis 框架自带的主键返回功能

① UserMapper接口

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315105713661
/*
 *  # 复杂操作:插入一条数据返回对应的主键
 *    sql:
 *       insert into user values(null,?,?,?,?);
 *    参数: user(username,birthday,sex,address)
 *    返回值: int(其实是被影响的行数)
 *           void
 *
 *    对应的主键: user.id
 * */

int addUser(@Param("user") User user);

② UserMapper.xml

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315105752540
<!--
 方案一: 这表的主键必须是自增长的 auto_increment
      useGeneratedKeys="true" 让自增长的主键开启返回功能
      keyColumn="id"  user表中主键列
      keyProperty="id" user实体主键属性

      注意:支持主键自增类型的数据库 MySQL 和 SqlServer , oracle不支持
-->

<insert id="addUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
     insert into user values(null,#{user.username},#{user.birthday},#{user.sex},#{user.address})
</insert>

③ 测试

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315110218995
    // 测试插入数据, 返回主键ID
    @Test
    public void test11() throws Exception {
        // 1. 获取 sqlSession 数据库连接会话
        MyBatisUtil myBatisUtil = new MyBatisUtil();
        SqlSession sqlSession = myBatisUtil.getSqlSession();

        //2. 会话对象得到UserMapper接口的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //3. 执行插入一个user对象, 返回主键ID
        //创建user对象
        User user = new User();
        //设置username
        user.setUsername("李云龙");
        //设置字符串的日期
        String str = "2021-03-15";
        // 使用SimpleDateFormat将其解析为 Date 对象
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Java.util.Date date = simpleDateFormat.parse(str);
        // 再将date设置为 java.sql.Date
        java.sql.Date date1 = new java.sql.Date(date.getTime());
//        System.out.println(date1);
        // 设置user的生日
        user.setBirthday(date1);
        //设置 sex
        user.setSex("男");
        //设置 address
        user.setAddress("湖南嚼槟榔的那嘎达地方");

        //调用插入数据的方法
        int i = userMapper.addUser(user);
        System.out.println("插入数据的行数: " + i);

        //查询插入后返回的主键ID
        System.out.println("插入数据的主键ID: " + user.getId());

        //7. 关闭会话
        myBatisUtil.commitAndClose(sqlSession);
    }

实现案例二:基于SQL的 SELECT LAST_INSERT_ID() 语句

上面的案例一是采用MyBatis框架自动的主键返回功能,而有些时候,我们的主键有可能不是自增 ID,有可能是自己生成的 UUID。

这时候就需要采用一些 SQL 语句进行查询了。下面我们来模拟查询,如下:

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315125223706
-- 查询数据的同时,查询返回自增的ID
-- 注意:这两行SQL要按照顺序同时执行,否则单独查询 SELECT LAST_INSERT_ID(); 不会返回最新的自增ID
insert into user values(null"张飞""2021-03-15""男""三国"); -- 插入数据
SELECT LAST_INSERT_ID();  -- 查询最新的自增ID

在这里我们在插入数据之后,同时执行了查询最新自增ID的操作,从而获取自增的ID。

下面我们在代码来实现一下这个效果。

① UserMapper接口

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315110410329
public interface UserMapper {

    /*
     *  # 复杂操作:插入一条数据返回对应的主键
     *    sql:
     *       insert into user values(null,?,?,?,?);
     *    参数: user(username,birthday,sex,address)
     *    返回值: int(其实是被影响的行数)
     *           void
     *
     *    对应的主键: user.id
     * */

    //int addUser(@Param("user") User user); // 设置 @Param 参数注解后,在映射xml文件必须写 #{user.属性名}
    int addUser(User user)// 没有设置 @Param 参数注解,直接写 #{属性名} 即可
}   

② UserMapper.xml

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315125852891
<!--
    方案二: <selectKey>
     keyColumn="id" user表中主键列
     keyProperty="id" user实体主键属性
     resultType="int" user实体主键属性类型
     order="AFTER"  表示此标签内部sql语句在insert执行之前(执行),还是之后执行(执行)
        AFTER 之后执行【在自增主键时】
        BEFORE 之前执行【使用指定主键时】
-->

<insert id="addUser">
    <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
        SELECT LAST_INSERT_ID()
    </selectKey>
    insert into user values(null, #{username},#{birthday},#{sex},#{address})
</insert>

③ 测试

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315125959784
    // 测试插入数据, 返回主键ID
    @Test
    public void test11() throws Exception {
        // 1. 获取 sqlSession 数据库连接会话
        MyBatisUtil myBatisUtil = new MyBatisUtil();
        SqlSession sqlSession = myBatisUtil.getSqlSession();

        //2. 会话对象得到UserMapper接口的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //3. 执行插入一个user对象, 返回主键ID
        //创建user对象
        User user = new User();
        //设置username
        user.setUsername("李云龙");
        //设置字符串的日期
        String str = "2021-03-15";
        // 使用SimpleDateFormat将其解析为 Date 对象
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        java.util.Date date = simpleDateFormat.parse(str);
        // 再将date设置为 java.sql.Date
        java.sql.Date date1 = new java.sql.Date(date.getTime());
//        System.out.println(date1);
        // 设置user的生日
        user.setBirthday(date1);
        //设置 sex
        user.setSex("男");
        //设置 address
        user.setAddress("湖南嚼槟榔的那嘎达地方");

        //调用插入数据的方法
        int i = userMapper.addUser(user);
        System.out.println("插入数据的行数: " + i);

        //查询插入后返回的主键ID
        System.out.println("插入数据的主键ID: " + user.getId());

        //7. 关闭会话
        myBatisUtil.commitAndClose(sqlSession);
    }

2.2 动态SQL

2.2.1 什么是动态SQL

动态SQL就是基于不同的查询情况,动态改变不同的查询条件,组合查询出来的结果。

下面我们使用一个超简单的查询需求来说明什么是动态SQL。

需求

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
1589022259909

基于【编号】和 【用户名】来搜索用户,而【编号】和【用户名】都可能为空。

实现方式

把id和username封装到user对象中,将user对象中不为空的属性作为查询条件。

这个时候我们执行的sql就有多种可能。

-- 如果id和用户名不为空
select * from user where id#{id} and username = #{username}

-- 如果只有id
select * from user where id#{id} 

-- 如果只有用户名
select * from user where username = #{username}

-- 如果id和用户名都为空
select * from user

像上面这样, 根据传入的参数不同, 需要执行的SQL的结构就会不同,这就是动态SQL

而对于在映射配置文件中动态设置SQL的话,我们就需要熟悉一下相关判断的标签文件。

2.2.2 if 条件判断

需求

把id和username封装到user对象中,将user对象中不为空的属性作为查询条件

① UserMapper接口

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315152119968
public interface UserMapper {
    /*
     * # 动态sql
     *   需求: 查询符合一定条件的用户(条件是可选的)
     *       select * from user
     *           where
     *               id = ?      -- 可能有
     *           and username = ?  -- 可能有
     *
     *   参数: int id, String username
     *   返回值: List<U>
     * */

    List<User> findUsersByIdAndUserNameIf(@Param("id") String id, @Param("username") String username);
}

② UserMapper.xml

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315161407282
    <!--
        java 逻辑:
            username = null;  都没有意义
            username = "";

            if(username != null && !username.equals("")){
                 and username = ?
            }

      sql逻辑:
           if标签: 必要属性test (写判断条件)
                满足条件,拼接sql

           where标签: 根据最终的条件,动态修改成合适的语法

    -->

    <select id="findUsersByIdAndUserNameIf" resultType="user">
        select * from user
        <where>
            <if test="id != null and id != ''">
                id = #{id}
            </if>
            <if test="username != null and username != ''">
               and username = #{username}
            </if>
        </where>
    </select>

③ 测试

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315162703515
// 测试根据 id 以及 username 查询用户
@Test
public void test12() throws Exception {
    // 1. 获取 sqlSession 数据库连接会话
    MyBatisUtil myBatisUtil = new MyBatisUtil();
    SqlSession sqlSession = myBatisUtil.getSqlSession();

    //2. 会话对象得到UserMapper接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    //3. 执行根据 id 以及 username 查询用户
    //3.1 同时查询 id 、username
    List<User> users = userMapper.findUsersByIdAndUserNameIf("20""李云龙");
    for (User user : users) {
        System.out.println("3.1 同时查询 id 、username: " + user);
    }
    //3.2 单独查询 id
    List<User> users2 = userMapper.findUsersByIdAndUserNameIf("19""");
    for (User user : users2) {
        System.out.println("3.2 单独查询 id: " + user);
    }
    //3.3 单独查询 username
    List<User> users3 = userMapper.findUsersByIdAndUserNameIf("""豹子精");
    for (User user : users3) {
        System.out.println("3.2 单独查询 username: " + user);
    }

    //7. 关闭会话
    myBatisUtil.commitAndClose(sqlSession);
}

2.2.3 set 用于update语句

需求

动态更新user表数据,如果该属性有值就更新,没有值不做处理

① UserMapper接口

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315172059455
    /*
     * # 动态sql
     *   需求: 修改某个用户信息
     *       update user
     *           set username = ?,   -- 动态
     *               birthday = ?,    -- 动态
     *               sex = ?,         -- 动态
     *               address = ?      -- 动态
     *          where id = ?;
     *   参数: User
     *   返回值: void
     *
     * */

    int updateUserById(User user);

② UserMapper.xml

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315172300336
    <!--
        set标签: 用在update语句中,动态修改sql语法
    -->

    <update id="updateUserById">
        update user
        <set>
            <if test="username != null and username != ''">
                username = #{username},
            </if>
            <if test="birthday != null and birthday != ''">
                birthday = #{birthday},
            </if>
            <if test="sex != null and sex != ''">
                sex = #{sex},
            </if>
            <if test="address != null and address != ''">
                address = #{address}
            </if>
        </set>
        where id = #{id}
    </update>

③ 测试

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315192700694
// 测试根据 id 修改用户信息
@Test
public void test13() throws Exception {
    // 1. 获取 sqlSession 数据库连接会话
    MyBatisUtil myBatisUtil = new MyBatisUtil();
    SqlSession sqlSession = myBatisUtil.getSqlSession();

    //2. 会话对象得到UserMapper接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    //3. 更具ID修改用户信息
    User user = new User();
    user.setUsername("小王");
    user.setSex("女");

    user.setId(20); // 修改ID为20的用户
    //update user SET username = ?, sex = ? where id = ?
    userMapper.updateUserById(user);
    
    //7. 关闭会话
    myBatisUtil.commitAndClose(sqlSession);
}

2.2.4 foreach 用于循环遍历【重点】

当如果查询条件是 in (….) 之类的需要遍历查询条件的情况,此时就需要使用 foreach 标签来处理了。

需求

根据多个id查询,user对象的集合

select * from user where id in (41,43,46);
<foreach>标签用于遍历集合,它的属性:

    • collection:代表要遍历的集合元素

    • open:代表语句的开始部分

    • close:代表结束部分

    • item:代表遍历集合的每个元素,生成的变量名

    • sperator:代表分隔符

练习二个版本

  1. 普通list集合 作为 查询条件参数的容器
  2. 普通array数组 作为 查询条件参数的容器

① UserMapper

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315193152470
    /*
    * # 动态sql3
    *   需求: 查询某几个用户信息
    *       select * from user where id in
    *            (1,3,4)   -> 动态的
    *
    *   参数: List<Integer> list / int[] array
    *   返回值: List<User>
    * */

    List<User> findUsersByIds(List<Integer> list)// 普通list集合 作为 查询条件参数的容器

    List<User> findUsersByIds2(int[] array)// 普通array数组 作为 查询条件参数的容器

② UserMapper.xml

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315193300734
<!--
    foreach 标签(遍历)
        1. collection属性: 被遍历的容器类型
            list/array
        2. item : 被遍历出来的元素
        3. open: 遍历开始时的内容
        4. close: 遍历结束的内容
        5. separator : 每遍历一次就添加一次的分隔符(最后一次遍历不加)

    距离: list = {1,2,3}

        遍历: (1,2,3)
-->

<select id="findUsersByIds" resultType="user">
    select * from user where id in
    <foreach collection="list" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>

<select id="findUsersByIds2" resultType="user">
    select * from user where id in
    <foreach collection="array" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>

③ 测试

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315193639272
// 测试 in 条件的查询
@Test
public void test14() throws Exception {
    // 1. 获取 sqlSession 数据库连接会话
    MyBatisUtil myBatisUtil = new MyBatisUtil();
    SqlSession sqlSession = myBatisUtil.getSqlSession();

    //2. 会话对象得到UserMapper接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    // 以集合保存 in 条件参数
    List<Integer> list = new ArrayList<>();
    Collections.addAll(list,1,3,4);
    List<User> result = userMapper.findUsersByIds(list);
    System.out.println("以集合保存 in 条件参数: " + result);

    // 以数组保存 in 条件参数
    int[] array = {2,4,6};
    List<User> result2 = userMapper.findUsersByIds2(array);
    System.out.println("以数组保存 in 条件参数: " + result2);

    //7. 关闭会话
    myBatisUtil.commitAndClose(sqlSession);
}

2.3 SQL片段

应用场景

映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的。

应用案例

①  寻找重复的 sql

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315193930864

② UserMapper.xml

6. Mybatis映射文件深入 - 新增数据后返回主键ID - 动态SQL - SQL片段
image-20210315194113121
    <select id="findUsersByIds" resultType="user">
--         select * from user
        <include refid="selectUser"/>
        where id in
        <foreach collection="list" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

    <select id="findUsersByIds2" resultType="user">
--         select * from user
        <include refid="selectUser"/>
        where id in
        <foreach collection="array" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

    <!--
        sql片段: 抽取重复出现的sql语句
        在其他的语句中可以
            include标签进行引用
    -->

    <sql id="selectUser">
         select * from user
    </sql>

设置之后,再使用上面 foreach 标签的测试代码执行一下验证是否正常执行即可。

2.4 知识小结

MyBatis映射文件配置

<select>:查询

<insert>:插入

<update>:修改

<delete>:删除

<selectKey>:插入返回主键

<where>:where条件

<if>:if判断

<foreach>:for循环

<set>:set设置

<sql>:sql片段抽取


原文始发于微信公众号(海洋的渔夫):6. Mybatis映射文件深入 – 新增数据后返回主键ID – 动态SQL – SQL片段

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/33740.html

(0)

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!