mybatis-SQL語句構建器類

SQL語句構建器類

問題

Java程序員面對的最痛苦的事情之一就是在Java代碼中嵌入SQL語句。這麼來作一般是因爲SQL語句須要動態來生成-不然能夠將它們放到外部文件或者存儲過程當中。正如你已經看到的那樣,MyBatis在它的XML映射特性中有一個強大的動態SQL生成方案。但有時在Java代碼內部建立SQL語句也是必要的。此時,MyBatis有另一個特性能夠幫到你,在減小典型的加號,引號,新行,格式化問題和嵌入條件來處理多餘的逗號或 AND 鏈接詞以前。事實上,在Java代碼中來動態生成SQL代碼就是一場噩夢。例如:程序員

String sql = "SELECT P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME, "
"P.LAST_NAME,P.CREATED_ON, P.UPDATED_ON " +
"FROM PERSON P, ACCOUNT A " +
"INNER JOIN DEPARTMENT D on D.ID = P.DEPARTMENT_ID " +
"INNER JOIN COMPANY C on D.COMPANY_ID = C.ID " +
"WHERE (P.ID = A.ID AND P.FIRST_NAME like ?) " +
"OR (P.LAST_NAME like ?) " +
"GROUP BY P.ID " +
"HAVING (P.LAST_NAME like ?) " +
"OR (P.FIRST_NAME like ?) " +
"ORDER BY P.ID, P.FULL_NAME";

The Solution

MyBatis 3提供了方便的工具類來幫助解決該問題。使用SQL類,簡單地建立一個實例來調用方法生成SQL語句。上面示例中的問題就像重寫SQL類那樣:sql

private String selectPersonSql() {
  return new SQL() {{
    SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
    SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
    FROM("PERSON P");
    FROM("ACCOUNT A");
    INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
    INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
    WHERE("P.ID = A.ID");
    WHERE("P.FIRST_NAME like ?");
    OR();
    WHERE("P.LAST_NAME like ?");
    GROUP_BY("P.ID");
    HAVING("P.LAST_NAME like ?");
    OR();
    HAVING("P.FIRST_NAME like ?");
    ORDER_BY("P.ID");
    ORDER_BY("P.FULL_NAME");
  }}.toString();
}

該例中有什麼特殊之處?當你仔細看時,那不用擔憂偶然間重複出現的"AND"關鍵字,或者在"WHERE"和"AND"之間的選擇,抑或什麼都不選。該SQL類很是注意"WHERE"應該出如今何處,哪裏又應該使用"AND",還有全部的字符串連接。數據庫

SQL類

這裏給出一些示例:apache

// Anonymous inner class
public String deletePersonSql() {
  return new SQL() {{
    DELETE_FROM("PERSON");
    WHERE("ID = #{id}");
  }}.toString();
}

// Builder / Fluent style
public String insertPersonSql() {
  String sql = new SQL()
    .INSERT_INTO("PERSON")
    .VALUES("ID, FIRST_NAME", "#{id}, #{firstName}")
    .VALUES("LAST_NAME", "#{lastName}")
    .toString();
  return sql;
}

// With conditionals (note the final parameters, required for the anonymous inner class to access them)
public String selectPersonLike(final String id, final String firstName, final String lastName) {
  return new SQL() {{
    SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
    FROM("PERSON P");
    if (id != null) {
      WHERE("P.ID like #{id}");
    }
    if (firstName != null) {
      WHERE("P.FIRST_NAME like #{firstName}");
    }
    if (lastName != null) {
      WHERE("P.LAST_NAME like #{lastName}");
    }
    ORDER_BY("P.LAST_NAME");
  }}.toString();
}

public String deletePersonSql() {
  return new SQL() {{
    DELETE_FROM("PERSON");
    WHERE("ID = #{id}");
  }}.toString();
}

public String insertPersonSql() {
  return new SQL() {{
    INSERT_INTO("PERSON");
    VALUES("ID, FIRST_NAME", "#{id}, #{firstName}");
    VALUES("LAST_NAME", "#{lastName}");
  }}.toString();
}

public String updatePersonSql() {
  return new SQL() {{
    UPDATE("PERSON");
    SET("FIRST_NAME = #{firstName}");
    WHERE("ID = #{id}");
  }}.toString();
}
方法 描述
SELECT(String) 開始或插入到 SELECT子句。 能夠被屢次調用,參數也會添加到 SELECT子句。 參數一般使用逗號分隔的列名和別名列表,但也能夠是數據庫驅動程序接受的任意類型。
SELECT_DISTINCT(String) 開始或插入到 SELECT子句, 也能夠插入 DISTINCT關鍵字到生成的查詢語句中。 能夠被屢次調用,參數也會添加到 SELECT子句。 參數一般使用逗號分隔的列名和別名列表,但也能夠是數據庫驅動程序接受的任意類型。
FROM(String) 開始或插入到 FROM子句。 能夠被屢次調用,參數也會添加到 FROM子句。 參數一般是表名或別名,也能夠是數據庫驅動程序接受的任意類型。
  • JOIN(String)
  • INNER_JOIN(String)
  • LEFT_OUTER_JOIN(String)
  • RIGHT_OUTER_JOIN(String)
