Mybatis—动态SQL

Posted Godles

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis—动态SQL相关的知识,希望对你有一定的参考价值。

  • MyBatis 的强大特性之一便是它的动态 SQL。
  • 动态SQL就是根据不同的条件生成不同的SQL语句。
  • 动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。
  • MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

1、搭建环境

最完整的包结构图示:

  1. 创建博客表

    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. 创建基础工程

    1. pom.xml导包

      <dependencies>
              <!--mysql驱动-->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>5.1.47</version>
              </dependency>
              <!--mybatis-->
      
              <dependency>
                  <groupId>org.mybatis</groupId>
                  <artifactId>mybatis</artifactId>
                  <version>3.5.4</version>
              </dependency>
              <!--junit-->
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.12</version>
              </dependency>
             <!--lombok-->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>1.18.10</version>
              </dependency>
          </dependencies>
      
      
    2. resources下编写配置文件mybatis-config.xml

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <!--configuration核心配置文件-->
      <configuration>
      
          <!--引入外部配置文件-->
          <properties resource="db.properties"/>
      
          <settings>
      
              <setting name="logImpl" value="LOG4J"/>
              <!--是否开启自动驼峰命名规则(camel case)映射-->
              <setting name="mapUnderscoreToCamelCase" value="true"/>
          </settings>
      
          <!--可以给实体类起别名-->
          <typeAliases>
              <package name="com.jiang.pojo"/>
          </typeAliases>
      
          <environments default="development">
              <environment id="development">
                  <transactionManager type="JDBC"/>
                  <dataSource type="POOLED">
                      <property name="driver" value="${driver}"/>
                      <property name="url" value="${url}"/>
                      <property name="username" value="${username}"/>
                      <property name="password" value="${pwd}"/>
                  </dataSource>
              </environment>
          </environments>
      
          <mappers>
              <mapper class="com.jiang.dao.BlogMapper"/>
          </mappers>
      
      
      </configuration>
      
    3. resources下编写配置文件db.properties

      driver=com.mysql.jdbc.Driver
      url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
      username=root
      pwd=123456
      
    4. resources下编写配置文件log4j.properties

      ### 设置###
      log4j.rootLogger = debug,stdout,D,E
      
      ### 输出信息到控制抬 ###
      log4j.appender.stdout = org.apache.log4j.ConsoleAppender
      log4j.appender.stdout.Target = System.out
      log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
      log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
      
      ### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
      log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
      log4j.appender.D.File = ./log/log.log
      log4j.appender.D.Append = true
      log4j.appender.D.Threshold = DEBUG
      log4j.appender.D.layout = org.apache.log4j.PatternLayout
      log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
      
      ### 输出ERROR 级别以上的日志到=E://logs/error.log ###
      log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
      log4j.appender.E.File =./log/error.log
      log4j.appender.E.Append = true
      log4j.appender.E.Threshold = ERROR
      log4j.appender.E.layout = org.apache.log4j.PatternLayout
      
    5. java包下创建com.jiang.pojo包,在此包下编写Blog实体类

      package com.jiang.pojo;
      
      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;
      
      
      }
      
      
    6. java包下创建com.jiang.dao包,在此包下编写实体类对应的

      • BlogMapper接口

      • BlogMapper.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.jiang.dao.BlogMapper">
        </mapper>
        
    7. java包下创建com.jiang.utils包编写MybatisUtils工具类

      package com.jiang.utils;
      
      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 java.io.IOException;
      import java.io.InputStream;
      
      //sqlSessionFactory --> sqlSession
      public class MybatisUtils {
      
          private static SqlSessionFactory sqlSessionFactory;
      
          static{
              try {
                  //使用Mybatis第一步:获取sqlSessionFactory对象
                  String resource = "mybatis-config.xml";
                  InputStream inputStream = Resources.getResourceAsStream(resource);
                  sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
      
          public static SqlSession  getSqlSession(){
              return sqlSessionFactory.openSession(true);
          }
      
      }
      
      
    8. java包下创建com.jiang.utils包编写UUID工具类

      //这个帮助我们等会生成随机数
      package com.jiang.utils;
      
      import org.junit.Test;
      
      import java.util.UUID;
      
      @SuppressWarnings("all") //抑制警告
      public class IDutils {
      
          public static String getId(){
              return UUID.randomUUID().toString().replaceAll("-","");
          }
      }
      
      
    9. 添加表中的数据

      • 接口

         //插入数据
            int addBlog(Blog blog);
        
      • 接口配置

         <insert id="addBlog" parameterType="blog">
                insert into mybatis.blog (id, title, author, create_time, views)
                values (#{id},#{title}, #{author}, #{createTime}, #{views});
            </insert>
        
      • test.java包中编写测试类MyTest

        @Test
            public void addInitBlog() {
                SqlSession session = MybatisUtils.getSqlSession();
                BlogMapper mapper = session.getMapper(BlogMapper.class);
                for (int i = 0; i < 4; i++) {
                    Blog blog = new Blog();
                    blog.setId(IDutils.getId());
                    blog.setTitle("Mybatis So easy");
                    blog.setAuthor("姜嘉航");
                    blog.setCreateTime(new Date());
                    blog.setViews(9999);
                    mapper.addBlog(blog);
                }
        
        
                Blog blog1 = new Blog();
                blog1.setId(IDutils.getId());
                blog1.setTitle("Java So easy");
                blog1.setAuthor("姜嘉航");
                blog1.setCreateTime(new Date());
                blog1.setViews(9999);
                mapper.addBlog(blog1);
        
                Blog blog2 = new Blog();
                blog2.setId(IDutils.getId());
                blog2.setTitle("Spring So easy");
                blog2.setAuthor("姜嘉航");
                blog2.setCreateTime(new Date());
                blog2.setViews(9999);
                mapper.addBlog(blog2);
        
                Blog blog3 = new Blog();
                blog3.setId(IDutils.getId());
                blog3.setTitle("微服务 So easy");
                blog3.setAuthor("姜嘉航");
                blog3.setCreateTime(new Date());
                blog3.setViews(9999);
                mapper.addBlog(blog3);
        
                Blog blog4 = new Blog();
                blog4.setId(IDutils.getId());
                blog4.setTitle("微服务 So easy");
                blog4.setAuthor("姜嘉航");
                blog4.setCreateTime(new Date());
                blog4.setViews(9999);
                mapper.addBlog(blog4);
        
        
                session.close();
            }
        
        

      插入成功!

2、If

要求:

通过作者名,和博客名字来查询博客

如果作者名字为null,则根据博客名字来查询

  1. 编写BlogMapper接口

      //查询博客
        List<Blog> getBlogByIF(Map map);
    
  2. 编写BlogMapper.xml

    <!--如果我们需要拼接where条件,又不希望客户端传递错误信息,需要更加智能的where标签,如果后面的语句有逗号,就自动添加where,如果后面语句开头是and或者or,它可以自动去掉-->
    <select id="getBlogByIF" parameterType="map" resultType="Blog">
            select * from mybatis.blog
                <if test="title != null">
                    title = #{title}
                </if>
                <if test="author != null">
                    and author = #{author}
                </if>
        </select>
    
  3. 测试不同参数结果运行

      @Test
        public void testGetBlogIf() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            HashMap<String, String> map = new HashMap<String, String>();
            map.put("title","Mybatis So easy");
            map.put("author","姜小姜");
            List<Blog> blogByIF = mapper.getBlogByIF(map);
    
            for (Blog blog : blogByIF) {
                System.out.println(blog);
            }
            sqlSession.close();
        }
    

3、trim(where,set)

3.1、set

要求:更新博客

  1. 编写BlogMapper接口

    //更新博客
        int updateBlog(Map map);
    
  2. 编写BlogMapper.xml

     <!--set标签,这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号-->
    <update id="updateBlog" parameterType="map">
            update mybatis.blog
            <set>
                <if test="title != null">
                    title = #{title},
                </if>
                <if test="author != null">
                    author = #{author}
                </if>
            </set>
            where id = #{id}
    
        </update>
    

    set元素等价于自定义的trim元素:

    <trim prefix="SET" suffixOverrides=",">
      ...
    </trim>
    
  3. 测试

     @Test
        public void upadateBlog() {
            SqlSession session = MybatisUtils.getSqlSession();
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            HashMap<String, String> map = new HashMap<String, String>();
    
    
            map.put("id", "c4cab03768a34dcbaafa36731e680b3e");
            map.put("author","姜小帅");
    
            mapper.updateBlog(map);
    
    
        }
    
    

3.2、where

  select * from mybatis.blog
        <where>
            <if test="title != null">
                title = #{title}
            </if>
            <if test="author != null">
                and author = #{author}
            </if>
        </where>

where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。

where元素等价于自定义trim元素

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

4、SQL片段

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

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

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

    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <include refid="if-title-author"></include>
        </where>
    </select>
    

注意事项:

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

5、foreach

collection传递的参数:你要给他传递的集合对象

item:遍历出来集合的每一项,可以再foreach中使用

open:用什么字符串打开

close:用什么字符串结束

separator:分隔符

  1. 编写BlogMapper接口

    List<Blog> queryBlogForeach(Map map);
    
  2. 编写BlogMapper.xml

      <!--相当于sql语句:
          select * from mybatis.blog where and(id=1 or id=2 or id=3 or...)
    -->
    <select id="queryBlogForeach" parameterType="map" resultType="blog">
            select * from mybatis.blog
    
            <where>
               <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                   id = #{id}
               </foreach>
            </where>
    
        </select>
    
  3. 测试

     @Test
        public void queryBlogForEach() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
            HashMap map = new HashMap();
    
            ArrayList<String> ids = new ArrayList<String>();
            ids.add("c4cab03768a34dcbaafa36731e680b3e");
            ids.add("372b3c3a0ce54cb19675e2f703afb310");
            ids.add("523b2881f4d34682956b1ede6214daba");
    
            map.put("ids", ids);
            List<Blog> blogs = mapper.queryBlogForeach(map);
    
            System.out.println(blogs);
    
            sqlSession.close();
        }
    

6、choose,when,otherwise

有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句

还是上面的例子

 select * from mybatis.blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>

以上是关于Mybatis—动态SQL的主要内容,如果未能解决你的问题,请参考以下文章

mybatis动态sql片段与分页,排序,传参的使用

Mybatis -- 动态Sql概述动态Sql之<if>(包含<where>)动态Sql之<foreach>sql片段抽取

mybatis动态sql之利用sql标签抽取可重用的sql片段

MYBATIS05_ifwherechoosewhentrimsetforEach标签sql片段

[mybatis]动态sql_sql_抽取可重用的sql片段

Mybatis动态sql