6.mybatis动态sql

本文深入探讨MyBatis框架中的动态SQL特性,包括if、choose、trim、foreach等元素的使用,通过实例展示了如何根据不同条件生成变化的SQL语句,实现更灵活的数据操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态Sql

1.什么是动态sql:

  1. 就是根据不同的条件生成不同的SQL语句
  2. 利用动态SQL这一特性可以彻底摆脱这种痛苦
  3. 如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
  4. 类型
    • if
    • choose (when, otherwise)
    • trim (where, set)
    • foreach

2.搭建环境

  1. 建一个表blog

    CREATE TABLE `blog` (
      `id` varchar(50) NOT NULL COMMENT '博客id',
      `title` varchar(100) NOT NULL COMMENT '博客标题',
      `author` varchar(30) NOT NULL COMMENT '博客作者',
      `create_time` datetime NOT NULL COMMENT '创建时间',
      `views` int(30) NOT NULL COMMENT '浏览量'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    
  2. 创建实体类

    import lombok.Data;
    
    import java.util.Date;
    
    @Data
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;//属性名和字段名不一致
        private int views;
    }
    
  3. 增加工具类

    import java.util.UUID;
    
    public class IDUtil {
        public static String genId(){
            //自动生成id
            return UUID.randomUUID().toString().replaceAll("-","");
        }
    }
    
  4. 编写接口

    public interface BlogMapper {
    }
    
  5. 编写xml文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.mapper.BlogMapper">
    
    </mapper>
    
  6. 注册

    <mappers>
        <mapper resource="com/mapper/BlogMapper.xml"/>
    </mappers>
    
  7. 解决字段名与属性名不一致问题

        <settings>
            <!--是否开启驼峰命名自动映射,解决属性名与字段名不一致的问题        -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        </settings>
    

3.增加初始化数据

  1. 创建接口

    int addBlog(Blog blog);
    
  2. 编写xml文件

    <insert id="addBlog" parameterType="blog">
            insert into blog (`id`,`title`,`author`,`createTime`,`views`) values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>
    
  3. 初始化

    import com.pojo.Blog;
    import com.utils.IDUtil;
    import com.utils.MybatisUtil;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.Date;
    
    public class MyTest {
    
        @Test
        public void addBlog(){
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            Blog blog = new Blog();
            blog.setId(IDUtil.genId());
            blog.setTitle("Mybatis入门");
            blog.setAuthor("张起灵");
            blog.setCreateTime(new Date());
            blog.setViews(9999);
            mapper.addBlog(blog);
    
            blog.setId(IDUtil.genId());
            blog.setTitle("第一个Mybatis程序");
            blog.setAuthor("二月红");
            blog.setCreateTime(new Date());
            blog.setViews(8888);
            mapper.addBlog(blog);
    
            blog.setId(IDUtil.genId());
            blog.setTitle("Mybatis配置");
            blog.setAuthor("半截李");
            blog.setCreateTime(new Date());
            blog.setViews(7777);
            mapper.addBlog(blog);
    
            blog.setId(IDUtil.genId());
            blog.setTitle("Mybatis分页");
            blog.setAuthor("陈皮阿四");
            blog.setCreateTime(new Date());
            blog.setViews(6666);
            mapper.addBlog(blog);
    
            blog.setId(IDUtil.genId());
            blog.setTitle("Mybatis注解");
            blog.setAuthor("吴老狗");
            blog.setCreateTime(new Date());
            blog.setViews(5555);
            mapper.addBlog(blog);
    
            sqlSession.close();
        }
    }
    

4.if语句

  1. 创建接口

    List<Blog> getByBolg(Map map);
    
  2. 编写xml文件

       <select id="getByBlog" parameterType="map" resultType="blog">
            select * from blog where 1=1
            <if test="title != null">
                and title = #{title}
            </if>
            <if test="author != null">
                and author = #{author}
            </if>
        </select>
    
  3. 测试

        @Test
        public void getByBlog(){
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            HashMap map = new HashMap();
            map.put("title","Mybatis入门");
            map.put("author","张起灵");
            List<Blog> byBlog = mapper.getByBlog(map);
            for (Blog blog : byBlog) {
                System.out.println(blog);
            }
            sqlSession.close();
        }
    
  4. 结果

    Opening JDBC Connection
    Created connection 1475491159.
    ==>  Preparing: select * from blog where 1=1 and title = ? and author = ? 
    ==> Parameters: Mybatis入门(String), 张起灵(String)
    <==    Columns: id, title, author, create_time, views
    <==        Row: 8fbd8a758ab54a79a0527b4db1b7dc91, Mybatis入门, 张起灵, 2020-08-26 22:50:29.0, 9999
    <==      Total: 1
    Blog(id=8fbd8a758ab54a79a0527b4db1b7dc91, title=Mybatis入门, author=张起灵, createTime=Wed Aug 26 22:50:29 CST 2020, views=9999)
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@57f23557]
    Returned connection 1475491159 to pool.
    
  5. 小结

    • 动态sql的if语句提供了可选的查找文本功能。
    • 如果不传入 “title”,那么所有的 BLOG 都会返回;
    • 如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)
    • 如果希望通过 “title” 和 “author” 两个参数进行可选搜索,只需要加入另一个条件即可

