MyBatis 3 提供了方便的工具类来帮助解决此问题。借助 SQL 类,我们只需要简单地创建一个实例,并调用它的方法即可生成 SQL 语句。让我们来用 SQL 类重写上面的例子:

    1. return new SQL() {{
    2. SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
    3. SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
    4. FROM("PERSON P");
    5. FROM("ACCOUNT A");
    6. INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
    7. INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
    8. WHERE("P.ID = A.ID");
    9. WHERE("P.FIRST_NAME like ?");
    10. OR();
    11. WHERE("P.LAST_NAME like ?");
    12. GROUP_BY("P.ID");
    13. HAVING("P.LAST_NAME like ?");
    14. OR();
    15. HAVING("P.FIRST_NAME like ?");
    16. ORDER_BY("P.ID");
    17. ORDER_BY("P.FULL_NAME");
    18. }}.toString();
    19. }

    这个例子有什么特别之处吗?仔细看一下你会发现,你不用担心可能会重复出现的 “AND” 关键字,或者要做出用 “WHERE” 拼接还是 “AND” 拼接还是不用拼接的选择。SQL 类已经为你处理了哪里应该插入 “WHERE”、哪里应该使用 “AND” 的问题,并帮你完成所有的字符串拼接工作。

    这里有一些示例:

    1. // 匿名内部类风格
    2. public String deletePersonSql() {
    3. return new SQL() {{
    4. DELETE_FROM("PERSON");
    5. WHERE("ID = #{id}");
    6. }}.toString();
    7. }
    8. // Builder / Fluent 风格
    9. public String insertPersonSql() {
    10. String sql = new SQL()
    11. .INSERT_INTO("PERSON")
    12. .VALUES("ID, FIRST_NAME", "#{id}, #{firstName}")
    13. .VALUES("LAST_NAME", "#{lastName}")
    14. .toString();
    15. return sql;
    16. }
    17. // 动态条件(注意参数需要使用 final 修饰,以便匿名内部类对它们进行访问)
    18. public String selectPersonLike(final String id, final String firstName, final String lastName) {
    19. return new SQL() {{
    20. SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME");
    21. FROM("PERSON P");
    22. if (id != null) {
    23. WHERE("P.ID like #{id}");
    24. }
    25. WHERE("P.FIRST_NAME like #{firstName}");
    26. }
    27. if (lastName != null) {
    28. WHERE("P.LAST_NAME like #{lastName}");
    29. }
    30. ORDER_BY("P.LAST_NAME");
    31. }}.toString();
    32. }
    33. public String deletePersonSql() {
    34. return new SQL() {{
    35. DELETE_FROM("PERSON");
    36. WHERE("ID = #{id}");
    37. }}.toString();
    38. }
    39. public String insertPersonSql() {
    40. return new SQL() {{
    41. INSERT_INTO("PERSON");
    42. VALUES("ID, FIRST_NAME", "#{id}, #{firstName}");
    43. VALUES("LAST_NAME", "#{lastName}");
    44. }
    45. public String updatePersonSql() {
    46. return new SQL() {{
    47. UPDATE("PERSON");
    48. SET("FIRST_NAME = #{firstName}");
    49. WHERE("ID = #{id}");
    50. }}.toString();
    51. }

    从版本 3.4.2 开始,你可以像下面这样使用可变长度参数:

    从版本 3.5.2 开始,你可以像下面这样构建批量插入语句:

    1. public String insertPersonsSql() {
    2. // INSERT INTO PERSON (ID, FULL_NAME)
    3. // VALUES (#{mainPerson.id}, #{mainPerson.fullName}) , (#{subPerson.id}, #{subPerson.fullName})
    4. return new SQL()
    5. .INSERT_INTO("PERSON")
    6. .INTO_COLUMNS("ID", "FULL_NAME")
    7. .INTO_VALUES("#{mainPerson.id}", "#{mainPerson.fullName}")
    8. .ADD_ROW()
    9. .INTO_VALUES("#{subPerson.id}", "#{subPerson.fullName}")
    10. .toString();
    11. }

    从版本 3.5.2 开始,你可以像下面这样构建限制返回结果数的 SELECT 语句,:

    1. public String selectPersonsWithOffsetLimitSql() {
    2. // SELECT id, name FROM PERSON
    3. // LIMIT #{limit} OFFSET #{offset}
    4. return new SQL()
    5. .SELECT("id", "name")
    6. .FROM("PERSON")
    7. .LIMIT("#{limit}")
    8. .OFFSET("#{offset}")
    9. .toString();
    10. }
    11. public String selectPersonsWithFetchFirstSql() {
    12. // SELECT id, name FROM PERSON
    13. // OFFSET #{offset} ROWS FETCH FIRST #{limit} ROWS ONLY
    14. return new SQL()
    15. .SELECT("id", "name")
    16. .FROM("PERSON")
    17. .OFFSET_ROWS("#{offset}")
    18. .FETCH_FIRST_ROWS_ONLY("#{limit}")
    19. .toString();
    20. }

    下面的方法仅仅适用于废弃的 SqlBuilder 和 SelectBuilder 类。

    方法描述
    BEGIN() / RESET()这些方法清空 SelectBuilder 类的 ThreadLocal 状态,并准备好构建一个新的语句。开始新的语句时,BEGIN() 是最名副其实的(可读性最好的)。但如果由于一些原因(比如程序逻辑在某些条件下需要一个完全不同的语句),在执行过程中要重置语句构建状态,就很适合使用 RESET()
    SQL()该方法返回生成的 SQL() 并重置 SelectBuilder 状态(等价于调用了 BEGIN()RESET())。因此,该方法只能被调用一次!

    SelectBuilder 和 SqlBuilder 类并不神奇,但最好还是知道它们的工作原理。 SelectBuilder 以及 SqlBuilder 借助静态导入和 ThreadLocal 变量实现了对插入条件友好的简洁语法。要使用它们,只需要静态导入这个类的方法即可,就像这样(只能使用其中的一条,不能同时使用):

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

    然后就可以像下面这样创建一些方法:

    1. /* 已被废弃 */
    2. public String selectBlogsSql() {
    3. BEGIN(); // 重置 ThreadLocal 状态变量
    4. SELECT("*");
    5. FROM("BLOG");
    6. }