使用mybatis的一些總結

前言

這段時間項目中需要寫一些api,我負責其中的幾個,也是第一次真正意義上的使用mybatis,所以我認爲有必要作一個總結來反映我學習的成果。


首先我們應該都知道,mybatis的sql語句要麼是寫在xml文件中,要麼直接使用@select註解在mapper類中寫。但我個人偏向於寫在xml文件中,因爲有時候sql語句比較長、比較複雜,直接用註解的話可能一眼看過去並不知道是實現了什麼功能,相反xml文件中使用特有的標籤能幫助我們理解語句完成的功能。

1. 動態sql
mybatis寫動態sql真的非常非常方便簡潔了,好用到想爲它瘋狂打call!!!
例如有三個可選的條件,那麼sql語句可以這麼寫:

SELECT
    A.authorName,A.country,B.bookName
    FROM author AS A
    LEFT OUTER JOIN book AS B ON (A.author_id = B.author_id AND A.book_id = B.book_id)
    <where>
      <if test="authorName!=null"></if>
      A.author_name = #{authorName}
      <if test="bookName!=null">
        AND B.bookName = #{bookName}
      </if>
      <if test="publicTime!=null">
        AND B.public_time = #{publicTime}
      </if>
    </where>
    GROUP BY A.authorName

在這個例子中,需要使用到幾個知識點:
a.表與表的連接
b.動態條件判斷語句的使用
c.<if>元素中,如果包括變量,不需要寫成#{varName}的形式,而是直接寫它的變量名就可以了。(我就是在這個地方吃了虧的,調試很久才發現,希望看到的讀者不要踩這個雷。)

2. 多張表連接的寫法

SELECT A.id,A.app_id,A.app_name,B.menu ,C.author_name
  FROM user_collection_app AS A
  LEFT OUTER JOIN app AS B ON A.app_id = B.app_id
  LEFT OUTER JOIN auth_user AS C ON B.author_id = C.author_id
  WHERE A.user_id = #{userId} GROUP BY A.app_id

3. 可以定義一個公共的接口實現基本的增刪改查
如果經常需要使用基本的增刪改查方法,爲了減少每次書寫語句的工作量,可以定義一個通用接口,讓其他的接口去繼承它,那麼每次要使用的時候,就能直接去調用了,這可以節約不少時間跟精力。
例如:

public interface MyService<T> {
    void save(T model);//持久化
    void save(List<T> models);//批量持久化
    void deleteById(Integer id);//通過主鍵刪除
    void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4”
    void update(T model);//更新
    T findById(Integer id);//通過ID查找
    T findById(Long id);//通過ID查找
    List<T> findBy(T record);
    T findBy(String fieldName, Object value) throws TooManyResultsException; //通過Model中某個成員變量名稱(非數據表中column的名稱)查找,value需符合unique約束
    List<T> findByIds(String ids);//通過多個ID查找//eg:ids -> “1,2,3,4”
    List<T> findByCondition(Example condition);//根據條件查找
    List<T> findAll();//獲取所有
}

4. xml文件中特殊字符的寫法
在sql語句中,難免會用到類似”<“、”>“這樣的符號,但是xml文件卻不能識別。所以要用的時候,必須進行轉義。

    &lt;         < 

    &gt;         >  

    &amp;        & 

    &apos;       '

    &quot;       "

    也可以使用<![CDATA[ ]]>符號進行說明,格式爲<![CDATA[ sql語句 ]]>  

參考博客:mybatis的一些特殊符號標識(大於,小於,等於,不等於)

5. 在數據庫篩選datetime類型的字段
除了select* fromtable where datetime between datetime1 and datetime2;這種寫法以外,還可以寫成:

select * from table where unix_timestamp(datetime) > unix_timestamp(datetime1) and unix_timestamp(datetime) < unix_
timestamp(datetime2);

這種形式,unix_timestamp函數是將字符型的時間,轉成unix時間戳。因爲不轉換的話雖然結果不報錯,但是並不準確。

6. 關於多表的連接可能會遇到的小問題
兩張表連接後(不管是左連接還是交叉連接),字段爲null的屬性,會被直接略掉,爲了避免這種情況的發生,有兩種方法:

a. 在sql語句中爲可能出現null的屬性添加ifnull函數,例如:

`select ifnull(status,0) AS status from table1 left join table2;`

當status爲null時,自動設爲默認值0,但是必須要爲它設置一個別名,否則有可能識別失敗。

當然如果設計的兩張表邏輯上比較簡單,字段並不是很多的時候,可以使用上面的方法。
但如果涉及兩張表以上的連接,並且字段較多的時候,明顯使用ifnull()函數就不太合適了,
這個時候我會更喜歡使用下面這種方式。

b. 用mybatis查詢返回的類型與entity聯繫起來,這個entity應在xml文件中作好映射

示例代碼:

<!--xml文件-->
<resultMap id="DevelopersResultMap" type="com.acloudapp.entity.developers.Developer">
    <id column="id" jdbcType="INTEGER" property="id"></id>
    <result column="user_id" jdbcType="VARCHAR" property="developerId" />
    <result column="real_name" jdbcType="VARCHAR" property="developerName" />
    <result column="nick_name" jdbcType="VARCHAR" property="nickName"/>
    <result column="head_portrait_img" jdbcType="VARCHAR" property="headPortrait"/>
    <result column="school_name" jdbcType="VARCHAR" property="school"/>
    <result column="mobile" jdbcType="VARCHAR" property="phoneNumber"/>
    <result column="create_time" jdbcType="TIMESTAMP" property="time" />
    <result column="type" jdbcType="VARCHAR" property="type" />
    <result column="province" jdbcType="VARCHAR" property="province" />
    <result column="city" jdbcType="VARCHAR" property="city" />
  </resultMap>
//Developer實體
public class Developer {
    private String developerId;
    private String developerName;
    private String nickName;
    private String headPortrait;
    private String school;
    private String phoneNumber;
    private Date time;
    private String type;
    private String province;
    private String city;

    //省略getter和setter方法
} 
<!--sql語句-->
SELECT
    auth_user.user_id,
    auth_user.real_name,
    auth_user.create_time,
    auth_user.nick_name,
    auth_user.head_portrait_img,
    auth_user.mobile,
    auth_user.school_name,
    auth_user.type,
    base_districts.city AS city,
    base_districts1.province AS province,
    #{recommendationId} AS recommendationId,
    #{position} AS position,
    #{title} AS title
    FROM auth_user
    LEFT JOIN base_districts ON base_districts.adcode=auth_user.adcode
    WHERE auth_user.user_id in (
            SELECT developers_id
            FROM user_quality_developers
            WHERE user_quality_developers.recommendation_id = #{recommendationId}
    )
    ORDER BY auth_user.create_time DESC

類似這種sql語句,比較複雜,且字段比較多的,我們就需要用一個實體與其進行映射。其中有的字段,如recommendationId、position,由於它們並沒有使用到表的字段,因此不需要寫入映射。

7. 內連接和外連接

參考資料:MySQL多表查詢之外鍵、表連接、子查詢、索引

8. 一段邏輯很複雜的sql語句
爲了寫這個查詢語句,簡直是絞盡腦汁,茶不思飯不想,找了許多資料(甚至去翻以前數據庫原理課的ppt),最後終於得到了相要的結果!!!喜大普奔啊!!!儘管知道邏輯複雜,肯定要耗費一些時間去查,只不過我已經儘量將語句精簡,希望在效率上不要太拖後腿。
這個語句用到了:子查詢、表連接、嵌套查詢、case-when句式、動態sql(喪心病狂啊有沒有???),難點是:我需要返回一個自定義的字段,該字段爲布爾類型的,因此我要用case-when進行邏輯判斷以確定返回的是1還是0;另外,在此基礎上,我還需要使用該自定義字段作爲外層查詢的條件進行篩選。。。總之我有點語無倫次了。直接po代碼- -。

 SELECT*
  FROM(
      SELECT
         A.operate_type AS appMenu,
         A.time AS time,
         A.operate_obj_name AS appName,
         B.icon,
         B.app_id AS appId,
         CASE
             when (SELECT COUNT(*) FROM comment
                    WHERE comment.user_id = #{userId} AND comment.app_id = A.operate_obj_id) > 0 then 1
             else 0 END AS isEvaluate
      FROM auth_user_log AS A
      LEFT OUTER JOIN app AS B ON A.operate_obj_id = B.app_id
      <where>
          <if test="userId!=null"></if>
              A.user_id = #{userId}
          <if test="startTime!=null">
              AND unix_timestamp(A.time) &gt; unix_timestamp(#{startTime})
          </if>
          <if test="endTime!=null">
              AND unix_timestamp(A.time) &lt; unix_timestamp(#{endTime})
          </if>
      </where>
      ORDER BY A.time DESC
  )C
    <where>
      <if test="isEvaluate!=null">
        isEvaluate = #{isEvaluate}
      </if>
    </where>

可以明顯看到,isEvaluate字段在外層查詢中被當作一個查詢的條件了,另外case-when裏的查詢語句的功能是判斷comment表中是否存在匹配的記錄(存在說明已評價,不存在則說明未評價),還有另一種簡單的寫法:

SELECT 1 FROM comment 
              WHERE comment.user_id = '10089'  AND comment.app_id = A.operate_obj_id limit 1

它返回的要麼就是1,要麼就是0,十分易於判斷。特別解釋下limit 1,mysql在找到一條記錄後就不會往下繼續找了。性能提升很多。

參考博客:


結語

使用mybatis倆仨月來,深刻體會到了它的輕便與魅力,如果還回去使用老舊的sql拼接,就真的太落後了。使用之餘,進行了一番感嘆,
希望能夠將mybatis學的更透徹。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章