使用 Mybatis 真心不要偷懶!

本文首發於個人公衆號《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

重構不完全指南!


2019011016001267.jpeg



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章