基於調用的方法,添加新的合適類型的 JOIN子句。 參數能夠包含由列命和join on條件組合成標準的join。
WHERE(String) 插入新的 WHERE子句條件, 由AND連接。能夠屢次被調用,每次都由AND來連接新條件。使用 OR() 來分隔OR。
OR() 使用OR來分隔當前的 WHERE子句條件。 能夠被屢次調用,但在一行中屢次調用或生成不穩定的SQL。
AND() 使用AND來分隔當前的 WHERE子句條件。 能夠被屢次調用,但在一行中屢次調用或生成不穩定的SQL。由於 WHERE 和 HAVING 兩者都會自動連接 AND, 這是很是罕見的方法,只是爲了完整性才被使用。
GROUP_BY(String) 插入新的 GROUP BY子句元素,由逗號鏈接。 能夠被屢次調用,每次都由逗號鏈接新的條件。
HAVING(String) 插入新的 HAVING子句條件。 由AND鏈接。能夠被屢次調用,每次都由AND來鏈接新的條件。使用 OR() 來分隔OR.
ORDER_BY(String) 插入新的 ORDER BY子句元素, 由逗號鏈接。能夠屢次被調用,每次由逗號鏈接新的條件。
DELETE_FROM(String) 開始一個delete語句並指定須要從哪一個表刪除的表名。一般它後面都會跟着WHERE語句!
INSERT_INTO(String) 開始一個insert語句並指定須要插入數據的表名。後面都會跟着一個或者多個VALUES()。
SET(String) 針對update語句,插入到"set"列表中
UPDATE(String) 開始一個update語句並指定須要更新的代表。後面都會跟着一個或者多個SET(),一般也會有一個WHERE()。
VALUES(String, String) 插入到insert語句中。第一個參數是要插入的列名,第二個參數則是該列的值。

SqlBuilder 和 SelectBuilder (已經廢棄)

在3.2版本以前,咱們使用了一點不一樣的作法,經過實現ThreadLocal變量來掩蓋一些致使Java DSL麻煩的語言限制。但這種方式已經廢棄了,現代的框架都歡迎人們使用構建器類型和匿名內部類的想法。所以,SelectBuilder 和 SqlBuilder 類都被廢棄了。框架

下面的方法僅僅適用於廢棄的SqlBuilder 和 SelectBuilder 類。工具

方法 描述
BEGIN() /RESET() 這些方法清空SelectBuilder類的ThreadLocal狀態,而且準備一個新的構建語句。開始新的語句時, BEGIN()讀取得最好。 因爲一些緣由(在某些條件下,也許是邏輯須要一個徹底不一樣的語句),在執行中清理語句RESET()讀取得最好。
SQL() 返回生成的 SQL() 並重置 SelectBuilder 狀態 (好像 BEGIN() 或 RESET() 被調用了). 所以,該方法只能被調用一次!

SelectBuilder 和 SqlBuilder 類並不神奇,可是知道它們如何工做也是很重要的。 SelectBuilder 使用 SqlBuilder 使用了靜態導入和ThreadLocal變量的組合來開啓整潔語法,能夠很容易地和條件交錯。使用它們,靜態導入類的方法便可,就像這樣(一個或其它,並不是二者):ui

import static org.apache.ibatis.jdbc.SelectBuilder.*;
import static org.apache.ibatis.jdbc.SqlBuilder.*;

這就容許像下面這樣來建立方法:spa

/* DEPRECATED */
public String selectBlogsSql() {
  BEGIN(); // Clears ThreadLocal variable
  SELECT("*");
  FROM("BLOG");
  return SQL();
}
/* DEPRECATED */
private String selectPersonSql() {
  BEGIN(); // Clears ThreadLocal variable
  SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
  SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
  FROM("PERSON P");
  FROM("ACCOUNT A");
  INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
  INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
  WHERE("P.ID = A.ID");
  WHERE("P.FIRST_NAME like ?");
  OR();
  WHERE("P.LAST_NAME like ?");
  GROUP_BY("P.ID");
  HAVING("P.LAST_NAME like ?");
  OR();
  HAVING("P.FIRST_NAME like ?");
  ORDER_BY("P.ID");
  ORDER_BY("P.FULL_NAME");
  return SQL();
}
相關文章
相關標籤/搜索