Mybatis动态SQL查询 --(附实战案例--8888个字--88质量分)

Posted 会洗碗的CV工程师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis动态SQL查询 --(附实战案例--8888个字--88质量分)相关的知识,希望对你有一定的参考价值。

目录

前言

一、动态SQL---if标签

1. 持久层添加用户通用查询方法

2. 映射文件添加相关标签

3. 测试类新建测试方法

4. 运行结果

二、动态SQL---where标签

1. 映射文件添加相关标签

2. 测试类新建测试方法

3. 运行结果

三、动态SQL---set标签

1. 持久层添加用户更新方法

2. 映射文件添加相关标签

3. 测试类新建测试方法

4. 运行结果

四、动态SQL---choose和wen和otherwise标签

1. 持久层添加用户通用查询方法

2. 映射文件添加相关标签

3. 测试类新建测试方法

4. 运行结果

五、动态SQL---foreach标签

1. 遍历数组

(1)持久层添加用户批量删除方法

(2)映射文件添加相关标签

(3)测试类新建测试方法

(4)运行结果

2. 遍历List

(1)持久层添加用户批量增加方法

(2)映射文件添加相关标签

(3)测试类新建测试方法

(4)运行结果

3. 遍历Map

(1)持久层添加多条件查询方法

(2)映射文件添加相关标签

(3)测试类新建测试方法

(4)运行结果

后言----查错


前言

        由于这是动态SQL,和先前得有些不一样,这里我们新建一个持久层接口UserMapper2和Mybatis映射文件UserMapper2.xml,测试类TestUserMapper2

 持久层接口UserMapper2

package com.mybatisstudy.mapper;

import com.mybatisstudy.pojo.User;

public interface UserMapper2 
    

 Mybatis映射文件UserMapper2.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.mybatisstudy.mapper.UserMapper2">
</mapper>

 测试类TestUserMapper2

import com.mybatisstudy.mapper.UserMapper2;
import com.mybatisstudy.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.InputStream;

public class TestUserMapper2 
    InputStream is = null;
    SqlSession session = null;
    UserMapper2 userMapper2 = null;

    //前置方法,不必重复代码
    @Before
    public void before() throws Exception 
        System.out.println("前置方法执行·············");
        // (1)读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        // (2)创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        // (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        // (4)SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
        // (5)SqlSession对象获取代理对象
        userMapper2 = session.getMapper(UserMapper2.class);
    

    //后置方法,释放资源
    @After
    public void after() throws Exception 
        System.out.println("后置方法执行·············");
        session.close();
        is.close();
    

一、动态SQL---if标签

        一个查询的方法的Sql语句不一定是固定的。比如电商网站的查询商品,用户使用不同条件查询,Sql语句就会添加不同的查询条件。此时就需要在方法中使用动态Sql语句。

        <if> 标签内的Sql片段在满足条件后才会添加,用法为: <if test="条件"> 。例如:根据不同条件查询用户:

1. 持久层添加用户通用查询方法

// 用户通用查询
    List<User> findByCondition(User user);

2. 映射文件添加相关标签

<!-- 动态_if -->
    <select id="findByCondition" parameterType="com.mybatisstudy.pojo.User" resultType="com.mybatisstudy.pojo.User">
        select * from user where 1 = 1
        <if test="username != null and username.length()!=0">
            and username like #username
        </if>
        <if test="sex != null and sex.length() != 0">
            and sex = #sex
        </if>
        <if test="address != null and address.length() != 0">
            and address = #address
        </if>
    </select>

        这里肯定就会有读者会问 where 后面为什么要加1 = 1,因为单单使用if标签的话,第一个条件是不用 and 关键字的,而后续的条件是需要加 and 关键字的。但是用户添加条件是随机的,没办法判断哪一个是第一个条件,因此在这里先添加1 = 1,就无需考虑后续的条件是否是第一个条件啦,但是后面还有更好的办法解决这个问题,在这里只是可以用这个方法解决目前的问题。

3. 测试类新建测试方法

// 测试通用查询方法
    @Test
    public void testFindByCondition()
        User user = new User();

        System.out.println("----------没有限制条件查询---------");
        List<User> users = userMapper2.findByCondition(user);
        users.forEach(System.out::println);

        System.out.println("----------用户名带有name限制条件查询---------");
        user.setUsername("%name%");
        List<User> users1 = userMapper2.findByCondition(user);
        users1.forEach(System.out::println);

        System.out.println("----------姓名带有name和性别是man限制条件查询---------");
        user.setSex("woman");
        List<User> users3 = userMapper2.findByCondition(user);
        users3.forEach(System.out::println);
    

4. 运行结果

  1. if中的条件不能使用&&/||,而应该使用and/or

  2. if中的条件可以直接通过属性名获取参数POJO的属性值,并且该值可以调用方法。 

二、动态SQL---where标签

OK,上述的问题的彩蛋来了,就是这个where标签,

<where> 可以代替sql中的where 1=1 和第一个and,更符合程序员的开发习惯,使用 <where> 后的映射文件如下:

1. 映射文件添加相关标签

<!-- 动态_if用where -->
    <select id="findByCondition" resultType="User" parameterType="User">
        select * from user
        <where>
            <if test="username != null and username.length()!=0">
                username like #username
            </if>
            <if test="sex != null and sex.length() != 0">
                and sex = #sex
            </if>
            <if test="address != null and address.length() != 0">
                and address = #address
            </if>
        </where>
    </select>

2. 测试类新建测试方法

这里不用新增,直接测试即可

3. 运行结果

因此我们以后就不用添加那个什么1 = 1了 

三、动态SQL---set标签

        <set> 标签用在update语句中。借助 <if> ,可以只对有具体值的字段进行更新。 <set> 会自动添加set关键字,并去掉最后一个if语句中多余的逗号。

1. 持久层添加用户更新方法

// 更新用户
    void update(User user);

2. 映射文件添加相关标签

<!-- 动态_set 更新用户 -->
    <update id="update" parameterType="User">
        update user
        <set>
            <if test="username != null and username.length()!=0">
                username = #username
            </if>
            <if test="sex != null and sex.length() != 0">
                and sex = #sex
            </if>
            <if test="address != null and address.length() != 0">
                and address = #address
            </if>
        </set>
        <where>
            id = #id
        </where>
    </update>

3. 测试类新建测试方法

// 测试更新用户方法
    @Test
    public void testUpdate()
        User user = new User();
        user.setId(4);
        user.setUsername("man4");
        userMapper2.update(user);

        session.commit();
    

4. 运行结果

        这里想整一个效果图,但是没想到帧率不够,最后居然绿屏了,但是不影响我们继续学习哈哈哈,理解到位即可害嗨嗨

四、动态SQL---choose和wen和otherwise标签

        这些标签表示多条件分支,类似JAVA中的 switch...case <choose> 类似switch <when> 类似 case <otherwise> 类似 default ,用法如下:

1. 持久层添加用户通用查询方法

这里就沿用那个通用查询方法即可

2. 映射文件添加相关标签

    <select id="findByCondition" resultType="User" parameterType="user">
        select * from user
        <where>
            <choose>
                <when test="username.length() &lt; 5">
                    username like #username
                </when>
                <when test="username.length() &lt; 10">
                    username = #username
                </when>
                <otherwise>
                    id = 1
                </otherwise>
            </choose>
        </where>
    </select>

        这段代码的含义就是当用户名的长度小于(不等于)5的时候,使用模糊查询,查询返回的是泛型为USer的List集合对象,list长度不定;当用户名大于等于5,小于10的时候,使用精确查询,查询指定用户名的用户,返回的是泛型为USer的List集合对象,list长度为1或者0;当用户名长度大于等于10的时候,返回的是id为1的用户

3. 测试类新建测试方法

// 测试通用查询方法——用户名
    @Test
    public void testFindByCondition1()
        User user = new User();
        System.out.println("------------设用户名字长度为2------------");
        user.setUsername("%on%");
        List<User> users = userMapper2.findByCondition(user);
        users.forEach(System.out::println);
        System.out.println("------------设用户名字长度为5------------");
        user.setUsername("%lions%");
        users = userMapper2.findByCondition(user);
        users.forEach(System.out::println);

        System.out.println("------------设用户名字长度为9------------");
        user.setUsername("T_no_name");
        users = userMapper2.findByCondition(user);
        users.forEach(System.out::println);

        System.out.println("------------设用户名字长度为10------------");
        user.setUsername("dddddddddd");
        users = userMapper2.findByCondition(user);
        users.forEach(System.out::println);

        System.out.println("------------设用户名字长度为12------------");
        user.setUsername("programmer_1");
        users = userMapper2.findByCondition(user);
        users.forEach(System.out::println);
    

4. 运行结果

        OK,其实从结果集和运行的SQL语句我们都可以得出,该映射文件的标签确实是验证了咱们刚刚的说法 

五、动态SQL---foreach标签

<foreach> 类似JAVA中的for循环,可以遍历集合或数组。 <foreach> 有如

下属性:

  1. collection:遍历的对象类型
  2. open:开始的sql语句
  3. close:结束的sql语句
  4. separator:遍历每项间的分隔符
  5. item:表示本次遍历获取的元素,遍历ListSet、数组时表示每项元素,遍历map时表示键值对的值。
  6. index:遍历List、数组时表示遍历的索引,遍历map时表示键值对的键。

1. 遍历数组

(1)持久层添加用户批量删除方法

// 用户批量删除
    void deleteBatch(int[] ids);

(2)映射文件添加相关标签

<!-- 批量删除 遍历数组-->    
    <delete id="deleteBatch" parameterType="int">
        delete from user
        <where>
            <foreach collection="array" item="id" open="id in(" close=")" separator=",">
                #id
            </foreach>
        </where>
    </delete>

其实这里对应的SQL语句就是:

delete from user in (?,?,?,...) 

