文档章节

使用 Mybatis 真心不要偷懒!

andyqian
 andyqian
发布于 2019/05/07 21:53
字数 1432
阅读 1.9K
收藏 19

本文首发个人公众号《andyqian》,期待你的关注!

前言

  这篇文章非常简单,没有什么高深技术。这些细节用过Mybatis的童鞋都知道。写这篇文章的缘由是:在最近的工作中,接手了一个外包项目,发现项目中 mapper 文件全部是自动生成的,代码十分冗余且不易维护,用知乎上的回答,这算得上是名副其实的”屎山”代码了。现在用 Mybatis做持久层算是Java系的主流,其中有一个主要的原因就是灵活性,但通过代码生成工具恰恰打破它的灵活性,生成一堆冗余且无用的代码,方法的命名也十分奇怪,总之,简直就是灾难。前者一时爽,后者呜呼哀哉,特别是上线后,那就更可怕了,到底改还是不改呢?不改强迫症,看着就不舒服,一改就要花费成倍的时间,仿佛是生产事故在像你招手。so,本着程序员本是同根生的原则,真心建议大家不要使用生成工具。前期的一点点时间的节省,后期真的就需要成倍的时间。

自动生成代码的若干宗罪

  1. SQL怎么优化?

  2. 命名不规范如何修改?

  3. 冗余代码怎么办?常常是为了一个代码,生成一堆代码。

  4. 参数可理解性?

传参方式

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)

优点

  1. 暂时没想出来啥优点。(知道的可以在评论区留言补充)。

     

缺点

  1. 多个参数时,只能通过角标的形式引入,可读性,以及可理解性比较差。

  2. 传参时,不同参数数量其规则不同,容易出错。

     

代码生成工具使用的比较多,我相信很少有人主动用这种方式

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 表示第二个参数。当然了,不推荐大家这么用,因为对可读性以及可理解性都不友好。

优点:  

  1. 代码可读性好,参数自可读。

  2. 可重命名参数名。

     

