Mybatis学习总结之mybatis使用建议
简介:什么是mybatis?
(前身为ibatis) mybatis 是一个可以自定义sql、存储过程和高级映射的持久层框架。mybatis消除了几乎所有的jdbc代码和参数的手工设置以及对结果集的检索。mybatis可以使用简单的xml或注解用于配置和原始映射,将接口和java的pojo(plain old java objects,普通的java对象)映射成数据库中的记录。
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:
<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> < <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">choose</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)">"startdate != null and startdate != '' and enddate != null and endate != ''"</span>> <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} </ <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">if</span>> <otherwise> <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> </otherwise></ <span class="hljs-keyword" style="padding:0px; margin:0px; color:rgb(133,153,0)">choose</span> ></span>
这样的if判断,其实是完全没有必要的,我们可以很简单的采用decode来解决默认值问题:
<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这种方式生成分页信息,如果你的脚本中最后一行出现了注释,则添加的部分会成为注释的一部分,执行就会报错。除此之外,某些情况下也可能导致部分条件被忽略,如下面的情况:
<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学习总结之mybatis使用建议,希望对大家有所帮助
上一篇: Web Service 运行原理详细介绍
下一篇: PHP中TP5 上传文件的实例详解
推荐阅读
-
Mybatis学习总结之mybatis使用建议
-
MyBatis学习教程(二)—如何使用MyBatis对users表执行CRUD操作
-
Spring+SpringMVC+MyBatis深入学习及搭建(一)之MyBatis的基础知识
-
框架学习总结-----MyBatis--- 映射文件配置(简述) 博客分类: 项目经验总结 MyBatis
-
框架学习总结-----MyBatis---核心配置文件 博客分类: 项目经验总结 框架 MyBatis
-
框架学习总结-----MyBatis---核心配置文件 博客分类: 项目经验总结 框架 MyBatis
-
系统学习JavaEE的ORM框架之Mybatis入门案例
-
MyBatis入门初体验之使用大全(2)
-
MyBatis入门初体验之使用大全(2)
-
MyBatis学习教程(二)—如何使用MyBatis对users表执行CRUD操作