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:

SELECT * FROM T_NEWS_TEXT WHERE 1 = 1
< choose>
< if test ="startdate != null and startdate != '' and enddate != null and endate != ''">
AND PUBLISHTIME >= #{startdate} AND PUBLISHTIME <= #{enddate}
</ if>
<otherwise>
AND PUBLISHTIME >= SYSDATE - 7 AND PUBLISHTIME <= SYSDATE
</otherwise></ choose >

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

SELECT * FROM T_NEWS_TEXT WHERE PUBLISHTIME >= DECODE(#{startdate},NULL,SYSDATE-7, #{startdate}) AND PUBLISHTIME <= DECODE(#{enddate},NULL,SYSDATE,#{enddate})

当然有人会想,引入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这种方式生成分页信息,如果你的脚本中最后一行出现了注释,则添加的部分会成为注释的一部分,执行就会报错。除此之外,某些情况下也可能导致部分条件被忽略,如下面的情况:

SELECT * FROM TEST WHERE COL1 >  1 -- 这里是注释<if test="a != null and a != ''">AND COL2 = #{a}</if>

即使传入的参数中存在对应的参数,实际也不会产生效果,因为后面的内容实际上是被完全注释了。这种错误,如果不经过严格的测试,是很难发现的。一般情况下,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缓存(转载)

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

  2. 转:MyBatis学习总结(Mybatis总结精华文章)

    http://www.cnblogs.com/xdp-gacl/tag/MyBatis%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/ 当前标签: MyBatis学习总结   ...

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

    [转]MyBatis学习总结(七)——Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualC ...

  4. 【转】MyBatis学习总结(一)——MyBatis快速入门

    [转]MyBatis学习总结(一)——MyBatis快速入门 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC ...

  5. Mybatis学习笔记16 - bind标签

    1.${}拼串进行模糊查询,不安全 示例代码: 接口定义: package com.mybatis.dao; import com.mybatis.bean.Employee; import java ...

  6. MyBatis 学习记录5 MyBatis的二级缓存

    主题 之前学习了一下MyBatis的一级缓存,主要涉及到BaseExecutor这个类. 现在准备学习记录下MyBatis二级缓存. 配置二级缓存与初始化发生的事情 首先二级缓存默认是不开启的,需要自 ...

  7. Mybatis学习笔记(一) —— mybatis介绍

    一.Mybatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名 ...

  8. MyBatis学习笔记一:MyBatis最简单的环境搭建

    MyBatis的最简单环境的搭建,使用xml配置,用来理解后面的复杂配置做基础 1.环境目录树(导入mybatis-3.4.1.jar包即可,这里是为后面的环境最准备使用了web项目,如果只是做 my ...

  9. Mybatis学习第一天——Mybatis的安装配置以及基本CURD操作

    1.Mybatis下载 Mybatis是开源的持久层框架,能够度jdbc进行简单的封装,但其并不是完全的ORM(Object Relational Mapping,对象关系映射),无法脱离数据库进行适 ...

  10. Mybatis学习笔记(八) —— Mybatis整合spring

    一.整合思路 1.SqlSessionFactory对象应该放到spring容器中作为单例存在. 2.传统dao的开发方式中,应该从spring容器中获得sqlsession对象. 3.Mapper代 ...

随机推荐

  1. 洛谷1462 通往奥格瑞玛的道路 最短路&&二分

    SPFA和二分的使用 跑一下最短路看看能不能回到奥格瑞玛,二分收费最多的点 #include<iostream> #include<cstdio> #include<cs ...

  2. POJ-2142 The Balance 扩展欧几里德(+绝对值和最小化)

    题目链接:https://cn.vjudge.net/problem/POJ-2142 题意 自己看题吧,懒得解释 思路 第一部分就是扩展欧几里德 接下来是根据 $ x=x_0+kb', y=y_0- ...

  3. 题解 P1531 【I Hate It】

    这道题明明是裸的线段树,蒟蒻却80分了五六次... ------------ 根据题意,显然是维护一棵单点修改区间查询的线段树,于是直接套区间修改的代码... 结构体,即为树上的节点. struct ...

  4. java 究竟老年代和年轻代的比例为多大合适呢?

    眼下我还没有这方面过多的经验,和切身体会 只是以我眼下的水平看来,年轻代不宜大,假设年轻代大会导致转为老年代的时候,老年代撑不下.导致full gc.回收停顿时间过长

  5. OpenCV学习笔记09--通过cvPtr2D或指针算法绘制图形

    练习:创建一个1000*1000的三通道图像,将其元素所有置0.以(200,50)和(400,200)为顶点绘制一个绿色平面 我们能够用两种方法来实现这一功能,一个是使用cvPtr2D,可是因为使用了 ...

  6. poj3169 差分约束系统

    题意: 从1到n,n个数,从左向右依次排列. 给定两种形式的约束条件: 1.xi与yi的最大距离为dk 2.xi与yi的最小距离为dk 问满足这些限定条件的情况下,数1和n的最大距离是多少?(若约束条 ...

  7. ubuntu 下安装eclipse &amp;java环境配置

    前面有一篇的博客写的是ubuntu下安装eclipse和java环境的配置.当时是安装网上的攻略进行的 ,当然也是能够成功的. 近期把那台电脑送人了 ,仅仅好在自己的这台电脑上又一次安装一次了 ,唯一 ...

  8. javascript 提交弹窗

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. tensorflow学习之路---简单的代码

    import numpyimport tensorflow as tf #自己创建的数据x_data = numpy.random.rand(100).astype(numpy.float32)#创建 ...

  10. Nginx安装与升级(包括虚拟主机)

    Nginx WEB服务器最主要就是各种模块的工作,模块从结构上分为核心模块.基础模块和第三方模块,其中三类模块分别如下: 核心模块:HTTP模块.EVENT模块和MAIL模块等: 基础模块:HTTP ...