详解Mybatis之动态sql问题

编译软件:IntelliJ IDEA 2019.2.4 x64
操作系统:win10 x64 位 家庭版
Maven版本:apache-maven-3.6.3
Mybatis版本:3.5.6


在这里插入图片描述


一. 在sql映射文件中如何写注释?

关于XXXMapper接口对应的映射文件里SQL中的注释

👉语法

①方式一

//mysql的注释	
-- 1=1

②方式二

//xml的注释
<!-- 1=1  -->

使用这两种注释方式各有什么不同呢?

👉请看如下测试

代码示例如下:

①使用第一种注释

<!--  根据查询条件查找对应的员工信息(条件不确定) 即采用动态的sql去查询  -->
    <select id="selectEmpByopr" resultType="employee">
        SELECT
            `id`,
            `last_name`,
            `email`,
            `salary`,
            `dept_id`
        FROM
            `tbl_employee`
        WHERE
            -- 1=1
            <if test="id !=null">
               and id=#{id}
            </if>
            <if test="lastName != null">
               and last_name=#{lastName}
            </if>
            <if test="email != null">
                and email=#{email}
            </if>
            <if test="salary != null">
                and last_name=#{salary}
            </if>
    </select>

在这里插入图片描述

②使用第二种注释

<!--  根据查询条件查找对应的员工信息(条件不确定) 即采用动态的sql去查询  -->
    <select id="selectEmpByopr" resultType="employee">
        SELECT
            `id`,
            `last_name`,
            `email`,
            `salary`,
            `dept_id`
        FROM
            `tbl_employee`
        WHERE
<!--             1=1-->
            <if test="id !=null">
               and id=#{id}
            </if>
            <if test="lastName != null">
               and last_name=#{lastName}
            </if>
            <if test="email != null">
                and email=#{email}
            </if>
            <if test="salary != null">
                and last_name=#{salary}
            </if>
    </select>

在这里插入图片描述

💡结论

通过以上二者运行测试结果对比,所以在需要使用注释时,推荐使用第二种注释方式


二. 什么是动态sql?

👉定义

  1. 动态sql指的是sql语句可动态化
  2. Mybatis的动态sql中支持OGNL表达式语言,OGNL(Object Graph Navigation
    Language)是对象图导航语言

❗注意

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1XJyWZlo-1690458988415)(C:\Users\king\AppData\Roaming\Typora\typora-user-images\1682994155884.png)]

👉用法案例

不指定查询条件,查询对应员工信息,即当你传入id,程序就根据id去查,传入什么条件,就去根据此条件去查(多个条件不确定)

代码示例如下

①在EmployeeMapper接口下书写相应的方法

//动态的sql方式,即不指定查询条件,查询对应员工信息
public List<Employee> selectEmpByopr(Employee employee);

②在EmployeeMapper接口相应的sql

<!--  根据查询条件查找对应的员工信息(条件不确定) 即采用动态的sql去查询  -->
    <select id="selectEmpByopr" resultType="employee">
        SELECT
            `id`,
            `last_name`,
            `email`,
            `salary`,
            `dept_id`
        FROM
            `tbl_employee`
        WHERE
            <if test="id !=null">
               and id=#{id}
            </if>
            <if test="lastName != null">
               and last_name=#{lastName}
            </if>
            <if test="email != null">
                and email=#{email}
            </if>
            <if test="salary != null">
                and last_name=#{salary}
            </if>
    </select>

③测试

@Test
public void test01(){
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过SqlSessionFactory对象调用openSession();
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取EmployeeMapper的代理对象
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);

        //动态参数(无参会报错,没有第一个参数也会报错)
        Employee employee=new Employee();

        List<Employee> employees = employeeMapper.selectEmpByopr(employee);

        //遍历集合employees
        for (Employee employee1 : employees) {
            System.out.println(employee1);
        }

    } catch (IOException e) {
        e.printStackTrace();
    }

}

❓但是这样会出现一个问题,即不传参会报错,没有第一个参数也会报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H8BuZQxZ-1690458988415)(C:\Users\king\AppData\Roaming\Typora\typora-user-images\1682997746252.png)]

💡原因分析

不传参时,mybatis解析sql的过程中走到where的部分,无参,会进入where里,但不会执行里面的任意if判断,where后没有任何赋值表达式,此sql为非法sql,故报错。没有第一个参数时也会报类似的问题。

👉解决方案

①在第二步中的where里加上 1=1,作为条件恒等式(老版本的解决措施)

在这里插入图片描述

②使用where标签

代码示例如下:

<!--  根据查询条件查找对应的员工信息(条件不确定) 即采用动态的sql去查询  -->
    <select id="selectEmpByopr" resultType="employee">
        SELECT
            `id`,
            `last_name`,
            `email`,
            `salary`,
            `dept_id`
        FROM
            `tbl_employee`
         <where>
            <if test="id !=null">
               and id=#{id}
            </if>
            <if test="lastName != null">
               and last_name=#{lastName}
            </if>
            <if test="email != null">
                and email=#{email}
            </if>
            <if test="salary != null">
                and last_name=#{salary}
            </if>
         </where>
    </select>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GPGt0JYh-1690458988416)(C:\Users\king\AppData\Roaming\Typora\typora-user-images\1682999094227.png)]


