mybatis强大的动态SQL

简介: mybatis强大的动态SQL

mybatis的动态SQL

一、动态sql语句概述

  Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。

问题来了: 什么是动态SQL? 动态SQL有什么作用?

传统的使用JDBC的方法,相信大家在组合复杂的的SQL语句的时候,需要去拼接,
稍不注意哪怕少了个空格,都会导致错误。
Mybatis的动态SQL功能正是为了解决这种问题, 
其通过 if, choose, when, otherwise, trim, where, set, foreach标签,
可组合成非常灵活的SQL语句,从而提高开发人员的效率。

下面就去跟着我来感受Mybatis动态SQL的魅力吧。


二、 include 实现SQL片段抽取

1、SQL片段的作用

Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,
最终达到 sql 重用的目的
减少Sql语句的冗余,简化开发

2、单表查询时实现SQL片段抽取

<!--抽取sql片段简化编写-->
<sql id="selectStudent"> select * from student</sql>
<select id="findById" parameterType="int" resultType="student">
    <include refid="selectStudent"></include> 
    where id=#{id}
</select>
<select id="findByIds" parameterType="list" resultType="student">
    <!-- 使用sql片段 refid:引用-->
    <include refid="selectStudent"></include>
    <where>
        <foreach collection="array" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>

3、级联查询时实现SQL片段抽取

<!--
   sql片段
   减少Sql语句的冗余,简化开发
    -->
    <sql id="selectEmp_Dep">
        select e.id      as emp_id,
               e.name    as emp_name,
               e.salary  as emp_salary,
               d.id      as dep_id,
               d.name    as dep_name,
               d.address as dep_address
    </sql>
    <!--引用SQL片段-->
    <select id="selectEmpById" resultMap="empResultMap">
        <include refid="selectEmp_Dep"/>
        from emp e
        inner join dep d
        on e.id = d.id
        where e.id = #{id};
    </select>

4、总结语法:

我们可以将一些重复性的 SQL 语句进行抽取,以达到复用的效果。


<sql>:抽取 SQL 语句标签。 
<include>:引入 SQL 片段标签。 
<sql id=“片段唯一标识”>抽取的 SQL 语句</sql> <include refid=“片段唯一标识”/>

三、多条件组合查询之<where-if>

我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。


1、原始的SQL语句示意


image.png

2、使用动态SQL语句if之后

当id=1时,name,password,birthday为空时
#sql语句只执行:
select id,name,password,birthday form 
user where id=1;
示意图:

image.png



动态条件查询,就是通过条件判断动态筛选查询条件:

<select>
    select id,name,password,birthday from t_user
    <where>
    注意:int类型的主键不能用0作为判断条件,
    只能用null作为判断值(int类型可以是0或者是负数)
    为了数据库表能与实体类映射,所有的实体类都只能是包装类
      <if test="id!=null">
      id = #{id}
      </if>
      <if test="name!=null">
     and name = #{name}
      </if>
      <if test="password!=null">
     and password= #{password}
      </if>
      <if test="birthday!=null">
     and  birthday= #{birthday}
      </if>
    </where>
</select>
注意:Where标签可以忽略其后的第一个and


image.png

UserDao接口

 1、if标签中的test里 判断的条件是当前参数名,
    如果参数是对象的话,书写的是对象的属性名
 2、  注意:int类型的主键不能用0作为判断条件,
    只能用null作为判断值(int类型可以是0或者是负数)
    为了数据库表能与实体类映射,所有的实体类都只能是包装类
  /**
     * 通过实体类查询所有用户
     * @param user
     * @return   List<User>
     */
    public List<User> selectUserAll(User user);
 UserDao两种写法示意图:

image.png

UserDaoImpl.xml

  <mapper namespace="com.tjcu.dao.UserDao">