缺点:

  1. 需要引入@Param 注解  (当然,我不认为这是缺点)。

  2. 可扩展性较弱。(超过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'

优点:

  1. 可扩展性好。

  2. 参数名与对象列明一致,理解性好。

     

缺点:

  1. 需要另外引入一个Java实体对象。(其实这是与可扩展性矛盾的地方)。

小结

  通过上述几种传参方式,我们已经非常清楚每一种方式的应用场景以及优缺点。如果要说最佳的方式,还得回归到Java规范上来。在Java手册中,有这么一条,建议超过3个参数及以上方法,改用参数对象传递。在上述例子中,我们同样遵守这样的规范是极好的。总之,我们应该明白:代码是给人读的,而不是计算机!


 

相关阅读:

再谈Java 生产神器 BTrace

Java 生产神器  BTrace

Java 基本功 之 CAS

重构不完全指南!

 

© 著作权归作者所有

andyqian
粉丝 71
博文 56
码字总数 74748
作品 0
长沙
程序员
私信 提问
加载中

评论(8)

请叫我七点起床
请叫我七点起床
需要注明一下@Param 注解,只限于mybatis用接口方式的调用,如果项目使用的sqlSession方式调用,这个注解是无效的。因为基于接口方式的调用,mybatis会根据参数的个数以及是否使用该注解(代码参考convertArgsToSqlCommandParam(Object[] args)方法)给我们自动封装到一个map中,而第二种方式,需要我们自己将参数封装。我们自己的项目就是用的第二种方式。
f
freezingsky
自从用了JPA,已经不愿意再去写sql了. 除非没办法 .
gitOpen_1
gitOpen_1
JPA
没有感情的编码机器
没有感情的编码机器
可以考虑用下 mybatis-plus
andyqian
andyqian 博主

引用来自“魔法王者安琪拉”的评论

这生成代码的方式太粗糙了,优化下,根据传递的参数生成,妥妥的
方法参数在少于3个时,用@Param注解是极好的,超过三个用对象了。不过最重要的是灵活且符合规范
魔法王者安琪拉
魔法王者安琪拉
这生成代码的方式太粗糙了,优化下,根据传递的参数生成,妥妥的
andyqian
andyqian 博主

引用来自“阳少”的评论

自己写一个代码生成完事
能解决上弊端,符合代码规范确实可以考虑。不过从目前已有的来看,大多比较鸡肋了,不推荐使用了!
阳少
阳少
自己写一个代码生成完事
ThreadPoolExecutor 原理解析

本文首发于个人微信公众号《andyqian》,期待你的关注 前言 在上一篇文章《Java线程池ThreadPoolExecutor》中描述了ThreadPoolExecutor的基本概念,以及一些常用方法。这对于我们来说,是远远...

andyqian
2019/06/14
58
0
Spring3和MyBatis3整合时一个诡异的问题,困扰两天了。

这两天一直在搞SpringMVC、Spring3,MyBatis整合,在过程中,启动Tomcat7时有一个问题一直出现, 软件环境:Spring 3.1.1、MyBatis 3.1.1、MyBatis-Spring 1.1.1 、Maven构建 Spring 相关配置...

Alicus
2013/01/13
4.3W
8
mybatis 关于总配置里的typeHandler类型句柄

今天把mybatis3.0学习手册看了下 之前用ibatis做过项目 觉得mybatis也难不倒哪 事实的确如此 只是mybatis在DAO层的封装 我挺欣赏。可是问题出来了 想问下 mybatis 关于总配置里的typeHandler...

冷-岩
2012/03/30
1.9K
2
Spring boot Mybatis 整合(完整版)

个人开源项目 springboot+mybatis+thymeleaf+docker构建的个人站点开源项目(集成了个人主页、个人作品、个人博客) 朋友自制的springboot接口文档组件swagger2 更多干货 SpringBoot系列目录...

舒运
2018/07/09
448
0
Java ORM 框架 - spring-db-template

偷懒必备的ORM框架,大幅度提高开发效率,减少编码量 功能简介 1. 极·简化数据库操作,大幅度提高编码效率,此框架核心价值所在,上手难度非常低; 例-分页查询: PageInfo<Demo> page = DemoT...

梦旅诗乐
2019/10/28
768
0

没有更多内容

加载失败,请刷新页面

加载更多

Mina 粘包、拆包的实现-网上常见的代码有bug

mina的粘包拆包其实是蛮简单的,只是一开始没搞清楚原理。 我们要约定数据包的格式,我这里的是(4个字节长度+json的string字符串) 1:写一个 ProtocolCodecFactory类,用来拦截数据包处理 ...

whoisliang
15分钟前
59
0
编写重试逻辑的最简洁方法?

有时我需要在放弃之前重试几次操作。 我的代码是这样的: int retries = 3;while(true) { try { DoSomething(); break; // success! } catch { if(--retries == 0) throw;...

技术盛宴
25分钟前
40
0
LeetCode: 7. 整数反转

思路:此题可以用python列表的reverse来进行 1.判断数值是否在范转之内 2.数值>0,直接转成列表;数值<0,正整数部分转成列表,并记录负号 3.列表反转并生成新的值,需要考虑负值部分 class ...

tedzheng
今天
55
0
使用postman测试接口,解决Session共享问题

问题: 在做登录模块时,使用Postman做接口测试,发现session不能共享问题:第一次请求将系统随机生成验证码放入Session中,第二次请求想要获取系统生成的验证码,但是取到的值为null,因此无...

code-ortaerc
今天
67
0
CentOS 7 SSH连接超时自动断开解决方案

用SSH登录到Linux的时候,由于默认的连接超时时间很短,经常断开。可以修改配置文件调整服务器端向客户端请求消息的时间间隔,解决自动断开的问题。 编辑/etc/ssh/sshd_config 找到 #ClientA...

matrixchan
今天
69
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部