1.Mapper层参数为Map,由Service层负责重载。

Mapper由于机制的问题,不能重载,参数一般设置成Map,但这样会使参数变得模糊,如果想要使代码变得清晰,可以通过service层来实现重载的目的,对外提供的Service层是重载的,但这些重载的Service方法其实是调同一个Mapper,只不过相应的参数并不一致。

也许有人会想,为什么不在Service层也设置成Map呢?我个人是不推荐这么做的,虽然为了方便,我在之前的项目中也大量采用了这种方式,但很明显会给日后的维护工作带来麻烦。因为这么做会使你整个MVC都依赖于Map模型,这个模型其实是很不错的,方便搭框架,但存在一个问题:仅仅看方法签名,你不清楚Map中所拥有的参数个数、类型、每个参数代表的含义。

试想,你只对Service层变更,或者DAO层变更,你需要清楚整个流程中Map传递过来的参数,除非你注释或者文档良好,否则必须把每一层的代码都了解清楚,你才知道传递了哪些参数。针对于简单MVC,那倒也还好,但如果层次复杂之后,代码会变得异常复杂,而且如果我增加一个参数,需要把每一个层的注释都添加上。相对于注释,使用方法签名来保证这种代码可控性会来得更可行一些,因为注释有可能是过时的,但方法签名一般不太可能是陈旧的。

2.尽量少用if choose等语句,降低维护的难度。

Mybatis的配置SQL时,尽量少用if choose 等标签,能用SQL实现判断的尽量用SQL来判断(CASE WHEN ,DECODE等),以便后期维护。否则,一旦SQL膨胀,超级恶心,如果需要调试Mybatis中的SQL,需要去除大量的判断语句,非常麻烦。另一方面,大量的if判断,会使生成的SQL中包含大量的空格,增加网络传输的时间,也不可取。

而且大量的if choose语句,不可避免地,每次生成的SQL会不太一致,会导致ORACLE大量的硬解析,也不可取。 
我们来看看这样的SQL:

  1. <code class="hljs sql" style="padding:0.5em; margin:0px; display:block; color:rgb(101,123,131); overflow-x:auto; background:rgb(253,246,227)"><span class="hljs-operator" style="padding:0px; margin:0px"><span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">SELECT</span> * <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">FROM</span> T_NEWS_TEXT <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">WHERE</span> <span class="hljs-number" style="padding:0px; margin:0px; color:rgb(42,161,152)">1</span> = <span class="hljs-number" style="padding:0px; margin:0px; color:rgb(42,161,152)">1</span>
  2. < <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">choose</span>>
  3. < <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">if</span> test =<span class="hljs-string" style="padding:0px; margin:0px; color:rgb(42,161,152)">"startdate != null and startdate != '' and enddate != null and endate != ''"</span>>
  4. <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">AND</span> PUBLISHTIME >= #{startdate} <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">AND</span> PUBLISHTIME <= #{enddate}
  5. </ <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">if</span>>
  6. <otherwise>
  7. <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">AND</span> PUBLISHTIME >= <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">SYSDATE</span> - <span class="hljs-number" style="padding:0px; margin:0px; color:rgb(42,161,152)">7</span> <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">AND</span> PUBLISHTIME <= <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">SYSDATE</span>
  8. </otherwise></ <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">choose</span> ></span></code>