三. 动态sql常用标签有哪些?

3.1 if标签

👉功能

用于完成简单的判断

示例代码如下:

 //如果属性id不为空,就将传入的参数id赋值给sql中的字段id
 <if test="id !=null">
      id=#{id}
 </if>

3.2 where标签

👉功能:

用于解决where关键字及where后第一个and或or的问题

示例代码如下:

<where>
   <if test="id !=null">
      and id=#{id}
   </if>
   <if test="lastName != null">
      and last_name=#{lastName}
   </if>
   <if test="email != null">
       and email=#{email}
   </if>
   <if test="salary != null">
       and last_name=#{salary}
   </if>
</where>

3.3 trim标签

👉功能

可以在条件判断完的SQL语句前后添加或者去掉指定的字符

👉属性

  • prefix添加前缀
  • prefixOverrides去掉前缀
  • suffix添加后缀
  • suffixOverrides去掉后缀

👉用法案例

不指定查询条件,查询对应员工信息(trim标签优化版)

代码示例如下:

①在EmployeeMapper接口定义相应的方法

//不指定查询条件,查询对应员工信息
public List<Employee> selectEmpByEmpTrim(Employee employee);

②在EmployeeMapper接口对应的映射文件中定义相应的sql

<!--  根据查询条件查找对应的员工信息(条件不确定) 动态的sql(trim标签优化版)  -->
<select id="selectEmpByEmpTrim" resultType="employee">
    SELECT
        `id`,
        `last_name`,
        `email`,
        `salary`,
        `dept_id`
    FROM
        `tbl_employee`
    <!--  给下面的sql语句加上前缀 where,去掉后缀and      -->
    <trim prefix="where" suffixOverrides="and">
        <if test="id !=null">
            id=#{id} and
        </if>
        <if test="lastName != null">
           last_name=#{lastName}  and
        </if>
        <if test="email != null">
            email=#{email}  and
        </if>
        <if test="salary != null">
            salary=#{salary}   and
        </if>
    </trim>
</select>

③测试

@Test
    public void test02(){
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

            //通过SqlSessionFactory对象调用openSession();
            SqlSession sqlSession = sqlSessionFactory.openSession();

            //获取EmployeeMapper的代理对象
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);

            //动态参数
            Employee employee=new Employee();
          /*  employee.setLastName("jack");
            employee.setSalary(5600.0);
*/
            List<Employee> employees = employeeMapper.selectEmpByEmpTrim(employee);

            //遍历集合employees
            for (Employee employee1 : employees) {
                System.out.println(employee1);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述

在这里插入图片描述

trim标签运行流程详解

在这里插入图片描述

3.4 set标签

👉功能

主要用于解决set关键字及多出一个【,】问题

👉用法案例

修改员工的信息

代码示例如下:

①在EmployeeMapper接口中定义修改员工的信息的方法

//修改员工的信息
public void updateEmp(Employee employee);

②在EmployeeMapper接口对应的映射文件中书写相应的sql

问题版(会出现多一个【,】问题

<update id="updateEmp">
    update
        tbl_employee
    set
        <if test="lastName != null">
            last_name=#{lastName}  ,
        </if>
        <if test="email != null">
            email=#{email}  ,
        </if>
        <if test="salary != null">
            salary=#{salary} ,
        </if>
    where
        id=#{id}
</update> 

set标签解决问题版

<update id="updateEmp">
    update
        tbl_employee
    <set>
        <if test="lastName != null">
            last_name=#{lastName}  ,
        </if>
        <if test="email != null">
            email=#{email}  ,
        </if>
        <if test="salary != null">
            salary=#{salary} ,
        </if>
    </set>
    where
        id=#{id}
</update>

③测试

@Test
public void test03(){
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过SqlSessionFactory对象调用openSession();
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取EmployeeMapper的代理对象
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);

        //动态参数
        Employee employee=new Employee();
        employee.setId(1);
        employee.setLastName("tom");
        employee.setSalary(16800.0);
        employeeMapper.updateEmp(employee);

xml

    } catch (IOException e) {
        e.printStackTrace();
    }
}

使用问题版的sql进行测试

在这里插入图片描述

使用set标签解决问题版的sql进行测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYwOzFHZ-1690458988420)(C:\Users\king\AppData\Roaming\Typora\typora-user-images\1683014982189.png)]

3.5 choose标签

👉功能

类似java中if-else【switch-case】结构

👉应用场景

应用于单个条件不确定的业务场景

👉用法案例

不指定查询条件,查询对应的员工信息(单个条件不确定的)

代码示例如下:

①在EmployeeMapper接口书写相应的方法

//不指定查询条件,查询对应员工信息(单个条件不确定的)
public List<Employee> selectEmpByOneOpr(int empId);

