使用 Mybatis 真心不要偷懒!
本文首发个人公众号《andyqian》,期待你的关注!
前言
这篇文章非常简单,没有什么高深技术。这些细节用过Mybatis的童鞋都知道。写这篇文章的缘由是:在最近的工作中,接手了一个外包项目,发现项目中 mapper 文件全部是自动生成的,代码十分冗余且不易维护,用知乎上的回答,这算得上是名副其实的”屎山”代码了。现在用 Mybatis做持久层算是Java系的主流,其中有一个主要的原因就是灵活性,但通过代码生成工具恰恰打破它的灵活性,生成一堆冗余且无用的代码,方法的命名也十分奇怪,总之,简直就是灾难。前者一时爽,后者呜呼哀哉,特别是上线后,那就更可怕了,到底改还是不改呢?不改强迫症,看着就不舒服,一改就要花费成倍的时间,仿佛是生产事故在像你招手。so,本着程序员本是同根生的原则,真心建议大家不要使用生成工具。前期的一点点时间的节省,后期真的就需要成倍的时间。
自动生成代码的若干宗罪:
-
SQL怎么优化?
-
命名不规范如何修改?
-
冗余代码怎么办?常常是为了一个代码,生成一堆代码。
-
参数可理解性?
…
传参方式
1. 使用 _parameter 关键字
接口:
List<Address> queryAddressBySimplte(String address,String coinType);
XML文件:
<sql id="querySQL"> select oid, party_id, coin_type, address,address_alias,address_type, wallet_id, user_id, status, register_time, created_time, update_time from t_molecule_address </sql> <select id="queryAddressBySimplte" resultType="Address"> <include refid="querySQL"/> where <if test="_parameter!=null and _parameter!=''"> address=#{0} </if> <if test="_parameter!=null and _parameter!=''"> and coin_type=#{1} </if> </select>
其中_parameter 参数表示参数对象,其内部数据结构为:MapperParamMap 。按照index进行参数值的获取。
注意事项:
-
当方法是单个参数时,可以直接使用 #{_parameter} 进行获取。
-
当方法有多个参数时,则不能直接使用 #{_parameter} 方式,必须通过Index的形式进行获取,#{0} 表示第一个参数,#{1} 表示第二个参数。
-
当方法为多个参数时使用 #{_parameter} 时。日志显示如下所示:
DEBUG main - ==> Preparing: select oid, party_id, coin_type, address,address_alias,address_type, wallet_id, user_id, status, register_time, created_time, update_time from t_molecule_address where address=? and coin_type=? 2019-05-07 13:24:31 DEBUG main - ==> Parameters: {0=0xbfa8f58ebea6e0643a5370c555a5bacfe320fd72, 1=ETH, param1=0xbfa8f58ebea6e0643a5370c555a5bacfe320fd72, param2=ETH}(MapperParamMap)
优点:
-
暂时没想出来啥优点。(知道的可以在评论区留言补充)。
缺点:
-
多个参数时,只能通过角标的形式引入,可读性,以及可理解性比较差。
-
传参时,不同参数数量其规则不同,容易出错。
代码生成工具使用的比较多,我相信很少有人主动用这种方式。
2. 使用@Param 注解
接口:
List<Address> queryAddressBySimplte(@Param("address")String address,@Param("coinType") String coinType);
XML文件
<sql id="querySQL"> select oid, party_id, coin_type, address,address_alias,address_type, wallet_id, user_id, status, register_time, created_time, update_time from t_molecule_address </sql> <select id="queryAddressBySimplte" resultType="Address"> <include refid="querySQL"/> where <if test="_parameter!=null and _parameter!=''"> address=#{address} </if> <if test="_parameter!=null and _parameter!=''"> and coin_type=#{coinType} </if> </select>
注意事项:
-
在XML文件中的参数名与@Param()中的参数名一致,与方法参数名无关。
例如:
List<Address> queryAddressBySimplte(@Param("addressAlias")String address,@Param("coinTypeAlias") String coinType);
这时我们在XML文件中,对应的参数应该为:
<select id="queryAddressBySimplte" resultType="Address"> <include refid="querySQL"/> where <if test="_parameter!=null and _parameter!=''"> address=#{addressAlias} </if> <if test="_parameter!=null and _parameter!=''"> and coin_type=#{coinTypeAlias} </if> </select>
-
使用注解时,我们还可以 params 方式。
例如:
<select id="queryAddressBySimplte" resultType="Address"> <include refid="querySQL"/> where <if test="param1!=null and param1!=''"> address=#{param1} </if> <if test="param2!=null and param2!=''"> and coin_type=#{param2} </if> </select>
其中 param1 表示第一个参数,param2 表示第二个参数。当然了,不推荐大家这么用,因为对可读性以及可理解性都不友好。
优点:
-
代码可读性好,参数自可读。
-
可重命名参数名。
缺点:
-
需要引入@Param 注解 (当然,我不认为这是缺点)。
-
可扩展性较弱。(超过3个参数的方法,就不建议使用该方法了)。
3. 使用 Java 对象
Domain对象(省略Get / Set 方法):
public class Address { /** * 币种类型 */ private String coinType; /** * 地址 */ private String address; ...
接口:
List<Address> queryAddressBySimplte(Address address);
XML文件:
<select id="queryAddressBySimplte" resultType="Address" parameterType="Address"> <include refid="querySQL"/> where <if test="address!=null and address!=''"> address=#{address} </if> <if test="coinType!=null and coinType!=''"> and coin_type=#{coinType} </if> </select>
其中参数为 Address 对象中的属性即可。如果参数为非对象属性中的参数,即会显示以下异常:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'coinType11' in 'class io.invault.molecule.dal.domain.address.Address'
优点:
-
可扩展性好。
-
参数名与对象列明一致,理解性好。
缺点:
-
需要另外引入一个Java实体对象。(其实这是与可扩展性矛盾的地方)。
小结
通过上述几种传参方式,我们已经非常清楚每一种方式的应用场景以及优缺点。如果要说最佳的方式,还得回归到Java规范上来。在Java手册中,有这么一条,建议超过3个参数及以上方法,改用参数对象传递。在上述例子中,我们同样遵守这样的规范是极好的。总之,我们应该明白:代码是给人读的,而不是计算机!。
相关阅读:
《重构不完全指南!》

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
jmx远程连接阿里云服务器的问题
最近在学习jvm监控,想尝试连接阿里云的jvm时出现一个问题:无法使用 service:jmx:rmi... 因为是springboot项目,启动的时候使用java -Djava.rmi.server.hostname=xx.xx.xx.xx -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8061 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar xxx.jar 用jvisualvm连接8061端口的时候问题报这个问题:无法使用 service:jmx:rmi 从网上找到解决办法:关闭防火墙。但是阿里云的防火墙是默认关闭的啊…… 原因是:除了JMX server指定的监听端口号外,JMXserver还会监听一到两个随机端口号,可以通过命grep 来查看当前java进程需要监听的随机端口号 于是用netstat -ntpl查看,发现与8061端口相同的PID下,...
- 下一篇
经典设计模式之策略模式【如何重构聚合支付平台,对接【支付宝,微信,银联支付】】
写在前面:设计模式源于生活,而又高于生活! 为什么要使用设计模式重构代码 使用设计模式可以重构整体架构代码、提高代码复用性、扩展性、减少代码冗余问题。 Java高级工程师装逼的技能! 什么是策略模式 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。 1.环境(Context)角色:持有一个Strategy的引用 2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。 3.具体策略(ContextStrategy)角色:包装了相关的算法或行为。 策略模式应用场景 比如搭建聚合支付平台的时候,这时候需要对接很多第三方支付接口,比如支付宝、微信支付、银联支付等。通过传统if代码判断的,后期的维护性非常差! public String toPayHtml2(String payCode){ if(payCode.equals("ali_pay")){ return "调用支付宝接口..."; } if(payCode.equals("uni...
相关文章
文章评论
共有0条评论来说两句吧...