(3)测试类新建测试方法

// 测试用户批量删除方法
    @Test
    public void testDeleteBatch()
        int[] ids = 4,8;
        userMapper2.deleteBatch(ids);

        session.commit();
    

(4)运行结果

运行前和运行后进行对比

 

 运行后,四和八对应的记录确实是被删除了

2. 遍历List

        <foreach> 遍历ListSet的方法是一样的,我们使用 <foreach> 遍历List进行批量添加。

(1)持久层添加用户批量增加方法

// 批量增加用户
    void insertBatch(List<User> users);

(2)映射文件添加相关标签

<!-- 批量增加用户 -->
    <insert id="insertBatch" parameterType="User">
        insert into user values
        <foreach collection="list" item="user" separator=",">
            (null,#user.username,#user.sex,#user.address)
        </foreach>
    </insert>

        这里对应的SQL语句则是:

        insert into user values(null,username,sex,address)*N

        N可以为大于或等于1的数

        其中肯定会有人问到了,为什么id要为null,其实这里id是主键,我们建库建表的时候就已经设置了这里是自增字段,因此我们无需重复操作,如果设置有和表里面的id重复,说不定还会报错 

(3)测试类新建测试方法

// 测试批量增加用户方法
    @Test
    public void testInsertBatch()
        List<User> list = new ArrayList<>();
        list.add(new User("man1","man","Beijing"));
        list.add(new User("man1","man","Beijing"));
        list.add(new User("man1","man","Beijing"));
        list.add(new User("man1","man","Beijing"));
        list.add(new User("man1","man","Beijing"));
        list.add(new User("man1","man","Beijing"));

        userMapper2.insertBatch(list);
        session.commit();
    

(4)运行结果

运行前和运行后进行对比

 

        OK,通过对比,确实是增加了相关记录,这里的id是因为我测试过很多次了,所以比较大,大家不用在意这些哈哈哈,侧面反映我是很认真在写的,期待小伙伴的支持

3. 遍历Map

        下面我们使用 <foreach> 遍历Map进行多条件查询。

(1)持久层添加多条件查询方法

/**
     * 多条件查询
     * @param map 查询的键值对 键:属性名 值:属性值
     * @return
     */
    List<User> findUser(@Param("queryMap") Map<String,Object> map);

(2)映射文件添加相关标签

<!-- 多条件查询 遍历map-->
    <select id="findUser" parameterType="map" resultType="User">
        select * from user
        <where>
            <foreach collection="queryMap" separator="and" index="key" item="value">
                $key = #value
            </foreach>
        </where>
    </select>

这里对应的代码段其实就是:

select * from user where (? = ?) and (? = ?)

就像我们在淘宝,拼多多和京东上买东西使用筛选一样 

(3)测试类新建测试方法

// 测试多条件查询方法
    @Test
    public void testFindUser()
        Map<String,Object> map = new HashMap<>();
        // 这里筛选性别为man和地址为Beijing的结果集
        map.put("sex","man");
        map.put("address","Beijing");
        List<User> users = userMapper2.findUser(map);
        users.forEach(System.out::println);
        // 再测试一下
        map.clear();
        // 这里筛选地址为Beijing和用户名为man1的结果集
        map.put("address","Beijing");
        map.put("username","man1");
        users = userMapper2.findUser(map);
        users.forEach(System.out::println);
    

(4)运行结果

先看用户表数据

执行方法,观察结果是否一致 (是的,确实一致),不一致怎么会放出来呢

 

        OK,这里确实保持一致,动态查询就学到这里了,后续会继续更新相关内容滴,敬请关注! !!

后言----查错

        如果执行过程中有错误的话,不妨看看有没有导错包和导错类,下面就是上面用到相关的类用到的导包

持久层接口用到的包 

package com.mybatisstudy.mapper;

import com.mybatisstudy.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

测试类用到的包

import com.mybatisstudy.mapper.UserMapper2;
import com.mybatisstudy.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.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

MyBatis动态SQL

 MyBatis在SQL映射文件中可以使用灵活,智能的动态SQL来实现SQL映射。

  if+set:完成更新操作

  if+where:完成多条查询

  if+trim:完成多条件查询(替代where)或者更新操作(替代set)

  choose (when,otherWise):完成条件查询(多条件下,选择其一)。

  foreach:完成复杂查询,主要用于in条件查询中,迭代集合。其中最关键的部分就是collection属性,根据不同的入参类型,该属性值为list。

    若入参对象为一个List实例,collection属性值为array。

    若入参对象为一个整数,collection属性值为array。

    若入参对象为多个,就需要把它们封装为一个Map进行处理。

 

以上是关于Mybatis动态SQL查询 --(附实战案例--8888个字--88质量分)的主要内容,如果未能解决你的问题,请参考以下文章

mybatis 查询 动态sql语句怎么写

MyBatis动态SQL

Mybatis动态查询语句

Oracle -Mybatis动态SQL查询分页的实现

mybatis_动态SQL

mybatis系列-08-动态sql