5.trim (where, set)语句

前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “1=1’” 设置成动态条件,当我们什么都不输入时查询会失败,所有就需要where语句

5.1、 where语句

  1. xml文件wher语句

    <select id="getByBlog" parameterType="map" resultType="blog">
            select * from blog
            <where>
                <if test="title != null">
                    and title = #{title}
                </if>
                <if test="author != null">
                    and author = #{author}
                </if>
            </where>
        </select>
    
  2. 测试

        @Test
        public void getByBlog(){
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            HashMap map = new HashMap();
            List<Blog> byBlog = mapper.getByBlog(map);
            for (Blog blog : byBlog) {
                System.out.println(blog);
            }
            sqlSession.close();
        }
    
  3. 结果

     Preparing: select * from blog 
    ==> Parameters: 
    <==    Columns: id, title, author, create_time, views
    <==        Row: 8fbd8a758ab54a79a0527b4db1b7dc91, Mybatis入门, 张起灵, 2020-08-26 22:50:29.0, 9999
    <==        Row: 52f65ba723884c428295c271f018bd06, 第一个Mybatis程序, 二月红, 2020-08-26 22:50:29.0, 8888
    <==        Row: a6ec79eafe4e4f8d8a2aa56511522bee, Mybatis配置, 半截李, 2020-08-26 22:50:29.0, 7777
    <==        Row: 8c11053ef8d84c0185b0997b9d3ba79f, Mybatis分页, 陈皮阿四, 2020-08-26 22:50:29.0, 6666
    <==        Row: 305a489143e44334ac26c9dcd2ec404e, Mybatis注解, 吴老狗, 2020-08-26 22:50:29.0, 5555
    <==      Total: 5
    Blog(id=8fbd8a758ab54a79a0527b4db1b7dc91, title=Mybatis入门, author=张起灵, createTime=Wed Aug 26 22:50:29 CST 2020, views=9999)
    Blog(id=52f65ba723884c428295c271f018bd06, title=第一个Mybatis程序, author=二月红, createTime=Wed Aug 26 22:50:29 CST 2020, views=8888)
    Blog(id=a6ec79eafe4e4f8d8a2aa56511522bee, title=Mybatis配置, author=半截李, createTime=Wed Aug 26 22:50:29 CST 2020, views=7777)
    Blog(id=8c11053ef8d84c0185b0997b9d3ba79f, title=Mybatis分页, author=陈皮阿四, createTime=Wed Aug 26 22:50:29 CST 2020, views=6666)
    Blog(id=305a489143e44334ac26c9dcd2ec404e, title=Mybatis注解, author=吴老狗, createTime=Wed Aug 26 22:50:29 CST 2020, views=5555)
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69379752]
    Returned connection 1765250898 to pool.
    
  4. 小结:

    • 由结果可以看出,当什么都不输入的是sql语句自动变成 select * from blog

    • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

    • 如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能,比如,和 where 元素等价的自定义 trim 元素为:

      <trim prefix="WHERE" prefixOverrides="AND |OR ">
        ...
      </trim>
      
    • prefixOverrides 属性会忽略通过管道符分隔的文本序列,上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容

