`
AngelAndAngel
  • 浏览: 231342 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

MyBatis的SqlBuilder源码详解

阅读更多
   Mybatis的这个类比较精巧,适合被“拿来用”,还是稍微分析下,也许能有点收获。

    Mybatis中的SqlBuilder是用来处理java程序动态拼接sql操作的,把我们从以前需要注意空格或者or,and,where等关键字处理中解脱出来,这个类设计的比较精巧,而且不依赖其他的类或者包,很适合移植到自己的项目中去,所以分离出来对其源码进行解读和改造。

    首先,它用一个Threadlocal对象来存储SQL对象(表达sql的实体对象),这个东西我觉得表明:你可以以函数工具的方式操作它,同时,你也可以用你的dao来继承这个类,并不用担心线程安全的问题。
这个类里面定义了一个私有静态类 SQL,这个类有一个StatementType的枚举对象,如下:
public enum StatementType {
        DELETE,
        INSERT,
        SELECT,
        UPDATE
}
分别表示增删改查操作。
然后有以下类型语句集合,存储不同sql语句段,如下
List<String> sets = new ArrayList<String>();
    List<String> select = new ArrayList<String>();
    List<String> tables = new ArrayList<String>();
    List<String> join = new ArrayList<String>();
    List<String> innerJoin = new ArrayList<String>();
    List<String> outerJoin = new ArrayList<String>();
    List<String> leftOuterJoin = new ArrayList<String>();
    List<String> rightOuterJoin = new ArrayList<String>();
    List<String> where = new ArrayList<String>();
    List<String> having = new ArrayList<String>();
    List<String> groupBy = new ArrayList<String>();
    List<String> orderBy = new ArrayList<String>();
    List<String> lastList = new ArrayList<String>();
    List<String> columns = new ArrayList<String>();
    List<String> values = new ArrayList<String>();
  
boolean distinct;
包含了几乎所有的sql关键字
你肯定想象的到,会有selectSQL,insertSQL,deleteSQL,updateSQL这四个方法,分别为你返回对应的sql语句。这几个等会会详细讲解
还有一个sql()方法,通过StatementType枚举来返回你想要的xxxxSql()。
    最后还有个比较重要的方法,sqlClause,这个方法通过你传入的语句/类型,为你构造sql,代码如下:
private void sqlClause(StringBuilder builder, String keyword, List<String> parts, String open, String close, String conjunction) {
      if (!parts.isEmpty()) {
        if (builder.length() > 0) builder.append("\n");
        builder.append(keyword);
        builder.append(" ");
        builder.append(open);
        String last = "________";
        for (int i = 0, n = parts.size(); i < n; i++) {
          String part = parts.get(i);
          if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
            builder.append(conjunction);
          }
          builder.append(part);
          last = part;
        }
        builder.append(close);
      }
    }

    这个方法参数意思如下:
    
  •      builder:当前待拼接的sql语句。
  •      Keyword:关键字,如select,from,join,inner join,having,where等等这些。
  •      Parts:就是相对应的上面的那些List对象,比如select,join等等,传递你需要拼接的实际sql内容。
  •       Open,close:就是此语句开始和结尾的闭合字符,比如select,form,join等这个肯定都是“”,而where和having这个肯定就是“(”和“)”
  •       Conjunction:多个关键字语句中间的连接串,比如说select,from,group  by,order by这些的多个语句块都是“, ”连接的,比如select   a.name,a.pid ,order by a.id,b.id等。
  • 而join多个表肯定是有多个join,left out join 后面肯定也会是left out join,where 多个肯定是 and 连接的(这里不考虑or,因为已经有Or这个关键方法来表示)。
     
      其实从参数就可以看出此方法的设计意图了。
       首先builder拼接关键字,然后拼接闭合(开头)字符,在拼接第二个开始,判断是
  否是AND 和OR关键字,这两个关键字在SqlBuilder上面会有定义成静态变量
  private static final String AND = ") \nAND (";
  private static final String OR = ") \nOR (";
      假如是,则直接拼接,不是,则拼接多项分隔符。
       最后,拼接闭合(结尾)字符,返回。
       下面我们看看那四个方法(selectSQL,insertSQL,deleteSQL,updateSQL)是怎样调用他们的,以SelectSQL为例:如下
   
private String selectSQL() {
      StringBuilder builder = new StringBuilder();
      if (distinct) {
        sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
      } else {
        sqlClause(builder, "SELECT", select, "", "", ", ");
      }

      sqlClause(builder, "FROM", tables, "", "", ", ");
      sqlClause(builder, "JOIN", join, "", "", "JOIN");
      sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN ");
      sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN ");
      sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN ");
      sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN ");
      sqlClause(builder, "WHERE", where, "(", ")", " AND ");
      sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
      sqlClause(builder, "HAVING", having, "(", ")", " AND ");
      sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
      return builder.toString();
    }


     这个SQL静态类最终是为SqlBuilder来提供服务的,而SqlBuilder则暴露出我们需要的接口,提供传值的入口,我们以调用者的角度来看看SqlBuilder是怎样工作的。
一般来说,我们程序里是这样调用的:
public String selectByPro() {
		BEGIN();
		SELECT("p.id,p.username,p.password");
		SELECT("p.createDate,p.modifyDate");
		FROM("person p");
		FROM("account a");
		INNER_JOIN("dept d on d.id=p.id");
		INNER_JOIN("company c on c.id=d.id");
		WHERE("p.id=a.id");
		WHERE("p.name like '%afei%'");
		OR();
		WHERE("p.sex = '1'");
		GROUP_BY("p.id");
		HAVING("p.age > 20");
		ORDER_BY("p.id");
		ORDER_BY("p.name");
	    
		return SQL();
}
     
      Begin的时候会new一个SQL()对象放入当前线程变量ThreadLocal,
在select,from,where,orderby这些操作的时候会调用SQL对象的相关List来进行add动作,构造List。最后从当前线程变量中取出SQL对象,调用sql方法,返回。


    假如值需要构造select语句,那么还有个精简版的SelectBuilder可以选择。


一点思考,
  • 1, 你可以把这些方法或属性放入你的基础DAO里面,不用担心线程安全的问题,这是最佳实践。
  • 2, 当作工具类来用,但是这样影响代码的可读性。
  • 3, 这个类里面的方法都不是同步的,而是操作了一个线程安全的变量,这样可以避免多线程在调用这个类的不同方法时被迫同步的情况。

 

By 阿飞哥 转载请说明
腾讯微博:http://t.qq.com/duyunfeiRoom
新浪微博:http://weibo.com/u/1766094735
2
1
分享到:
评论
2 楼 AngelAndAngel 2011-09-13  
giianhui 写道
希望能附上源码,谢谢!

你下载mybatis的源码之后就有啊。

package org.apache.ibatis.jdbc;

import java.util.ArrayList;
import java.util.List;

public class SqlBuilder {
  private static final String AND = ") \nAND (";
  private static final String OR = ") \nOR (";

  private static final ThreadLocal<SQL> localSQL = new ThreadLocal<SQL>();

  public static void BEGIN() {
    RESET();
  }

  public static void RESET() {
    localSQL.set(new SQL());
  }

  public static void UPDATE(String table) {
    sql().statementType = SQL.StatementType.UPDATE;
    sql().tables.add(table);
  }

  public static void SET(String sets) {
    sql().sets.add(sets);
  }

  public static String SQL() {
    try {
      return sql().sql();
    } finally {
        RESET();
    }
  }

  public static void INSERT_INTO(String tableName) {
    sql().statementType = SQL.StatementType.INSERT;
    sql().tables.add(tableName);
  }

  public static void VALUES(String columns, String values) {
    sql().columns.add(columns);
    sql().values.add(values);
  }

  public static void SELECT(String columns) {
    sql().statementType = SQL.StatementType.SELECT;
    sql().select.add(columns);
  }

  public static void SELECT_DISTINCT(String columns) {
    sql().distinct = true;
    SELECT(columns);
  }

  public static void DELETE_FROM(String table) {
    sql().statementType = SQL.StatementType.DELETE;
    sql().tables.add(table);
  }

  public static void FROM(String table) {
    sql().tables.add(table);
  }

  public static void JOIN(String join) {
    sql().join.add(join);
  }

  public static void INNER_JOIN(String join) {
    sql().innerJoin.add(join);
  }

  public static void LEFT_OUTER_JOIN(String join) {
    sql().leftOuterJoin.add(join);
  }

  public static void RIGHT_OUTER_JOIN(String join) {
    sql().rightOuterJoin.add(join);
  }

  public static void OUTER_JOIN(String join) {
    sql().outerJoin.add(join);
  }

  public static void WHERE(String conditions) {
    sql().where.add(conditions);
    sql().lastList = sql().where;
  }

  public static void OR() {
    sql().lastList.add(OR);
  }

  public static void AND() {
    sql().lastList.add(AND);
  }

  public static void GROUP_BY(String columns) {
    sql().groupBy.add(columns);
  }

  public static void HAVING(String conditions) {
    sql().having.add(conditions);
    sql().lastList = sql().having;
  }

  public static void ORDER_BY(String columns) {
    sql().orderBy.add(columns);
  }

  private static SQL sql() {
    SQL sql = localSQL.get();
    if (sql == null) {
      RESET();
      sql = localSQL.get();
    }
    return sql;
  }

  private static class SQL {
    public enum StatementType {
        DELETE,
        INSERT,
        SELECT,
        UPDATE
    }
    
    StatementType statementType;
    List<String> sets = new ArrayList<String>();
    List<String> select = new ArrayList<String>();
    List<String> tables = new ArrayList<String>();
    List<String> join = new ArrayList<String>();
    List<String> innerJoin = new ArrayList<String>();
    List<String> outerJoin = new ArrayList<String>();
    List<String> leftOuterJoin = new ArrayList<String>();
    List<String> rightOuterJoin = new ArrayList<String>();
    List<String> where = new ArrayList<String>();
    List<String> having = new ArrayList<String>();
    List<String> groupBy = new ArrayList<String>();
    List<String> orderBy = new ArrayList<String>();
    List<String> lastList = new ArrayList<String>();
    List<String> columns = new ArrayList<String>();
    List<String> values = new ArrayList<String>();
    boolean distinct;

    private void sqlClause(StringBuilder builder, String keyword, List<String> parts, String open, String close, String conjunction) {
      if (!parts.isEmpty()) {
        if (builder.length() > 0) builder.append("\n");
        builder.append(keyword);
        builder.append(" ");
        builder.append(open);
        String last = "________";
        for (int i = 0, n = parts.size(); i < n; i++) {
          String part = parts.get(i);
          if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
            builder.append(conjunction);
          }
          builder.append(part);
          last = part;
        }
        builder.append(close);
      }
    }

    private String selectSQL() {
      StringBuilder builder = new StringBuilder();
      if (distinct) {
        sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
      } else {
        sqlClause(builder, "SELECT", select, "", "", ", ");
      }

      sqlClause(builder, "FROM", tables, "", "", ", ");
      sqlClause(builder, "JOIN", join, "", "", "JOIN");
      sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN ");
      sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN ");
      sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN ");
      sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN ");
      sqlClause(builder, "WHERE", where, "(", ")", " AND ");
      sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
      sqlClause(builder, "HAVING", having, "(", ")", " AND ");
      sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
      return builder.toString();
    }

    private String insertSQL() {
      StringBuilder builder = new StringBuilder();

      sqlClause(builder, "INSERT INTO", tables, "", "", "");
      sqlClause(builder, "", columns, "(", ")", ", ");
      sqlClause(builder, "VALUES", values, "(", ")", ", ");
      return builder.toString();
    }

    private String deleteSQL() {
      StringBuilder builder = new StringBuilder();

      sqlClause(builder, "DELETE FROM", tables, "", "", "");
      sqlClause(builder, "WHERE", where, "(", ")", " AND ");
      return builder.toString();
    }

    private String updateSQL() {
      StringBuilder builder = new StringBuilder();

      sqlClause(builder, "UPDATE", tables, "", "", "");
      sqlClause(builder, "SET", sets, "", "", ", ");
      sqlClause(builder, "WHERE", where, "(", ")", " AND ");
      return builder.toString();
    }

    public String sql() {
      if (statementType == null) {
        return null;
      }
      
      String answer;
      
      switch (statementType) {
      case DELETE:
        answer = deleteSQL();
        break;
          
      case INSERT:
        answer = insertSQL();
        break;
      
      case SELECT:
        answer = selectSQL();
        break;
          
      case UPDATE:
        answer = updateSQL();
        break;
          
      default:
        answer = null;
      }

      return answer;
    }
  }
}


1 楼 giianhui 2011-09-13  
希望能附上源码,谢谢!

相关推荐

Global site tag (gtag.js) - Google Analytics