增删改查

  1. 增删改返回类型支持Integer、Long、Boolean,void直接在接口中定义。
  2. 需要手动提交事务:session.commit();
sqlSessionFactory.session();//手动提交
sqlSessionFactory.session(true);//自动提交

动态SQL

动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。MyBatis3精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
四个基本元素:

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

从参数中取出值进行判断,如果有参数且不为空则拼接SQL。(多个条件直接传入对象)。
遇到特殊字符使用转义字符
查询时如果有些条件没带,则会拼装错误
如:

SELECT * FROM BLOG 
    WHERE state = ‘ACTIVE’  AND title = title

解决办法:1.在sql语句where添加 1=1,后续拼接AND XXX不会出错。
使用where标签见所有条件包含在内,MyBatis会将标签中拼接的SQL多出来的AND或者 OR 去掉。局限:只会去掉第一个AND,如果将AND写在语句后:<if test="title != null"> title like #{title} AND </if>也会出错,使用trim解决。

  • 两个默认内置参数:
  1. _parameter:代表整个参数
    单个参数:即为_parameter本身(代表当前参数对象)
    多个参数:参数会被封装成一个map:_parameter代表这个map
  2. _databaseId:如果配置了databaseIdProvider标签, _databaseId就代表当前数据的别名。(可以解决一个方法中根据不同数据库执行不同的sql语句)。
  • 抽取sql(支持上述判断)

<sql id="">与<select>同级,提取可重用的sql片段(常用列名),方便引用。
使用<include refid="">通过id抽取sql。
include可以自定义一些property,sql内部标签就能使用自定义的属性。取值方法仅为${id}

if

动态 SQL 通常要做的事情是根据条件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike"
        resultType="Blog">
    SELECT * FROM BLOG 
    WHERE state = ‘ACTIVE’ 
    <if test="title != null">
         AND title like #{title}
    </if>
    </select>

这条语句提供了一种可选的查找文本功能。如果没有传入“title”,那么所有处于“ACTIVE”状态的BLOG都会返回;反之若传入了“title”,那么就会对“title”一列进行模糊查找并返回 BLOG 结果(细心的读者可能会发现,“title”参数值是可以包含一些掩码或通配符的)。

如果希望通过“title”和“author”两个参数进行可选搜索该怎么办呢?首先,改变语句的名称让它更具实际意义;然后只要加入另一个条件即可。

<select id="findActiveBlogLike"
      resultType="Blog">
    SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
    <if test="title != null">
         AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
          AND author_name like #{author.name}
    </if>
    </select>
choose, when, otherwise

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

还是上面的例子,但是这次变为提供了“title”就按“title”查找,提供了“author”就按“author”查找的情形,若两者都没有提供,就返回所有符合条件的 BLOG(实际情况可能是由管理员按一定策略选出 BLOG 列表,而不是返回大量无意义的随机结果)。

<select id="findActiveBlogLike"
 resultType="Blog">
 SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
  AND title like #{title}
</when>
<when test="author != null and author.name != null">
  AND author_name like #{author.name}
</when>
<otherwise>
  AND 1= 1
</otherwise>
  </choose>
</select>
trim, where, set

前面几个例子已经合宜地解决了一个臭名昭著的动态 SQL 问题。现在回到“if”示例,这次我们将“ACTIVE = 1”也设置成动态的条件,看看会发生什么。

<select id="findActiveBlogLike"
 resultType="Blog">
  SELECT * FROM BLOG 
  WHERE 
  <if test="state != null">
state = #{state}
  </if> 
  <if test="title != null">
AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
AND author_name like #{author.name}
  </if>

如果这些条件没有一个能匹配上会发生什么?最终这条 SQL 会变成这样:

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

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件句式来解决,如果你也曾经被迫这样写过,那么你很可能从此以后都不会再写出这种语句了。

MyBatis 有一个简单的处理,这在 90% 的情况下都会有用。而在不能使用的地方,你可以自定义处理方式来令其正常工作。一处简单的修改就能达到目的:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG 
  <where> 
    <if test="state != null">
         state = #{state}
    </if> 
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

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

如果 where 元素没有按正常套路出牌,我们可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim (使用不多)元素为:

//prefix:前缀,将拼接后的SQL指定一个前缀
//prefixOverrides:前缀覆盖:去掉字符串前多余的字符
//suffix:后缀
//suffixOverrides:后缀覆盖,去掉字符串后多余的字符
<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
</trim>

prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它的作用是移除所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容。

类似的用于动态更新语句的解决方案叫做 set。set 元素可以用于动态包含需要更新的列,而舍去其它的(例如语句中的“,”,利用trim也可以实现)。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。(译者注:因为用的是“if”元素,若最后一个“if”没有匹配上而前面的匹配上,SQL 语句的最后就会有一个逗号遗留)

若你对 set 元素等价的自定义 trim 元素的代码感兴趣,那这就是它的真面目:

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

注意这里我们删去的是后缀值,同时添加了前缀值。

foreach

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

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  //open:以xx开始
  //close:以xx结束
  //separator:分隔符
  //index/item:遍历list时是索引,item是当前值。
  遍历map时是map的key,item是map的值。
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

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

注意 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。可用于批量保存。

  • mysql批量保存的两种方式:
    1.values: <foreach connect="" item="" separator=","> (#{emp.id},#{emp.name})</foreach> 2.insert语句 <foreach connect="" item="" separator=";"> insert (id,name) values (#{emp.id},#{emp.name})</foreach>
    第二种需要数据库连接支持allowMultiQieries=true;分号分隔sql可以用于其他批量操作
  • oracle不支持values(),();
    oracle支持的方式:
  1. 将多个SQL语句包含在begin - end;中
    2.利用中间表
bind

bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。比如:

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

多数据库支持

一个配置了“_databaseId”变量的 databaseIdProvider 可用于动态代码中,这样就可以根据不同的数据库厂商构建特定的语句。比如下面的例子:

<insert id="insert">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
    </if>
    <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
    </if>
  </selectKey>
  insert into users values (#{id}, #{name})
</insert>

动态 SQL 中的可插拔脚本语言

MyBatis 从 3.2 开始支持可插拔脚本语言,这允许你插入一种脚本语言驱动,并基于这种语言来编写动态 SQL 查询语句。

可以通过实现以下接口来插入一种语言:

public interface LanguageDriver {
  ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
  SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
  SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}

一旦设定了自定义语言驱动,你就可以在 mybatis-config.xml 文件中将它设置为默认语言:

<typeAliases>
  <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<settings>
  <setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>

除了设置默认语言,你也可以针对特殊的语句指定特定语言,可以通过如下的 lang 属性来完成:

<select id="selectBlog" lang="myLanguage">
  SELECT * FROM BLOG
</select>

或者,如果你使用的是映射器接口类,在抽象方法上加上 @Lang 注解即可:

public interface Mapper {
  @Lang(MyLanguageDriver.class)
  @Select("SELECT * FROM BLOG")
  List<Blog> selectBlog();
}

注意 可以将 Apache Velocity 作为动态语言来使用,更多细节请参考 MyBatis-Velocity 项目。

你前面看到的所有 xml 标签都是由默认 MyBatis 语言提供的,而它由别名为 xml 的语言驱动器 org.apache.ibatis.scripting.xmltags.XmlLanguageDriver 所提供。



文章目录

  • 增删改查
  • 动态SQL
  • if
  • choose, when, otherwise
  • trim, where, set
  • foreach
  • bind
  • 多数据库支持
  • 动态 SQL 中的可插拔脚本语言