这样的if判断,其实是完全没有必要的,我们可以很简单的采用DECODE来解决默认值问题:

  1. <code class="hljs sql" style="padding:0.5em; margin:0px; display:block; color:rgb(101,123,131); overflow-x:auto; background:rgb(253,246,227)"><span class="hljs-operator" style="padding:0px; margin:0px"><span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">SELECT</span> * <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">FROM</span> T_NEWS_TEXT <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">WHERE</span> PUBLISHTIME >= <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">DECODE</span>(#{startdate},<span class="hljs-literal" style="padding:0px; margin:0px">NULL</span>,<span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">SYSDATE</span>-<span class="hljs-number" style="padding:0px; margin:0px; color:rgb(42,161,152)">7</span>, #{startdate}) <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">AND</span> PUBLISHTIME <= <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">DECODE</span>(#{enddate},<span class="hljs-literal" style="padding:0px; margin:0px">NULL</span>,<span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">SYSDATE</span>,#{enddate})</span></code>

当然有人会想,引入CASE WHEN,DECODE会导致需要Oracle函数解析,会拖慢SQL执行时间,有兴趣的同学可以回去做一下测试,看看是否会有大的影响。就个人经验而言,在我的开发过程,没有发现因为函数解析导致SQL变慢的情形。影响SQL执行效率的一般情况下是JOIN、ORDER BY、DISTINCT、PARTITATION BY等这些操作,这些操作一般与表结构设计有很大的关联。相对于这些的效率影响程度,函数解析对于SQL执行速度影响应该是可以忽略不计的。

另外一点,对于一些默认值的赋值,像上面那条SQL,默认成当前日期什么的,其实可以完全提到Service层或Controller层做处理,在Mybatis中应该要少用这些判断。因为,这样的话,很难做缓存处理。如果startdate为空,在SQL上使用动态的SYSDATE,就无法确定缓存startdate日期的key应该是什么了。所以参数最好在传递至Mybatis之前都处理好,这样Mybatis层也能减少部分if choose语句,同时也方便做缓存处理。

当然不使用if choose也并不是绝对的,有时候为了优化SQL,不得不使用if来解决,比如说LIKE语句,当然一般不推荐使用LIKE,但如果存在使用的场景,尽可能在不需要使用时候去除LIKE,比如查询文章标题,以提高查询效率。 最好的方式是使用lucence等搜索引擎来解决这种全文索引的问题。

总的来说,if与choose判断分支是不可能完全去除的,但是推荐使用SQL原生的方式来解决一些动态问题,而不应该完全依赖Mybatis来完成动态分支的判断,因为判断分支过于复杂,而且难以维护。

3.用XML注释取代SQL注释。

Mybatis中原SQL的注释尽量不要保留,注释会引发一些问题,如果需要使用注释,可以在XML中用<!-- -->来注释,保证在生成的SQL中不会存在SQL注释,从而降低问题出现的可能性。这样做还有一个好处,就是在IDE中可以很清楚的区分注释与SQL。

现在来谈谈注释引发的问题,我做的一个项目中,分页组件是基于Mybatis的,它会在你写的SQL脚本外面再套一层SELECT COUNT(*) ROWNUM_ FROM (....) 计算总记录数,同时有另一个嵌套SELECT * FROM(...) WHERE ROWNUM > 10 AND RONNUM < 10 * 2这种方式生成分页信息,如果你的脚本中最后一行出现了注释,则添加的部分会成为注释的一部分,执行就会报错。除此之外,某些情况下也可能导致部分条件被忽略,如下面的情况:

  1. <code class="hljs vbnet" style="padding:0.5em; margin:0px; display:block; color:rgb(101,123,131); overflow-x:auto; background:rgb(253,246,227)"><span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">SELECT</span> * <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">FROM</span> TEST <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">WHERE</span> COL1 >  <span class="hljs-number" style="padding:0px; margin:0px; color:rgb(42,161,152)">1</span> -- 这里是注释<<span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">if</span> test=<span class="hljs-string" style="padding:0px; margin:0px; color:rgb(42,161,152)">"a != null and a != ''"</span>><span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">AND</span> COL2 = <span class="hljs-preprocessor" style="padding:0px; margin:0px; color:rgb(203,75,22)">#{a}</<span class="hljs-keyword" style="padding:0px; margin:0px">if</span>></span></code>

即使传入的参数中存在对应的参数,实际也不会产生效果,因为后面的内容实际上是被完全注释了。这种错误,如果不经过严格的测试,是很难发现的。一般情况下,XML注释完全可以替代SQL注释,因此这种行为应该可以禁止掉。

4.尽可能使用#{},而不是${}.

Mybatis中尽量不要使用${},尽量这样做很方便开发,但是有一个问题,就是大量使用会导致ORACLE的硬解析,拖慢数据库性能,运行越久,数据库性能会越差。对于一般多个字符串IN的处理,可以参考如下的解决方案:http://www.myexception.cn/sql/849573.html,基本可以解决大部分${}.

关于${},另一个误用的地方就是LIKE,我这边还有个案例:比如一些树型菜单,节点会设计成'01','0101',用两位节点来区分层级,这时候,如果需要查询01节点下所有的节点,最简单的SQL便是:SELECT * FROM TREE WHERE ID LIKE '01%',这种SQL其实无可厚非,因为它也能用到索引,所以不需要特别的处理,直接使用就行了。但如果是文章标题,则需要额外注意了:SELECT * FROM T_NEWS_TEXT WHERE TITLE LIKE '%OSC%',这是怎么也不会用到索引的,上面说了,最好采用全文检索。但如果离不开LIKE,就需要注意使用的方式: ID LIKE #{ID} || '%'而不是ID LIKE '${ID}%',减少硬解析的可能。

有人觉得使用||会增加ORACLE处理的时间,我觉得不要把ORACLE看得太傻,虽然有时候确实非常傻,有空可以再总结ORACLE傻不垃圾的地方,但是稍加测试便知:这种串联方式,对于整个SQL的解析执行,应该是微乎其微的。

当然还有一些特殊情况是没有办法处理的,比如说动态注入列名、表名等。对于这些情况,则比较棘手,没有找到比较方便的手段。由于这种情况出现的可能性会比较少,所以使用${}倒也不至于有什么太大的影响。当然你如果有代码洁癖的话,可以使用ORACLE的动态执行SQL的机制Execute immediate,这样就可以完全避免${}出现的可能性了。这样会引入比较复杂的模型,这个时候,你就需要取舍了。

针对于以上动态SQL所导致的问题,最激进的方式是全部采用存储过程,用数据库原生的方式来解决,方便开发调试,当然也会带来问题:对开发人员会有更高的要求、存储过程的管理等等,我这边项目没有采用过这种方式,这里不做更多的展开。

5.简单使用Mybatis。

Mybatis的功能相对而言还是比较弱的,缺少了好多必要的辅助库,字符串处理等等,扩展也比较困难,一般也就可能对返回值进行一些处理。因此最好仅仅把它作为单纯的SQL配置文件,以及简单的ORM框架。不要尝试在Mybatis中做过多的动态SQL,否则会导致后续的维护非常恶心。

MyBatis学习总结_16_Mybatis使用的几个建议的更多相关文章

  1. MyBatis学习总结(二)——使用MyBatis对表执行CRUD操作(转载)

    本文转载自:http://www.cnblogs.com/jpf-java/p/6013540.html 上一篇博文MyBatis学习总结(一)--MyBatis快速入门中我们讲了如何使用Mybati ...

  2. MyBatis学习总结(八)——Mybatis3.x与Spring4.x整合(转载)

      孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(八)--Mybatis3.x与Spring4.x整合 一.搭建开发环境 1.1.使用Maven创建Web项目 执行如下命令: m ...

  3. MyBatis学习总结(七)——Mybatis缓存(转载)

      孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(七)--Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的 ...

  4. (原创)mybatis学习二,spring和mybatis的融合

    mybatis学习一夯实基础 上文介绍了mybatis的相关知识,这一节主要来介绍mybaits和spring的融合 一,环境搭建 1,jar包下载,下载路径为jar包 2,将包导入到java工程中 ...

  5. (原创)mybatis学习一,夯实基础

    一,what?(是什么) MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可 ...

  6. MyBatis学习--简单的增删改查

    jdbc程序 在学习MyBatis的时候先简单了解下JDBC编程的方式,我们以一个简单的查询为例,使用JDBC编程,如下: Public static void main(String[] args) ...

  7. MyBatis学习总结(二)——使用MyBatis对表执行CRUD操作

    上一篇博文MyBatis学习总结(一)——MyBatis快速入门中我们讲了如何使用Mybatis查询users表中的数据,算是对MyBatis有一个初步的入门了,今天讲解一下如何使用MyBatis对u ...

  8. 【Todo】Mybatis学习-偏理论

    之前写过好几篇Mybatis相关的文章: http://www.cnblogs.com/charlesblc/p/5906431.html  <SSM(SpringMVC+Spring+Myba ...

  9. MyBatis学习系列三——结合Spring

    目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring MyBatis在项目中应用一般都要结合Spring,这一章主要把MyBat ...

随机推荐

  1. 小组开发项目针对性的NABC分析

    单独就我们团队开发项目——重力解锁的功能特点而言,我们解决了智能手机屏幕解锁的乏味和繁琐的特点,显得更有趣味性和独特性,更符合现代人追随时尚的潮流:我们根据个人的不同喜好和便利性来设定一些动作,利用重 ...

  2. SQL Server 中使用 convert 转换 datetime 格式示例

    Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AMSelect CONVERT(varchar(100), GETDATE() ...

  3. DiskFileItemFactory类---分析及运用

    DiskFileItemFactory类 将请求消息实体中的每一个项目封装成单独的DiskFileItem (FileItem接口的实现) 对象的任务由 org.apache.commons.file ...

  4. SQLServer BCP 命令的使用

    现在有一个包含数据的文件,每个字段用“|”分隔,现在要把这些数据导入到数据库的表中. 数据文件如下: R001|20150710 可以使用如下命令: bcp testDB.dbo.testTable ...

  5. shell编程之sleep的运用

    #!/bin/bashecho -n "Count:"tput sccount=0;while true;doif [ $count -lt 40 ]then let count+ ...

  6. 【ContestHunter】【弱省胡策】【Round3】(C)

    容斥原理+Fib Orz HE的神犇们 蒟蒻只能改出来第三题……实在太弱 官方题解:http://pan.baidu.com/s/1o6MdtQq fib的神奇性质……还有解密a[i]的过程……这里就 ...

  7. 剑指offer--面试题21

    题目:设计包含min函数的栈,pop(),push(),min()的时间复杂度均为O(1) 自己所写代码如下:(写‘栈’的代码还是有些不熟练!) #include <iostream> u ...

  8. NYOJ-21 三个水杯 AC 分类: NYOJ 2014-02-08 11:35 174人阅读 评论(0) 收藏

    人生中第一个AC的广搜题目,喵呜,C++的STL果真不错, #include<stdio.h> #include<queue> #include<string.h> ...

  9. 引擎设计跟踪(九.14.2i) Android GLES 3.0 完善

    最近把渲染设备对应的GLES的API填上了. 主要有IRenderDevice/IShader/ITexture/IGraphicsResourceManager/IIndexBuffer/IVert ...

  10. [工作记录] Android OpenGL ES: non-square texture - continue

    previous: [工作记录] Android OpenGL ES 2.0: square texture not supported on some device recently I found ...