Dynamic SQL

Dynamic SQL

One of the most powerful features of MyBatis has always been its Dynamic SQL capabilities

If you have any experience with JDBC or any similar framework, you understand how painful it is to conditionally concatenate strings of SQL together, making sure not to forget spaces or to omit a comma at the end of a list of columns. 

Dynamic SQL can be downright painful to deal with.

While working with Dynamic SQL will never be a party, MyBatis certainly improves the situation with a powerful Dynamic SQL language that can be used within any mapped SQL statement.

The Dynamic SQL elements should be familiar to anyone who has used JSTL or any similar XML based text processors. 

In previous versions of MyBatis, there were a lot of elements to know and understand. MyBatis 3 greatly improves upon this, and now there are less than half of those elements to work with. MyBatis employs powerful OGNL based expressions to eliminate most of the other elements:

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

if

The most common thing to do in dynamic SQL is conditionally include a part of a where clause. For example:

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

This statement would provide an optional text search type of functionality

If you passed in no title, then all active Blogs would be returned. 

But if you do pass in a title, it will look for a title like that (for the keen eyed, yes in this case your parameter value would need to include any masking or wildcard characters).

What if we wanted to optionally search by title and author? First, I’d change the name of the statement to make more sense. 

Then simply add another condition.

<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

Sometimes we don’t want all of the conditionals to apply, instead we want to choose only one case among many options. 

Similar to a switch statement in Java, MyBatis offers a choose element.

Let’s use the example above, but now let’s search only on title if one is provided, then only by author if one is provided. 

If neither is provided, let’s only return featured blogs (perhaps a strategically list selected by administrators, instead of returning a huge meaningless list of random blogs).

<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 featured = 1
    </otherwise>
  </choose>
</select>

trim, where, set

The previous examples have been conveniently dancing around a notorious dynamic SQL challenge. 

Consider what would happen if we return to our "if" example, but this time we make "ACTIVE = 1" a dynamic condition as well.

<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>
</select>

What happens if none of the conditions are met? You would end up with SQL that looked like this:

SELECT * FROM BLOG
WHERE

This would fail. What if only the second condition was met? You would end up with SQL that looked like this:

SELECT * FROM BLOG
WHERE
AND title like someTitle

This would also fail. This problem is not easily solved with conditionals, and if you’ve ever had to write it, then you likely never want to do so again.

MyBatis has a simple answer that will likely work in 90% of the cases. 

And in cases where it doesn’t, you can customize it so that it does. 

With one simple change, everything works fine:

<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>

The where element knows to only insert "WHERE" if there is any content returned by the containing tags. 

Furthermore, if that content begins with "AND" or "OR", it knows to strip it off.

If the where element does not behave exactly as you like, you can customize it by defining your own trim element. 

For example, the trim equivalent to the where element is:

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

The prefixOverrides attribute takes a pipe delimited list of text to override, where whitespace is relevant

The result is the removal of anything specified in the prefixOverrides attribute, and the insertion of anything in the prefix attribute.

There is a similar solution for dynamic update statements called set

The set element can be used to dynamically include columns to update, and leave out others. For example:

<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>

Here, the set element will dynamically prepend the SET keyword, and also eliminate any extraneous commas that might trail the value assignments after the conditions are applied.

If you’re curious about what the equivalent trim element would look like, here it is:

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

Notice that in this case we’re overriding a suffix, while we’re still appending a prefix.

foreach

Another common necessity for dynamic SQL is the need to iterate over a collection, often to build an IN condition. For example:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

The foreach element is very powerful, and allows you to specify a collection, declare item and index variables that can be used inside the body of the element. 

It also allows you to specify opening and closing strings, and add a separator to place in between iterations. 

The element is smart in that it won’t accidentally append extra separators.

NOTE You can pass any Iterable object (for example List, Set, etc.), as well as any Map or Array object to foreach as collection parameter. When using an Iterable or Array, index will be the number of current iteration and value item will be the element retrieved in this iteration

When using a Map (or Collection of Map.Entry objects), index will be the key object and item will be the value object.

This wraps up the discussion regarding the XML configuration file and XML mapping files. 

The next section will discuss the Java API in detail, so that you can get the most out of the mappings that you’ve created.

bind

The bind element lets you create a variable out of an OGNL expression and bind it to the context. For example:

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

Multi-db vendor support