<!--SQL片段抽取-->
    <sql id="selectUser">
        select id, username, password, age, birthday
    </sql>
    <!--
         实体类属性:
        id,username, password,age,birthday
    -->
    <select id="selectUserAll" resultType="user">
     <include refid="selectUser"/>
       from t_user
        <where>
            <if test="id!=null">
                id=#{id}
            </if>
            <if test="username!=null">
                and username=#{username}
            </if>
            <if test="password!=null">
                and password=#{password}
            </if>
            <if test="age!=null">
                and age=#{age}
            </if>
            <if test="birthday!=null">
                and birthday=#{birthday}
            </if>
        </where>
    </select>
</mapper>

3、不同条件下的执行结果

1.当查询条件id和username都存在时,控制台打印的sql语句如下:


    @Test
    public void selectTest(){
        UserDao mapper = (UserDao) MybatisUtil.getMapper(UserDao.class);
        User user = new User(null,"王恒杰","123456",null,null);
        List<User> users = mapper.selectUserAll(user);
        for (User u : users) {
            System.out.println("查看登录的用户"+u);
        }
        MybatisUtil.close();
    }


image.png

2、当查询条件是查询所有时,控制台打印的sql语句如下:


 

       @Test
    public void selectTest(){
        UserDao mapper = (UserDao) MybatisUtil.getMapper(UserDao.class);
        User user = new User();
        List<User> users = mapper.selectUserAll(user);
        for (User u : users) {
            System.out.println("查询所有"+u);
        }
        MybatisUtil.close();
    }

image.png

动态Sql之where-if示意图


image.png

4、总结语法:

<where>:条件标签。如果有动态条件,则使用该标签代替 where 关键字。
<if>:条件判断标签。
<if test=“条件判断”>
  查询条件拼接
</if>

5、注意 事项:

 1、if标签中的test里 判断的条件是当前参数名,
    如果参数是对象的话,书写的是对象的属性名
 2、注意:int类型的主键不能用0作为判断条件,
    只能用null作为判断值(int类型可以是0或者是负数)
    如果用到SQL语句,为了数据库表能与实体类映射,所有的实体类都只能是包装类

四、动态修改SQL【Update字句】

1、调用修改方法:

sql:
  update t_yser set name=#{name} where id=#{id}
  update t_yser set name=#{name},password=#{password} where id=#{id}
  update t_yser set name=#{name},password=#{password},birthday=#{birthday} where id=#{id}
如果传递的参数是空值,sql语句会将表中对应的行数数据所有列置空
  update(new User(1,null,null,null));

2、动态修改:通过判断动态确认要修改的列

**# 修改1号用户的密码** 
update(new User(2,null,'123456',null));
**#UserDaoMapper.xml动态修改实现** 
 <update id="updateUser">
        update t_user set
            <if test="username!=null">
                 username=#{username},
            </if>
            <if test="password!=null">
                 password=#{password},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
            <if test="birthday!=null">
               birthday=#{birthday}
            </if>
        where id=#{id}
    </update>

问题又来了: “如果我只有name不为null, 那么这SQL不就成了 update set name = #{name}, where … ? 你那name后面那逗号会导致出错啊!”


是的,这时候,就可以用mybatis为我们提供的set 标签了。下面是通过set标签改造后:


**# 修改2号用户的密码** 
update(new User(2,null,'123456',null));
**#UserDaoMapper.xml动态修改实现** 
 <update id="updateUser">
        update t_user
        <set>
            <if test="username!=null">
                 username=#{username},
            </if>
            <if test="password!=null">
                 password=#{password},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
            <if test="birthday!=null">
               birthday=#{birthday}
            </if>
        </set>
        where id=#{id}
    </update>

测试代码:


 

  @Test
    public void updateTest(){
        UserDao mapper = (UserDao) MybatisUtil.getMapper(UserDao.class);
        User user = new User(2, null,"123456",null,null);
       mapper.updateUser(user);
    }

3、注意事项:

1. set标签自动忽略最后一个","
2.修改时不要全部都为空,最少修改一列
3.动态修改可以在前端用数据回显代替

五、批量操作——批量删除

1、原始批量删除示意图:


image.png

2、在service层批量删除

将原来在Action(Controller)层的批量删除,转移到Service层


