1、动态SQL

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。

环境搭建

项目结构

mybatis 查询字段resultType 实体不能自动转成驼峰_SQL

建表语句

CREATE TABLE `blog` (  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '博客id',  `title` varchar(30) NOT NULL COMMENT '博客标题',  `author` varchar(30) NOT NULL COMMENT '博客作者',  `create_time` datetime NOT NULL COMMENT '创建时间',  `views` int(30) NOT NULL COMMENT '浏览量',  `state` int(2) DEFAULT NULL COMMENT '状态',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

实体类

@Data@NoArgsConstructor@AllArgsConstructorpublic class Blog {    private Integer id;    private String title;    private String author;    private Date createTime;    private int views;    private int active;}

MybatisUtil工具类

public class MybatisUtil {    static SqlSessionFactory sqlSessionFactory;    static{        try{            String resource = "mybatis-config.xml";            InputStream inputStream = Resources.getResourceAsStream(resource);            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        }catch (Exception e) {            e.printStackTrace();        }    }    public static SqlSession getSqlSession() {        return sqlSessionFactory.openSession();    }}

mybatis-config.xml核心配置文件

<?xml  version="1.0" encoding="UTF-8" ?>/span>        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>        <properties resource="db.properties" />    <settings>        <setting name="logImpl" value="LOG4J"/>    settings>        <typeAliases>        <package name="com.liziba.mybatis.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="${password}"/>            dataSource>        environment>    environments>    <mappers>        <mapper resource="com/liziba/mybatis/mapper/BlogMapper.xml" />    mappers>configuration>

db.properties数据库配置文件

driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTCusername=xxxxpassword=xxxx

log4j.properties日志配置文件

# 将等级为DEBUG级别的日志输出到console和file两个目的地log4j.rootLogger=DEBUG,console,file# 控制台输出配置log4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.Target=System.outlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.layout=org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%c]-%m%n# 文件输出配置log4j.appender.file=org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./log/rzp.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n# 日志输出级别配置log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sq1.PreparedStatement=DEBUG

if

这条语句提供了一个可选的文本查找类型的功能。如果没有传入"title",那么所有active = 1的BLOG都会返回;反之若传入了"title",那么就会把模糊查找"title"内容的BLOG结果返回

<select id="findActiveBlogsWithTitleLike" parameterType="string" resultType="blog">    select * from blog    where active = 1    <if test="title != null">        and title like #{title}    if>select>

如果想可选地通过"title"和"author"两个条件搜索

<select id="selectBlogsUseIf" parameterType="map" resultType="blog">    select * from blog    where active = 1    <if test="title != null">        and title = #{title}    if>    <if test="author != null">        and author = #{author}    if>select>

choose (when, otherwise)

有些时候,我们不想用到所有的条件语句,而只想从中择其一二

<select id="findActiveBlogsOrViewsWithLike" parameterType="map" resultType="blog">    select * from blog    where active = 1    <choose>        <when test="title != null">            and title like #{title}        when>        <when test="author != null">            and author like #{author}        when>        <otherwise>            and views > 1000        otherwise>    choose>select>

trim (where, set)

where

现在考虑回到"if"示例,这次我们将active = 1也设置成动态的条件,通过参数传入,看看会发生什么。

<select id="findActiveBlogsWithTitleLike" parameterType="map" resultType="blog">    select * from blog    where     <if test="active != null">    active = #{active}    if>     <if test="title != null">        and title like #{title}    if>select>

如果这些条件没有一个能匹配上将会怎样?最终这条 SQL 会变成这样

select * from blog where

这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样

select * from blog where and title like 'xxxx'

这个查询也会失败

如何解决呢?我们通过where来解决,where 元素知道只有在一个以上的if条件有值的情况下才去插入"WHERE"子句。而且,若最后的内容是"AND"或"OR"开头的,where 元素也知道如何将他们去除。

<select id="findActiveBlogsWithTitleLike" parameterType="map" resultType="blog">    select * from blog    <where >      <if test="active != null">    active = #{active}    if>     <if test="title != null">        and title like #{title}    if>    where>select>

set

类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的

这里需要注意的是每个值后面的   ,

<update id="updateBlogIfNecessary" parameterType="blog">    update blog     <set>        <if test="title != null">            title = #{title},        if>        <if test="author != null">            author = #{author},        if>        <if test="views != null">            views = #{views},        if>        <if test="active != null">            active = #{active},        if>    set>    where id = #{id}update>

foreach

动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候

<select id="selectBlogsByIds" parameterType="list" resultType="blog">    select * from blog    where id in    <foreach collection="list" item="id" index="index" open="(" separator="," close=")">        #{id}    foreach>select>

foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

注意 你可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以"list"作为键,而数组实例的键将是"array"。

SQL 片段

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

  • 使用SQL标签抽取公共部分
<sql id="if-title-author">    <if test="title!=null">        title = #{title}    if>    <if test="author!=null">        and author = #{author}    if>sql>
  • 在需要使用的地方使用Include标签引用即可
<select id="getBlogs" parameterType="map" resultType="blog">    select * from blog    <where>        <include refid="if-title-author">include>    where>select>

自定义标签

where等价于

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

set等价于

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