• mybatis 10: 动态sql --- part2


    < foreach >标签

    作用

    • 用来进行循环遍历,完成循环条件的查询,批量删除,批量增加,批量更新

    用法

    • 包括循环查询 + 批量删除 + 批量增加 + 批量更新的用法

    UsersMapper.java

    package com.example.mapper;
    
    import com.example.pojo.User;
    
    import java.util.List;
    
    /**
     * 数据访问层的接口,定义对数据库完成的CRUD的操作
     */
    public interface UsersMapper {
    
    
        //循环查询
        List getByIds(Integer []id_array);
    
        //批量删除
        int deleteByIds(Integer []id_array);
    
        //批量插入
        int insertBatch(List users);
        
        //批量更新
        int updateBatch(List users);
    }
    

    UsersMapper.xml

    
    mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.example.mapper.UsersMapper">
    
        
        <select id="getByIds" resultType="user">
            select
                <include refid="allColumns"/>
            from
                users
            where
                id
            in
                <foreach collection="array" item="id" open="(" close=")" separator=",">
                    #{id}
                foreach>
        select>
    
    
        
        <delete id="deleteByIds">
            delete from
                       users
            where
                id
            in
            <foreach collection="array" item="id" separator="," open="(" close=")">
                #{id}
            foreach>
        delete>
        
        
        
        <insert id="insertBatch">
            insert into
                users(username, birthday, sex, address)
            values
                <foreach collection="list" item="user" separator=",">
                    (#{user.userName}, #{user.birthday}, #{user.sex}, #{user.address})
                foreach>
        insert>
        
        
        
        <update id="updateBatch">
            <foreach collection="list" item="user" separator=";">
                update
                    users
                <set>
                    <if test="user.userName != null and user.userName != ''">
                        username=#{user.userName},
                    if>
    
                    <if test="user.birthday != null">
                        birthday=#{user.birthday},
                    if>
    
                    <if test="user.sex != null and user.sex != ''">
                        sex=#{user.sex},
                    if>
    
                    <if test="user.address != null and user.address != ''">
                        address=#{user.address},
                    if>
                set>
                where
                    id=#{user.id}
            foreach>
        update>
    mapper>
    

    映射文件分析

    • 当入参是数组时,parameterType可以不写,

      • 其实只有当入参类型是实体类时,才必须指明其类型,其他类型的数据皆可不写
    • < foreach >标签的属性说明

      • collection:指明待遍历的数据容器的类型,可选的有三个:array,list,map
      • item:给遍历出的每个元素指定一个名称,便于在标签内使用
      • open 和 close:原先sql语句,in后面待遍历的数据放在括号中,这里通过标签属性的形式来实现
        • 可以将sql语句和标签的书写分隔开,使得代码更加直观
        • 注意:这是循环外层的括号,用来容纳所有的元素,循环内部的元素自身的括号不能用这对标签,要手动加上
      • 是否要使用 open 和 close标签的说明:
       
      <foreach collection="array" item="id" separator="," open="(" close=")">
           #{id}
       foreach>
      
      
       <foreach collection="list" item="user" separator=",">
           
           
           (#{user.userName}, #{user.birthday}, #{user.sex}, #{user.address})		
       foreach>
      
      
      
    
    • separator:遍历出的元素之间用什么符号分隔,mybatis框架会确保间隔符号的正确使用

      • 正确使用是指:在指定需要的间隔符后,间隔符号的个数以及出现在元素之间的位置,由mybatis自动正确放置(如果有多个元素的话)
    • 进行批量更新操作时

      • 本质:由底层解析出的sql语句可知,本质执行的是多条独立的update语句
        • 这也决定了< update >标签内应该是一个 < foreach >标签,分隔符应该是";",用来间隔多条相互独立的update语句
      • 注意:这与执行一条update语句,影响多行记录是不同的,必须在jdbc.properties文件中的url的值后新增配置:allowMultiQueries=true
      jdbc.driverClassName=com.mysql.cj.jdbc.Driver
      jdbc.url=jdbc:mysql://ip:3306/ssm?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
      jdbc.username=XXXX
      jdbc.password=YYYY
    
    • 不修改jdbc.properties时报错
      image

    • 各sql标签在底层分别被解析为

    //批量查询
    
    ==>  Preparing: select id, username, birthday, sex, address from users where id in ( ? , ? , ? )
    
    //批量删除
    
    ==>  Preparing: delete from users where id in ( ? , ? )
    
    //批量插入
    
    ==>  Preparing: insert into users(username, birthday, sex, address) values (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?)
    
    //批量更新
    
    ==>  Preparing: 
    update users SET username=?, birthday=?, sex=?, address=? where id=? ;
    update users SET username=?, birthday=?, sex=?, address=? where id=? ; 
    update users SET username=?, birthday=?, sex=?, address=? where id=?
    

    测试代码

    package com.example.mapper;
    
    import com.example.pojo.User;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Array;
    
    import java.util.List;
    
    
    public class TestUsersMapper {
    
        //时间刷
        SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
    
        //SqlSession对象
        SqlSession sqlSession;
    
        //mybatis动态代理对象
        UsersMapper usersMapper;
    
        //获取SqlSession
        @Before
        public void getSqlSession() throws IOException {
            //读取核心配置文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //创建SqlSessionFactory对象
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            //获取SqlSession
            sqlSession = factory.openSession();
            //获取mybatis动态代理对象
            usersMapper = sqlSession.getMapper(UsersMapper.class);
        }
    
        //归还SqlSession
        @After
        public void closeSession(){
            sqlSession.close();
        }
    
        //循环查询测试
        @Test
        public void testGetByIds(){
            Integer []id_array = {1, 3, 5};
            List users = usersMapper.getByIds(id_array);
            users.forEach(System.out::println);
        }
    
        //批量删除测试
        @Test
        public void testDeleteByIds(){
            Integer []id_array = {3, 29};
            int num = usersMapper.deleteByIds(id_array);
            if(num == 2){
                System.out.println("批量删除成功!");
                sqlSession.commit();
            }else{
                System.out.println("批量删除失败!");
            }
        }
        
        //批量插入测试
        @Test
        public void testInsertBatch() throws ParseException {
            User u1 = new User("西决", date.parse("2001-01-01"), "男", "北京");
            User u2 = new User("南音", date.parse("2002-02-02"), "女", "北京");
            User u3 = new User("北北", date.parse("2003-03-03"), "男", "北京");
    
            List users = new ArrayList<>();
            users.add(u1);
            users.add(u2);
            users.add(u3);
    
            int num = usersMapper.insertBatch(users);
            if(num == 3){
                System.out.println("批量插入成功!");
                sqlSession.commit();
            }else{
                System.out.println("批量插入失败!");
            }
        }
        
        //批量更新测试
        @Test
        public void testUpdateBatch() throws ParseException {
            User u1 = new User(31,"西决2", date.parse("2001-01-01"), "男", "北京");
            User u2 = new User(32,"南音2", date.parse("2002-02-02"), "女", "北京");
            User u3 = new User(33,"北北2", date.parse("2003-03-03"), "男", "北京");
    
            List users = new ArrayList<>();
            users.add(u1);
            users.add(u2);
            users.add(u3);
    
            int num = usersMapper.updateBatch(users);
            if(num == 1){
                System.out.println("批量更新成功!");
                sqlSession.commit();
            }else{
                System.out.println("批量更新失败!");
            }
        }
    }
    

    测试代码分析(着重分析一下批量更新)

    • 在执行批量更新操作时,为什么数据表的记录明明修改了3条,输出结果中更新结果的返回值却是1呢?
    • 更新后的数据表,修改了3条记录

    image

    • 批量更新后的返回结果
    <==    Updates: 1
    批量更新成功!
    
    • 原因:

      • 首先要区别< update >标签和update语句:在 < update >标签中有一个< foreach >标签,他循环了3条相互独立的update语句
      • 注意:每条update语句恰巧都只是修改了一条记录,所以< update >标签返回3次1后结束,因为循环3次后循环标签结束了
      • 对于如下测试代码,num其实被赋值3次,num的值是最后一次赋值的结果,本次测试恰好是:1
        • 其实笔者不太确定num是否被赋值了3,但是根据底层输出的结果可知,起作用的一定是最后一条update语句影响的记录条数
        • 不是循环的3条update语句影响条数的和,因为输出结果:是1,而不是3
       int num = usersMapper.updateBatch(users);
    

    输出结果

    //批量查询结果
    
    Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object]
    Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object]
    Opening JDBC Connection
    Created connection 16148478.
    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f667fe]
    ==>  Preparing: select id, username, birthday, sex, address from users where id in ( ? , ? , ? )
    ==> Parameters: 1(Integer), 3(Integer), 5(Integer)
    <==    Columns: id, username, birthday, sex, address
    <==        Row: 1, 荷包蛋, 2002-08-23, 女, 黑河市
    <==        Row: 3, 小张, 1999-02-22, 1, 长沙
    <==        Row: 5, 段, 2001-03-10, 1, 太原
    <==      Total: 3
    Users{id=1, userName='荷包蛋', birthday=Fri Aug 23 00:00:00 CST 2002, sex='女', address='黑河市'}
    Users{id=3, userName='小张', birthday=Mon Feb 22 00:00:00 CST 1999, sex='1', address='长沙'}
    Users{id=5, userName='段', birthday=Sat Mar 10 00:00:00 CST 2001, sex='1', address='太原'}
    Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f667fe]
    Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f667fe]
    Returned connection 16148478 to pool.
    
    Process finished with exit code 0
    
    //批量删除结果
    
    Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object]
    Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object]
    Opening JDBC Connection
    Created connection 544966217.
    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
    ==>  Preparing: delete from users where id in ( ? , ? )
    ==> Parameters: 3(Integer), 29(Integer)
    <==    Updates: 2
    批量删除成功!
    Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
    Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
    Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@207b8649]
    Returned connection 544966217 to pool.
    
    Process finished with exit code 0
    
    //批量增加结果
    
    Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object]
    Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object]
    Opening JDBC Connection
    Created connection 749604930.
    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2cae1042]
    ==>  Preparing: insert into users(username, birthday, sex, address) values (?, ?, ?, ?) , (?, ?, ?, ?) , (?, ?, ?, ?)
    ==> Parameters: 西决(String), 2001-01-01 00:00:00.0(Timestamp), 男(String), 北京(String), 南音(String), 2002-02-02 00:00:00.0(Timestamp), 女(String), 北京(String), 北北(String), 2003-03-03 00:00:00.0(Timestamp), 男(String), 北京(String)
    <==    Updates: 3
    批量插入成功!
    Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2cae1042]
    Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2cae1042]
    Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2cae1042]
    Returned connection 749604930 to pool.
    
    Process finished with exit code 0
    
    //批量更新结果
    
    Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object]
    Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object]
    Opening JDBC Connection
    Created connection 1718322084.
    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@666b83a4]
    ==>  Preparing: update users SET username=?, birthday=?, sex=?, address=? where id=? ; update users SET username=?, birthday=?, sex=?, address=? where id=? ; update users SET username=?, birthday=?, sex=?, address=? where id=?
    ==> Parameters: 西决2(String), 2001-01-01 00:00:00.0(Timestamp), 男(String), 北京(String), 31(Integer), 南音2(String), 2002-02-02 00:00:00.0(Timestamp), 女(String), 北京(String), 32(Integer), 北北2(String), 2003-03-03 00:00:00.0(Timestamp), 男(String), 北京(String), 33(Integer)
    <==    Updates: 1
    批量更新成功!
    Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@666b83a4]
    Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@666b83a4]
    Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@666b83a4]
    Returned connection 1718322084 to pool.
    
    Process finished with exit code 0
    
  • 相关阅读:
    C#Winform新建工程
    vite+rollup
    java计算机毕业设计西安财经大学校园一卡通管理系统源码+系统+数据库+lw文档+mybatis+运行部署
    无法对wsl-docker-data本身的unbutu镜像扩容操作
    【数据可视化】第四章—— 基于pandas的数据可视化(pandas基本操作)
    LLVM编译技术应用分析
    stm32-SPI协议
    C. Card Game
    IDEA 28 个天花板技巧 + 12 款神级插件,生产力起飞...
    OWASP Top 10 2022 介紹
  • 原文地址:https://www.cnblogs.com/nefu-wangxun/p/16586821.html