image.png

在Service层进行批量删除的缺点:


*效率低,每次删除都需要获取链接操作数据库

3、在Dao层使用mybatis批量删除

1、解决service层批量删除效率低方案:

解决办法:最好一次性全部删除
SQL语句: 一次性删除多个用户
delete from t_user where id in(1,2,3,4);

2、mybatis中SQL动态语句实现批量删除:

delete from t_user where id in(`1,2,3,4`);
<!--
   collection:指定当前参数是数组还是集合 数组:array 集合:list
   open:开始符号
   close:结束符号
   item:遍历时当前元素的名字
   separator:元素之间的分隔符  【英语单词:separator:离析器】
-->
delete from t_user where id in
 <foreach collection="list" open="id in(" close=")" item="id" separator="," >
     #{id}
 </foreach>

4、批量删除案例:

(1)批量删除示意图:


image.png


(2)实体类+sql数据库


image.png




(3)dao接口


 /**
     * 批量删除
     * @param ids
     */
    public void deleteUser(List<Integer> ids);

(4)UserDaoMapper.xml[核心代码]

   <!--
    foreach:遍历集合来组装sql
        collection: map集合的key 如果是数组:array 集合:list
        open: 以某种字符开始
        close:以某种字符结束
        item: 集合中的元素
        separator:以某种字符分割
        index: 当前所遍历到的索引号
    -->
    <delete id="deleteUser">
        delete from t_user where id
        <foreach collection="list" open="in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </delete>

(5)测试类

  @Test
    public void deleteTest(){
        UserDao mapper = (UserDao) MybatisUtil.getMapper(UserDao.class);
        List<Integer> ids=new ArrayList<Integer>();
        ids.add(7);
        ids.add(8);
        ids.add(9);
        mapper.deleteUser(ids);
       MybatisUtil.close();
    }

5、注意事项:

1.如果参数进行了参数绑定,可以使用绑定之后的名字

6、dao层进行批量删除的好处:

减少与数据库的连接次数,提高程序运行效率

六、批量查询 之<foreach>

循环执行sql的拼接操作,例如:SELECT * FROM student WHERE id IN (1,3,4)。


UserDao


    /**
     * foreach查询多个id
     * @param id
     * @return  List<User>
     */
    public List<User> selectByIds(List<Integer> id);

UserDaoImpl.xml配置

 <!--
    foreach查询多个id
      collection:参数容器类型, (list-集合, array-数组)。
      open:开始的 SQL 语句。
      close:结束的 SQL 语句。
      item:参数变量名。
      separator:分隔符。
    -->
    <select id="selectByIds" resultType="user">
        select * from t_user
        <where>
            <foreach collection="list" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

测试类

 @Test
    public void selectByIts() throws IOException {
        SqlSession sqlSession = MybatisUtil.openSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(3);
        list.add(4);
        List<User> users = mapper.selectByIds(list);
        for (User user : users) {
            System.out.println(user);
        }
        MybatisUtil.close(sqlSession);
    }

总结语法:


<foreach>:循环遍历标签。适用于多个参数或者的关系。
    <foreach collection=“”open=“”close=“”item=“”separator=“”>
    获取参数
  </foreach>

属性

collection:参数容器类型, (list-集合, array-数组)。

open:开始的 SQL 语句。

close:结束的 SQL 语句。

item:参数变量名。

separator:分隔符。


七、知识小结

MyBatis映射文件配置:


<select>:查询
<insert>:插入
<update>:修改
<delete>:删除
<where>:where条件
<if>:if判断
<foreach>:循环
<sql>:sql片段抽取
SQLSyntaxErrorException:SQL语法错误异常
check the manual that corresponds 
to your MySQL server version for the right syntax to use near 'in:
查看与您的MySQL服务器版本相对应的手册,了解在“in”附近使用的正确语法
The error may involve defaultParameterMap:该错误可能涉及默认参数映射
相关文章
|
3天前
|
JSON Java 数据库连接
MyBatis SQL 返回 resultType="map"
本文深入分析MyBatis中使用Map转VO的常见问题,指出JSON序列化转换方案虽可行但性能差、类型不安全、维护困难。推荐直接使用`resultType=&quot;VO&quot;`或`@Results`映射,提升性能10倍以上,增强可读性与可维护性,杜绝类型丢失风险,是更优的实践方案。
|
29天前
|
SQL XML Java
通过MyBatis的XML配置实现灵活的动态SQL查询
总结而言,通过MyBatis的XML配置实现灵活的动态SQL查询,可以让开发者以声明式的方式构建SQL语句,既保证了SQL操作的灵活性,又简化了代码的复杂度。这种方式可以显著提高数据库操作的效率和代码的可维护性。
133 18
|
6月前
|
SQL Java 数据库连接
【YashanDB知识库】解决mybatis的mapper文件sql语句结尾加分号";"报错
【YashanDB知识库】解决mybatis的mapper文件sql语句结尾加分号";"报错
|
5月前
|
SQL Java 数据库连接
MyBatis动态SQL字符串空值判断,这个细节99%的程序员都踩过坑!
本文深入探讨了MyBatis动态SQL中字符串参数判空的常见问题。通过具体案例分析,对比了`name != null and name != &#39;&#39;`与`name != null and name != &#39; &#39;`两种写法的差异,指出后者可能引发逻辑混乱。为避免此类问题,建议在后端对参数进行预处理(如trim去空格),简化MyBatis判断逻辑,提升代码健壮性与可维护性。细节决定成败,严谨处理参数判空是写出高质量代码的关键。
684 0
|
1月前
|
SQL Java 数据库连接
SSM相关问题-1--#{}和${}有什么区别吗?--Mybatis都有哪些动态sql?能简述一下动 态sql的执行原理吗?--Spring支持的几种bean的作用域 Scope
在MyBatis中,`#{}`是预处理占位符,可防止SQL注入,适用于大多数参数传递场景;而`${}`是直接字符串替换,不安全,仅用于动态表名、列名等特殊场景。二者在安全性、性能及使用场景上有显著区别。
57 0
|
4月前
|
SQL XML Java
菜鸟之路Day35一一Mybatis之XML映射与动态SQL
本文介绍了MyBatis框架中XML映射与动态SQL的使用方法,作者通过实例详细解析了XML映射文件的配置规范,包括namespace、id和resultType的设置。文章还对比了注解与XML映射的优缺点,强调复杂SQL更适合XML方式。在动态SQL部分,重点讲解了`&lt;if&gt;`、`&lt;where&gt;`、`&lt;set&gt;`、`&lt;foreach&gt;`等标签的应用场景,如条件查询、动态更新和批量删除,并通过代码示例展示了其灵活性与实用性。最后,通过`&lt;sql&gt;`和`&lt;include&gt;`实现代码复用,优化维护效率。
332 5
|
6月前
|
SQL Java 数据库连接
【YashanDB 知识库】解决 mybatis 的 mapper 文件 sql 语句结尾加分号";"报错
【YashanDB 知识库】解决 mybatis 的 mapper 文件 sql 语句结尾加分号";"报错
|
6月前
|
SQL 缓存 Java
框架源码私享笔记(02)Mybatis核心框架原理 | 一条SQL透析核心组件功能特性
本文详细解构了MyBatis的工作机制,包括解析配置、创建连接、执行SQL、结果封装和关闭连接等步骤。文章还介绍了MyBatis的五大核心功能特性:支持动态SQL、缓存机制(一级和二级缓存)、插件扩展、延迟加载和SQL注解,帮助读者深入了解其高效灵活的设计理念。
|
7月前
|
SQL XML Java
九、MyBatis动态SQL
九、MyBatis动态SQL
92 2
|
6月前
|
SQL XML Java
六、MyBatis特殊的SQL:模糊查询、动态设置表名、校验名称唯一性
六、MyBatis特殊的SQL:模糊查询、动态设置表名、校验名称唯一性
169 0