5.2、set语句

  1. 接口

    int updateBlog(Map map);
    
  2. xml文件

     <update id="updateBlog" parameterType="map">
            update blog
            <set>
                <if test="title != null">
                    title = #{title},
                </if>
                <if test="author != null">
                    author = #{author}
                </if>
            </set>
            where id = #{id}
        </update>
    
  3. 测试

        @Test
        public void updateBlog(){
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            HashMap map = new HashMap();
            map.put("title","Mybatis入门2");
            //map.put("author","张起灵");
            map.put("id","8fbd8a758ab54a79a0527b4db1b7dc91");
            mapper.updateBlog(map);
    
            sqlSession.close();
        }
    
  4. 结果

    Opening JDBC Connection
    Created connection 1024429571.
    ==>  Preparing: update blog SET title = ? where id = ? 
    ==> Parameters: Mybatis入门2(String), 8fbd8a758ab54a79a0527b4db1b7dc91(String)
    <==    Updates: 1
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3d0f8e03]
    Returned connection 1024429571 to pool.
    
  5. 小结

    • 用于动态更新语句的类似解决方案叫做 set

    • set元素可以用于动态包含需要更新的列,忽略其它不更新的列

    • 这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

      来看看与 set 元素等价的自定义 trim 元素吧:

      <trim prefix="SET" suffixOverrides=",">
        ...
      </trim>
      
    • 注意,我们覆盖了后缀值设置,并且自定义了前缀值。

6.choose (when, otherwise)语句

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

  1. 接口

    List<Blog> getByBolg(Map map);
    
  2. xml文件

    <select id="getByBlog" parameterType="map" resultType="blog">
            select * from blog
            <where>
                <choose>
                    <when test="title != null">
                        and title = #{title}
                    </when>
                    <when test="author != null">
                        and author = #{author}
                    </when>
                    <otherwise>
    					and 1=1
                    </otherwise>
                </choose>
            </where>
        </select>
    
  3. 测试

        @Test
        public void getByBlog(){
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            HashMap map = new HashMap();
            map.put("title","Mybatis入门");
            map.put("author","张起灵");
            List<Blog> byBlog = mapper.getByBlog(map);
            for (Blog blog : byBlog) {
                System.out.println(blog);
            }
            sqlSession.close();
        }
    
  4. 结果

    Opening JDBC Connection
    Created connection 494586676.
    ==>  Preparing: select * from blog WHERE title = ? 
    ==> Parameters: Mybatis入门(String)
    <==      Total: 0
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1d7acb34]
    Returned connection 494586676 to pool.
    
  5. 小结

    • 传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。
    • 若两者都没有传入,就返回所有.
    • 若两者都传入,找不到

7.sql片段

有时候,我们可能会将一些功能的部分抽取出来,方便复用!

  1. 使用SQL标签抽取公共的部分

       <sql id="if-title-author">
            <if test="title != null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author}
            </if>
        </sql>
    
  2. 在需要使用的地方使用include标签引用即可

    <update id="updateBlog" parameterType="map">
            update blog
            <set>
                <include refid="if-title-author"></include>
            </set>
            where id = #{id}
        </update>
    
  3. 注意事项:

    • 最好基于单表来定义SQL片段
    • 不要存在where标签

8.foreach语句

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候

  1. 接口

    List<Blog> queryBYBlogForEach(Map map);
    
  2. xml文件

        <!--    foreach语句-->
        <select id="queryByBlogForEach" parameterType="map" resultType="blog">
            select * from blog
            <where>
               <foreach collection="viewss" open="(" separator="or" close=")" item="views">
                   views = #{views}
               </foreach>
            </where>
        </select>
    
  3. 测试

        @Test
        public void queryByBolgForEach(){
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            HashMap map = new HashMap();
            ArrayList<Integer> viewss = new ArrayList<>();
            map.put("viewss",viewss);
            viewss.add(9999);
            viewss.add(8888);
    
            mapper.queryByBlogForEach(map);
            sqlSession.close();
        }
    
  4. 结果

    Opening JDBC Connection
    Created connection 1765250898.
    ==>  Preparing: select * from blog WHERE ( views = ? or views = ? ) 
    ==> Parameters: 9999(Integer), 8888(Integer)
    <==    Columns: id, title, author, create_time, views
    <==        Row: 8fbd8a758ab54a79a0527b4db1b7dc91, Mybatis入门, 张起灵, 2020-08-26 22:50:29.0, 9999
    <==        Row: 52f65ba723884c428295c271f018bd06, 第一个Mybatis程序, 二月红, 2020-08-26 22:50:29.0, 8888
    <==      Total: 2
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@69379752]
    Returned connection 1765250898 to pool.
    

9.总结

  • 所谓的动态sql,本质还是sql语句,只是我们可以在sql层面,去执行一个逻辑代码
  • 多态Sql就是在拼接sql语句,我们只要保证sql的正确性,按照sql的格式,去排列组合就可以了
  • 建议:
    • 先在mysql中写出完整的sql再对应的去修改成为我们的动态sqls实现通用即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值