设为首页 收藏本站
查看: 766|回复: 0

[经验分享] Mybatis使用:Dynamic Sql

[复制链接]

尚未签到

发表于 2016-11-25 09:07:29 | 显示全部楼层 |阅读模式
Mybatis使用:Dynamic Sql

 

本节我们学习下Mybatis最强大的特性之一。
如果你使用过JDBC来拼接SQL语句,后者有其它简单ORM框架使用经验的话,你会明白根据根据条件拼接SQL是多么痛苦,少个括号,逗号,空格都是一种灾难。你需要仔细的测试你的SQL生成的各种结果来确保SQL拼接覆盖到了各种情况。这种情况特别影响心情,还有可能将隐患带到线上。如果出现这种情况的话,你听到bug的第一印象就是动态拼接SQL的问题,抓狂的感觉可想而知啦。
而使用Mybatis的Dynamic SQL能够让你处理、远离这种痛苦。
好吧,上面这段类似宣传的说明就是说明一件事:使用mybatis吧,Dynamic SQL特性能帮助你解决条件SQL拼接的问题。当然,前提是建立在你能熟悉、熟练Mybatis的Dynamic SQL特性的基础之上。
 
说了这么多,Dynamic SQL究竟能有多强大呢,我们通过例程来看下。
注意本文的Dynamic SQL我以前用过,现在回头看下特性差别不大,就不在单独写SQL验证,以官方的SQL教程为准,会加上一些自己的理解和使用场景,还有一些教程不太容易理解的地方。官方的Dynamic SQL地址见这里:http://www.mybatis.org/core/dynamic-sql.html
 
Dynamic SQL支持下面四种条件SQL:

if
choose (when, otherwise)
trim (where, set)
foreach
我们分别来学习下这四种条件SQL的使用。
if
这个SQL是在Dynamic SQL里面使用最多,也是最为重要的一个,使用方法倒不难,看下if条件在where条件语句中的情况:
 

<select id="findActiveBlogWithTitleLike" parameterType="Blog" resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
 这个SQL提供基本的查询功能state='active',另外提供一个可选的条件title:如果你传了title这个参数,就会添加上title这个条件。
 
如果想选择多个条件的话,if语句时可以叠加使用的:

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

 
如果你添加的调试时Blog实体类中的对象的条件,如下面的author,判断author.name是否为空:

<select id="findActiveBlogLike" parameterType="Blog" 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 where other
在有些情况下,你不需要所有的条件,需要在多个条件中进行选择,如Java中的Switch或者if...else...判断:

<select id="findActiveBlogLike" parameterType="Blog" 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>

 根据test的结果添加条件,如果条件都不满足,则设定默认的条件,这个功能还是比较实用的,关键是思路清晰,易于理解。
 
trim, where, set
如果where后面的条件都是可选项怎么办?这三个参数就是解决这个问题的。这几个测试条件都比较特殊,需要仔细阅读体会。

<select id="findActiveBlogLike" parameterType="Blog" 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>

 比较容易理解,有三个判断条件,根据不同的判断条件进行不同的查询;但是很容易发现问题,入股偶这些条件都没有匹配时SQL会变成这样:
 

SELECT * FROM BLOG
WHERE

 这个明显是个Error1064语法错误,如果仅仅匹配第二个条件的话,SQL回事这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

 这个也是个语法错误。对于where后面全部是条件判断的话,就不能用单纯的if语句了。
 
Mybatis的处理时将where也作为条件,这个是Dynamic SQL新添加的功能:

<select id="findActiveBlogLike" parameterType="Blog" 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元素能根据被包含的判断,做出是否插入的判断;并且能够判断出AND和OR开头的内容。
 
在官方文档中还有一句话,如果上面的where没有按照期望工作的话,使用trim元素来自定义。这句话比较奇怪,莫非是官方不相信这个功能的准确性。

<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">
SELECT * FROM BLOG
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<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>
</trim>
</select>

 prefixOverrides属性会根据前缀覆盖内容,当然有前缀覆盖,也一定有后缀覆盖。
 
在使用动态更新时,使用类似的解决方案是set,set元素可以更新需要更新的列,而不需要全部更新;这个能让我们的SQL自行判断是否需要更新,如果你不设定值得花,SQL会忽略掉更新。这个会避免我们会将null值更新到数据库去,在很多情况下这个是不允许的。

<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author">
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>

 更新数据时,只要设定需要更新的部分即可,不用全部重新设定。
 
相对应的trim元素可以写成这样:

<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author">
update Author
<trim prefix="SET",suffixOverrides=",">
<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>
</trim>
where id=#{id}
</update>

 trim会覆盖掉后缀的逗号,使得SQL能够正常提交。
 
foreach
最后一个Dynamic SQL的通用操作是迭代,迭代集合还是很常见的,这通常在构建IN条件中出现,如:

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

 foreach操作非常强大;它能够根据你指定的集合,将其迭代应用在集合元素内。foreach能够指定开放和关闭的标记,并且能够指定迭代之间的分隔符,这个对于获得数据库指定多个状态的数据时很有效的。
 
bind
在前面我们看到过like的例子,在哪个例子中,你需要自行添加好匹配符之后,在传给SQL。如果你只想传入Bill,来获得author.name中包含有Bill的作家名称,你需要使用bind操作OGNL表达式:

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

 你只用传入Bill,就可以查询title中包含有Bill的title;而在前面中,你需要将title设定为'%Bill%',这个操作才是真正的like含义。
 
最后说下Dynamic SQL的插件式的脚本特性,这个需要最新mybatis 3.2的支持。
mybatis支持两种内置语言,xml和raw;上面讨论的都是xml,能够支持上面提到的所有元素。
但是raw语言的话就不会支持全部;当你使用raw格式来设定SQL时,Mybatis仅仅做参数替换后就将替换参数后的SQL提交给数据库。如你所见,对于Mybatis的Dynamic SQL特性来说,raw就是一种倒退。但是raw依然存在,因为它比xml格式要快。
对于raw的使用,实际上在开发者,如果你的SQL本身就比价简单,就不用经过一系列动态SQL解析,直接提交给数据库即可,下面我们看下例程:

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

 
如果使用Annotation的话,可以这么使用:

public interface Mapper {
@Lang(RawLanguageDriver.class)
@Select("SELECT * FROM BLOG")
List<Blog> selectBlog();
}
 
如果想创建自己的LanguageDriver的话,你需要实现LanguageDriver接口:

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

 
如果你确认自己的SQL仅作参数替换,而不需要Dynamic特性的话,就可以设定自己的SQL为raw,提升性能。
 
Dynamic SQL的特性就这么多,如果理解深刻、使用熟练的话,那在工作中是如虎添翼,不用考虑SQL拼接了。这个对于开发者来说,能够更好的专注于业务逻辑的开发,提升工作效率。 
本文就到这里。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-305268-1-1.html 上篇帖子: Mybatis 插入并返回主键 下篇帖子: MyBatis运用心得(3)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表