本文首發於個人公衆號《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個參數及以上方法,改用參數對象傳遞。在上述例子中,我們同樣遵守這樣的規範是極好的。總之,我們應該明白:代碼是給人讀的,而不是計算機!。
相關閱讀:
《重構不完全指南!》