【Mybatis】动态sql及常用标签

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 【Mybatis】动态sql及常用标签,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

闲话

最近对象跟我说,她把我写进论文最后的致谢里了,没有想过这么浪漫的事也会发生在我的身上

基本要点

定义:动态SQL可以理解为,通过使用一系列标签,去动态生成我们所需要的SQL语句

1、IF标签

首先我们创建一个表,里面存放博客相关信息

DROP TABLE IF EXISTS `t_decade_blog`;

create table t_decade_blog
(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`name` VARCHAR(50) NOT NULL COMMENT '博客名称',
`author` VARCHAR(50) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` INT(20) NOT NULL COMMENT '博客浏览量'
)ENGINE = INNODB CHARACTER SET = utf8;

接着我们创建对应的Java实体类

package com.decade.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
    private String id;

    private String name;

    private String author;

    private Date createTime;

    private int views;
}

接着我们创建接口类和对应的SQL映射文件

package com.decade.mapper;

import com.decade.entity.Blog;

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

public interface BlogMapper {

    int addBlog(Blog blog);
    
    List<Blog> queryBlogByIf(Map map);
}
<?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.decade.mapper.BlogMapper">

    <insert id="addBlog" parameterType="Blog">
        insert into t_decade_blog (id,name,author,create_time,views)
        values (#{id},#{name},#{author},#{createTime},#{views})
    </insert>

    <select id="queryBlogByIf" parameterType="map" resultType="Blog">
        select * from t_decade_blog
        where author = 'Decade0712'
        <if test="views != null">
            and views= #{views}
        </if>
        <if test="name != null">
            and name = #{name}
        </if>
    </select>
</mapper>

另外,我们还有工具类IdUtils,用于创建随机id

package com.decade.utils;

import java.util.UUID;

public class IdUtils {
    public static String getId() {
        return UUID.randomUUID().toString();
    }
}

和Mybatis创建sqlSession的工具类

package com.decade.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;

/**
 * Mybatis工具类
 */
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            // 使用Mybatis第一步:获取sqlSession
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 既然有了sqlSessionFactory,我们就可以得到sqlSession实例了
    // sqlSession完全包含了面向数据库执行sql命令所需要的方法,如果要开启自动提交事务,openSession中设置true即可
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession(true);
    }

}

接着是核心配置文件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>
    <properties resource="db.properties">
        <property name="username" value="decade"/>
        <property name="password" value="11111"/>
    </properties>
    
    <settings>
        <!-- 标准的日志工厂 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
        <package name="com.decade.entity"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--驱动配置,com.mysql.jdbc.driver -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--每一个mapper.xml文件都需要在核心配置文件中注册-->
    <mappers>
        <mapper class="com.decade.mapper.BlogMapper"/>
    </mappers>
</configuration>

和数据库信息文件db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/decade_test?useUnicode=true&characterEncoding=UTF-8
username=root
password=root

最后我们创建两个测试方法,一个插入数据,另外一个测试if标签

import com.decade.entity.Blog;
import com.decade.mapper.BlogMapper;
import com.decade.utils.IdUtils;
import com.decade.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyTest {

    @Test
    public void test() {
        Blog blog = new Blog(IdUtils.getId(), "Mybatis系列", "Decade0712", new Date(), 2000);
        Blog blog1 = new Blog(IdUtils.getId(), "Spring系列", "Decade0712", new Date(), 4000);
        Blog blog2 = new Blog(IdUtils.getId(), "设计模式系列", "Decade0712", new Date(), 6000);

        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            mapper.addBlog(blog);
            mapper.addBlog(blog1);
            mapper.addBlog(blog2);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    }

    @Test
    public void test2() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Map<String, Object> map = new HashMap<>();
            map.put("name", "Spring系列");
            map.put("views", 4000);
            List<Blog> blogs = mapper.queryBlogByIf(map);
            blogs.forEach(System.out::println);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    }
}

按照预期,如果我们传入空的map,那么会查出所有的数据,否则查出符合条件的数据

运行结果如下
在这里插入图片描述

2、choose标签

MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
只要满足其中一个就退出判断了,后面的就不会继续判断了
otherwise等价于default,如果上述条件都不满足,就走otherwise下的条件语句

<select id="queryBlogByWhich" parameterType="map" resultType="Blog">
	select * from t_decade_blog where create_time = '2022-03-28 22:30:14'
    <choose>
	    <when test="views != null">
        	and views = #{views}
        </when>
        <when test="name != null">
	        and name = #{name}
        </when>
	    <otherwise>
            and author = 'Decade0712'
	    </otherwise>
    </choose>
</select>

3、where标签

在我们使用if标签时,有可能写成这样

<select id="queryBlogInfo" parameterType="map" resultType="Blog">
  SELECT * FROM BLOG
  WHERE
  <if test="views != null">
	  views= #{views}
  </if>
  <if test="name != null">
  	  and name = #{name}
  </if>
</select>

如果上述条件都不满足,那么查询sql就会变成

SELECT * FROM BLOG WHERE

如果只匹配第二个条件,那么sql会变成,也会查询失败

SELECT * FROM BLOG
WHERE
AND name = 'decade'

上述两种情况均会导致查询失败

为了避免这种情况发生,我们通常将where标签和if标签搭配使用,类似于

<select id="queryBlogByWhere" parameterType="map" resultType="Blog">
	select * from t_decade_blog
    <where>
	    <if test="views != null">
	        and views= #{views}
        </if>
        <if test="name != null">
            and name = #{name}
        </if>
   </where>
</select>

where 标签只会在满足条件的情况下才插入 “WHERE” 关键字,如果第一个条件是以 “AND” 或 “OR”为开头,where 元素也会自动将它们去除,比如这里,如果views不为空,那么where会自动将这里的and去掉,最后执行的查询语句是

SELECT * FROM BLOG
WHERE
views = 'decade'

4、set标签

set标签一般用于代替sql语句中的set关键字

<update id="updateBlog" parameterType="map">
	update t_decade_blog
    <set>
	    <if test="name != null">name=#{name},</if>
        <if test="author != null">author=#{author},</if>
        <if test="views != null">views=#{views}</if>
    </set>
    where id = #{id}
</update>

和where标签异曲同工的是,它会自动帮你去掉多余的逗号
就像上面,如果只满足第一个if,它会自动删除name后面的逗号
最后执行的sql为

update t_decade_blog set name = 'aaa' where id = 'id'

5、trim标签

可以理解为,我们可以使用trim标签来自己决定如何拼接sql

参数解释如下

prefix:在trim标签内sql语句加上前缀。

prefixOverrides:指定去除多余的前缀内容 
如:prefixOverrides = "and",去除trim标签内sql语句多余的前缀"and"。

suffix:在trim标签内sql语句加上后缀。

suffixOverrides:指定去除多余的后缀内容
如:suffixOverrides=",",去除trim标签内sql语句多余的后缀","

这里,我们在sql前面加上(前缀,以)为后缀结尾,并且去掉多余的逗号

<insert id="insert" parameterType="map">
	INSERT INTO t_decade_blog 
    <trim prefix="(" suffix=")" suffixOverrides=",">
        id,name,author,create_time,views
    </trim>
    <trim prefix="VALUES(" suffix=")" suffixOverrides=",">
        #{id},#{name}, #{author}, #{createTime},#{views}
    </trim>
</insert>

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

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

同理,set标签也可以用trim去定义

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

6、foreach标签

  • foreach 元素的功能非常强大,你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach
  • 它也允许你指定开头(open)与结尾(close)的字符串以及集合项迭代之间的分隔符(separator)
  • 当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素
  • 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值

我们结合一个例子来学习一下,假设我们想用spring系列和mybatis系列的id去查一下博客信息
用SQL应该是

select * from t_decade_blog where id in ('76782763-48d0-4cef-b8e1-1054e181e41d','06bd6c69-e47e-4e78-9e02-5110a013e457');

现在,我们用mybatis中的foreach标签实现一下
首先我们在SQL映射文件中定义一个查询语句,这里要遍历的集合叫ids,每一次遍历得到的元素叫id,我们以(开头,用逗号做分隔符,以)结尾

<select id="queryBlogByForeach" parameterType="map" resultType="Blog">
	select * from t_decade_blog where id in
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>

然后我们定义一下接口类

package com.decade.mapper;

import com.decade.entity.Blog;

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

public interface BlogMapper {

    List<Blog> queryBlogByForeach(Map map);

}

最后我们写一下测试方法

import com.decade.entity.Blog;
import com.decade.mapper.BlogMapper;
import com.decade.utils.IdUtils;
import com.decade.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.*;

public class MyTest {
    @Test
    public void test2() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Map<String, Object> map4 = new HashMap<>();
            List<String> ids = new ArrayList<>();
            ids.add("06bd6c69-e47e-4e78-9e02-5110a013e457");
            ids.add("76782763-48d0-4cef-b8e1-1054e181e41d");
            map4.put("ids", ids);
            List<Blog> blogs4 = mapper.queryBlogByForeach(map4);
            blogs4.forEach(System.out::println);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    }
}

运行结果如下,成功查询出我们需要的数据
在这里插入图片描述

7、SQL片段

我们可以将重复出现的SQL部分提取出来

注意:
最好基于单表来定义sql片段,不要有太复杂的查询(例如多表查询)
不要存在where标签

<select id="queryBlogByWhere" parameterType="map" resultType="Blog">
	select * from t_decade_blog
    <where>
	    <if test="views != null">
	        and views= #{views}
        </if>
        <if test="name != null">
            and name = #{name}
        </if>
   </where>
</select>

上方的查询可以抽取为

<sql id="queryByIf">
	<if test="views != null">
		and views= #{views}
    </if>
    <if test="name != null">
        and name = #{name}
    </if>
</sql>

<select id="queryBlogByWhere" parameterType="map" resultType="Blog">
	select * from t_decade_blog
    <where>
	    <include refid="queryByIf"/>
   </where>
</select>

如有错误,欢迎指正!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/136771.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!