If a databaseIdProvider was configured a "_databaseId" variable is available for dynamic code, so you can build different statements depending on database vendor

Have a look at the following example:

<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>

Pluggable Scripting Languages For Dynamic SQL

Starting from version 3.2 MyBatis supports pluggable scripting languages, so you can plug a language driver and use that language to write your dynamic SQL queries.

You can plug a language by implementing the following interface:

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);
}

Once you have your custom language driver you can set it to be the default by configuring it in the mybatis-config.xml file:

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

Instead of changing the default, you can specify the language for an specific statement by adding the lang attribute as follows:

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

Or, in the case you are using mappers, using the @Lang annotation:

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

NOTE You can use Apache Velocity as your dynamic language. 

Have a look at the MyBatis-Velocity project for the details.

All the xml tags you have seen in the previous sections are provided by the default MyBatis language that is provided by the driverorg.apache.ibatis.scripting.xmltags.XmlLanguageDriver which is aliased as xml.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Mybatis动态SQL是指在Mybatis框架中,根据不同的条件动态生成SQL语句的技术。通过使用Mybatis提供的动态SQL标签,可以根据不同的条件生成不同的SQL语句,从而实现更加灵活的数据查询和操作。动态SQL标签包括if、choose、when、otherwise、foreach等,可以根据需要灵活组合使用,生成符合要求的SQL语句。 ### 回答2: MyBatis动态SQL是一个非常有用的功能,它可以使开发人员在运行期间修改或组合SQL语句。通过使用这种方法,开发人员可以在不同情况下生成不同的SQL语句,从而使应用程序更加灵活。它的主要优点是减少代码重复,提高代码组织的可读性和代码的重用性。在一些需要在运行时动态生成SQL的情况下,这种方式显得尤为重要。 MyBatis动态SQL可以用在许多场景中,例如根据条件查询不同的结果集、动态生成表名等。MyBatis动态SQL的实现,可以借助属性检查器和OGNL表达式编辑器,还可以选用标签库中的if、where、foreach、choose等标签实现。在实际开发中,一些情况下需要动态构建SQL,比如: 1. 动态构建查询条件。通过使用where标签,可以根据输入参数的不同来拼接查询语句。 2. 动态构建排序条件。通过使用orderBy标签,可以根据不同的排序条件来动态构建排序语句。 3. 动态构建更新语句。通过使用set标签,可以根据不同的更新操作来动态构建更新语句。 4. 动态构建插入语句。通过使用foreach标签,可以根据不同的输入参数来动态构建插入语句。 5. 动态构建删除语句。通过使用if标签,可以根据不同的条件来动态构建删除语句。 总而言之,MyBatis动态SQL是一种强大的功能,可以提高我们代码的复用性和可维护性。它可以帮助我们快速构建灵活的SQL语句,并且能够适应不同的业务需求。因此,在编写MyBatis应用程序时,我们应该充分发挥这种功能的优势,提高我们的开发效率和代码质量。 ### 回答3: MyBatis动态SQL是通过MyBatis提供的特殊语法编写灵活且可重用的SQL语句。它利用了MyBatis强大的type handler机制和基于OGNL的表达式语言,可以方便地实现各种业务需求。 动态SQL允许我们基于不同的条件和参数来生成动态的SQL语句,从而实现灵活的查询和操作,常见的动态SQL语句包括IF、WHERE、TRIM、CHOOSE等。其中,IF语句可以根据传入的参数来判断是否包含某一部分SQL语句,WHERE语句可以构建WHERE子句,并自动处理AND和OR的逻辑关系,TRIM语句可以动态去除SQL语句中的空格和逗号,CHOOSE语句可以根据传入的参数选择不同的查询语句。 除了基本的动态SQL语句外,MyBatis还提供了foreach语句,它可以帮助我们循环处理集合类型的参数,并自动生成对应数量的占位符和参数列表。此外,MyBatis还可以通过继承BaseMapper接口并使用@SqlProvider注解自动生成CRUD操作的SQL语句,从而避免手动编写大量相似的SQL语句,并提高代码的可维护性和重用性。 总之,MyBatis动态SQL是一种强大的语法,可以方便地生成灵活、可重用的SQL语句,避免了大量重复劳动和错误。如果你掌握了MyBatis动态SQL的使用,相信会极大地提高你的开发效率和代码质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值