②在EmployeeMapper接口对应的映射文件中书写相应的sql

<!--  根据查询条件查找对应的员工信息(条件不确定) 动态的sql(trim标签优化版)  -->
<select id="selectEmpByOneOpr" resultType="employee">
    SELECT
        `id`,
        `last_name`,
        `email`,
        `salary`,
        `dept_id`
    FROM
        `tbl_employee`

    <where>
        <choose>
            <when test="id !=null">
                id=#{id}
            </when>
            <when test="lastName != null">
                last_name=#{lastName}
            </when>
            <when test="email != null">
                email=#{email}
            </when>
            <otherwise>
                salary=#{salary}
            </otherwise>
        </choose>
    </where>
    
</select>

③测试

@Test
public void test04(){
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过SqlSessionFactory对象调用openSession();
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取EmployeeMapper的代理对象
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);

        List<Employee> employees = employeeMapper.selectEmpByOneOpr(1);
        System.out.println(employees);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e7bR0Hli-1690458988421)(C:\Users\king\AppData\Roaming\Typora\typora-user-images\1683016763843.png)]

3.6 foreach标签

👉功能

类似java中for循环

👉标签属性

  • collection要迭代的集合
  • item当前从集合中迭代出的元素
  • separator元素与元素之间的分隔符
  • open开始字符
  • close结束字符

👉应用场景

🚩 ①遍历迭代

用法案例

通过多个id获取员工的信息 【EmpIds:员工id的集合】

代码示例如下:

a.在EmployeeMapper接口定义相应的方法

/**
 * 通过多个id获取员工的信息 【EmpIds:员工id的集合】
 * @param EmpIds
 * @return
 */
public List<Employee> selectEmpByIds(@Param("ids") List<Integer> EmpIds);

b.在EmployeeMapper接口对应的映射文件中定义相应的sql

 <select id="selectEmpByIds" resultType="employee">
        SELECT
            `id`,
            `last_name`,
            `email`,
            `salary`,
            `dept_id`
        FROM
            `tbl_employee`
        <where>
            `id` in
            (
            <foreach collection="ids" item="id" separator=",">
                #{id}
            </foreach>
            )
        </where>
    </select>

c.测试

@Test
public void test04(){
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过SqlSessionFactory对象调用openSession();
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取EmployeeMapper的代理对象
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);

        List<Employee> employees = employeeMapper.selectEmpByOneOpr(1);
        System.out.println(employees);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

🚩②批量导入

用法案例

批量添加员工数据

代码示例如下:

a.在EmployeeMapper接口定义相应的方法

  //批量添加员工数据
    public void batchInsertEmp(@Param("emps") List<Employee> employees);

b.在EmployeeMapper接口对应的映射文件中定义相应的sql

// 批量添加员工数据,使用insert标签书写相应的sql
<insert id="batchInsertEmp">
    insert into
        tbl_employee(last_name,email,salary)
    values
        <foreach collection="emps" item="emp" separator=",">
            (#{emp.lastName},#{emp.email},#{emp.salary})
        </foreach>

</insert>

c.测试

@Test
public void test06(){
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过SqlSessionFactory对象调用openSession();
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取EmployeeMapper的代理对象
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);

        //定义要添加的员工集合
       List<Employee> list=new ArrayList<>();
       list.add(new Employee("zhangsan","sdhjsd@qq.com",6700.0));
       list.add(new Employee("wangwu","dddhjsd@123.com",9700.0));

        employeeMapper.batchInsertEmp(list);
        sqlSession.commit();


    } catch (IOException e) {
        e.printStackTrace();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TE2Af2O2-1690458988422)(C:\Users\king\AppData\Roaming\Typora\typora-user-images\1683030856816.png)]

在这里插入图片描述

3.7 sql标签

👉功能

提取可重用SQL片段

❗注意

该SQL片段可以是一个完整的sql语句,也可以是一个sql语句中的某个片段)

👉用法案例

使用sql标签对3.6小节中的应用场景1的案例里映射文件里的的”select xxx,xxxx,xxx from
xxxx”部分提取出来,作为一个可重用的sql片段,在select>标签内引入该sql片段

代码示例如下:

①使用sql标签抽取映射文件中”select xxx,xxxx,xxx from xxxx”部分片段作为可重用的sql片段

<!-- 抽取映射文件中”select xxx,xxxx,xxx from xxxx”部分片段作为可重用的sql片段   -->
<sql id="select_employee">
        SELECT
            `id`,
            `last_name`,
            `email`,
            `salary`,
            `dept_id`
        FROM
            `tbl_employee`
    </sql>

    <select id="selectEmpByIds" resultType="employee">
        <!--  将刚才抽取的sql片段select_employee引入进来  -->
        <include refid="select_employee"></include>
        <where>
            `id` in
            (
            <foreach collection="ids" item="id" separator=",">
                #{id}
            </foreach>
            )
        </where>
    </select>

②测试运行

在这里插入图片描述


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌上少年,且听这风吟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值