예제 #1
0
 void delete(Db db, Object obj) {
   if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
     throw new IllegalStateException(
         "No primary key columns defined "
             + "for table "
             + obj.getClass()
             + " - no update possible");
   }
   SQLStatement stat = new SQLStatement(db);
   StatementBuilder buff = new StatementBuilder("DELETE FROM ");
   buff.append(db.getDialect().getTableName(schemaName, tableName));
   buff.resetCount();
   Object alias = ClassUtils.newObject(obj.getClass());
   Query<Object> query = Query.from(db, alias);
   boolean firstCondition = true;
   for (FieldDefinition field : fields) {
     if (field.isPrimaryKey) {
       Object aliasValue = field.getValue(alias);
       Object value = field.getValue(obj);
       if (!firstCondition) {
         query.addConditionToken(ConditionAndOr.AND);
       }
       firstCondition = false;
       query.addConditionToken(new Condition<Object>(aliasValue, value, CompareType.EQUAL));
     }
   }
   stat.setSQL(buff.toString());
   query.appendWhere(stat);
   StatementLogger.delete(stat.getSQL());
   stat.executeUpdate();
 }
예제 #2
0
 @SuppressWarnings("unchecked")
 private <X> List<X> selectSimple(X x, boolean distinct) {
   SQLStatement stat = getSelectStatement(distinct);
   appendSQL(stat, x);
   appendFromWhere(stat);
   ResultSet rs = stat.executeQuery();
   List<X> result = New.arrayList();
   Statement s = null;
   try {
     s = rs.getStatement();
     while (rs.next()) {
       try {
         X value;
         Object o = rs.getObject(1);
         int convertHereIsProbablyWrong;
         if (Clob.class.isAssignableFrom(o.getClass())) {
           value = (X) ClassUtils.convert(o, String.class);
         } else {
           value = (X) o;
         }
         result.add(value);
       } catch (Exception e) {
         throw new RuntimeException(e);
       }
     }
   } catch (SQLException e) {
     throw new RuntimeException(e);
   } finally {
     JdbcUtils.closeSilently(rs);
     JdbcUtils.closeSilently(s);
   }
   return result;
 }
예제 #3
0
 @SuppressWarnings("unchecked")
 private <X, Z> List<X> select(Z x, boolean distinct) {
   Class<?> clazz = x.getClass();
   if (ClassUtils.isSimpleType(clazz)) {
     return selectSimple((X) x, distinct);
   }
   clazz = clazz.getSuperclass();
   return select((Class<X>) clazz, (X) x, distinct);
 }
예제 #4
0
 public static Boolean not(Boolean x) {
   return Db.registerToken(
       ClassUtils.newObject(Boolean.class),
       new Function("", x) {
         public <T> void appendSQL(SQLStatement stat, Query<T> query) {
           stat.appendSQL("NOT ");
           query.appendSQL(stat, x[0]);
         }
       });
 }
예제 #5
0
 public static Boolean isNotNull(Object x) {
   return Db.registerToken(
       ClassUtils.newObject(Boolean.class),
       new Function("", x) {
         public <T> void appendSQL(SQLStatement stat, Query<T> query) {
           query.appendSQL(stat, x[0]);
           stat.appendSQL(" IS NOT NULL");
         }
       });
 }
예제 #6
0
 void setValue(Object obj, Object o) {
   try {
     if (!field.isAccessible()) {
       field.setAccessible(true);
     }
     o = ClassUtils.convert(o, field.getType());
     field.set(obj, o);
   } catch (Exception e) {
     throw new RuntimeException(e);
   }
 }
예제 #7
0
 public static Boolean like(String x, String pattern) {
   Boolean o = ClassUtils.newObject(Boolean.class);
   return Db.registerToken(
       o,
       new Function("LIKE", x, pattern) {
         public <T> void appendSQL(SQLStatement stat, Query<T> query) {
           stat.appendSQL("(");
           query.appendSQL(stat, x[0]);
           stat.appendSQL(" LIKE ");
           query.appendSQL(stat, x[1]);
           stat.appendSQL(")");
         }
       });
 }
예제 #8
0
 public static Boolean or(Boolean... x) {
   return Db.registerToken(
       ClassUtils.newObject(Boolean.class),
       new Function("", (Object[]) x) {
         public <T> void appendSQL(SQLStatement stat, Query<T> query) {
           int i = 0;
           for (Object o : x) {
             if (i++ > 0) {
               stat.appendSQL(" OR ");
             }
             query.appendSQL(stat, o);
           }
         }
       });
 }
예제 #9
0
 private <X> List<X> select(Class<X> clazz, X x, boolean distinct) {
   List<X> result = New.arrayList();
   TableDefinition<X> def = db.define(clazz);
   SQLStatement stat = getSelectStatement(distinct);
   def.appendSelectList(stat, this, x);
   appendFromWhere(stat);
   ResultSet rs = stat.executeQuery();
   Statement s = null;
   try {
     s = rs.getStatement();
     while (rs.next()) {
       X row = ClassUtils.newObject(clazz);
       def.readRow(row, rs);
       result.add(row);
     }
   } catch (SQLException e) {
     throw new RuntimeException(e);
   } finally {
     JdbcUtils.closeSilently(rs);
     JdbcUtils.closeSilently(s);
   }
   return result;
 }
예제 #10
0
 public static Long count(Object x) {
   return Db.registerToken(ClassUtils.newObject(Long.class), new Function("COUNT", x));
 }
예제 #11
0
 @SuppressWarnings("unchecked")
 public static <T extends Number> T sum(T x) {
   return (T) Db.registerToken(ClassUtils.newObject(x.getClass()), new Function("SUM", x));
 }
예제 #12
0
 public static Integer length(Object x) {
   return Db.registerToken(ClassUtils.newObject(Integer.class), new Function("LENGTH", x));
 }
예제 #13
0
 @SuppressWarnings("unchecked")
 public static <X> X max(X x) {
   Class<X> clazz = (Class<X>) x.getClass();
   X o = ClassUtils.newObject(clazz);
   return Db.registerToken(o, new Function("MAX", x));
 }
예제 #14
0
 void initWithNewObject(Object obj) {
   Object o = ClassUtils.newObject(field.getType());
   setValue(obj, o);
 }
예제 #15
0
/**
 * A table definition contains the index definitions of a table, the field definitions, the table
 * name, and other meta data.
 *
 * @param <T> the table type
 */
class TableDefinition<T> {

  /** The meta data of an index. */
  static class IndexDefinition {
    IndexType type;
    String indexName;

    List<String> columnNames;
  }

  /** The meta data of a field. */
  static class FieldDefinition {
    String columnName;
    Field field;
    String dataType;
    int maxLength;
    boolean isPrimaryKey;
    boolean isAutoIncrement;
    boolean trimString;
    boolean allowNull;
    String defaultValue;

    Object getValue(Object obj) {
      try {
        return field.get(obj);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    void initWithNewObject(Object obj) {
      Object o = ClassUtils.newObject(field.getType());
      setValue(obj, o);
    }

    void setValue(Object obj, Object o) {
      try {
        if (!field.isAccessible()) {
          field.setAccessible(true);
        }
        o = ClassUtils.convert(o, field.getType());
        field.set(obj, o);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    Object read(ResultSet rs, int columnIndex) {
      try {
        return rs.getObject(columnIndex);
      } catch (SQLException e) {
        throw new RuntimeException(e);
      }
    }
  }

  String schemaName;
  String tableName;
  int tableVersion;
  private boolean createTableIfRequired = true;
  private final Class<T> clazz;
  private final ArrayList<FieldDefinition> fields = New.arrayList();
  private final IdentityHashMap<Object, FieldDefinition> fieldMap = ClassUtils.newIdentityHashMap();

  private List<String> primaryKeyColumnNames;
  private final ArrayList<IndexDefinition> indexes = New.arrayList();
  private boolean memoryTable;

  TableDefinition(Class<T> clazz) {
    this.clazz = clazz;
    schemaName = null;
    tableName = clazz.getSimpleName();
  }

  Class<T> getModelClass() {
    return clazz;
  }

  List<FieldDefinition> getFields() {
    return fields;
  }

  void setSchemaName(String schemaName) {
    this.schemaName = schemaName;
  }

  void setTableName(String tableName) {
    this.tableName = tableName;
  }

  /**
   * Define a primary key by the specified model fields.
   *
   * @param modelFields the ordered list of model fields
   */
  void setPrimaryKey(Object[] modelFields) {
    List<String> columnNames = mapColumnNames(modelFields);
    setPrimaryKey(columnNames);
  }

  /**
   * Define a primary key by the specified column names.
   *
   * @param columnNames the ordered list of column names
   */
  void setPrimaryKey(List<String> columnNames) {
    primaryKeyColumnNames = New.arrayList(columnNames);
    // set isPrimaryKey flag for all field definitions
    for (FieldDefinition fieldDefinition : fieldMap.values()) {
      fieldDefinition.isPrimaryKey =
          this.primaryKeyColumnNames.contains(fieldDefinition.columnName);
    }
  }

  <A> String getColumnName(A fieldObject) {
    FieldDefinition def = fieldMap.get(fieldObject);
    return def == null ? null : def.columnName;
  }

  private ArrayList<String> mapColumnNames(Object[] columns) {
    ArrayList<String> columnNames = New.arrayList();
    for (Object column : columns) {
      columnNames.add(getColumnName(column));
    }
    return columnNames;
  }

  /**
   * Defines an index with the specified model fields.
   *
   * @param type the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
   * @param modelFields the ordered list of model fields
   */
  void addIndex(IndexType type, Object[] modelFields) {
    List<String> columnNames = mapColumnNames(modelFields);
    addIndex(type, columnNames);
  }

  /**
   * Defines an index with the specified column names.
   *
   * @param type the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
   * @param columnNames the ordered list of column names
   */
  void addIndex(IndexType type, List<String> columnNames) {
    IndexDefinition index = new IndexDefinition();
    index.indexName = tableName + "_" + indexes.size();
    index.columnNames = New.arrayList(columnNames);
    index.type = type;
    indexes.add(index);
  }

  public void setMaxLength(Object column, int maxLength) {
    String columnName = getColumnName(column);
    for (FieldDefinition f : fields) {
      if (f.columnName.equals(columnName)) {
        f.maxLength = maxLength;
        break;
      }
    }
  }

  void mapFields() {
    boolean byAnnotationsOnly = false;
    boolean inheritColumns = false;
    boolean strictTypeMapping = false;
    if (clazz.isAnnotationPresent(JQTable.class)) {
      JQTable tableAnnotation = clazz.getAnnotation(JQTable.class);
      byAnnotationsOnly = tableAnnotation.annotationsOnly();
      inheritColumns = tableAnnotation.inheritColumns();
      strictTypeMapping = tableAnnotation.strictTypeMapping();
    }

    List<Field> classFields = New.arrayList();
    classFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
    if (inheritColumns) {
      Class<?> superClass = clazz.getSuperclass();
      classFields.addAll(Arrays.asList(superClass.getDeclaredFields()));
    }

    for (Field f : classFields) {
      // default to field name
      String columnName = f.getName();
      boolean isAutoIncrement = false;
      boolean isPrimaryKey = false;
      int maxLength = 0;
      boolean trimString = false;
      boolean allowNull = true;
      String defaultValue = "";
      boolean hasAnnotation = f.isAnnotationPresent(JQColumn.class);
      if (hasAnnotation) {
        JQColumn col = f.getAnnotation(JQColumn.class);
        if (!StringUtils.isNullOrEmpty(col.name())) {
          columnName = col.name();
        }
        isAutoIncrement = col.autoIncrement();
        isPrimaryKey = col.primaryKey();
        maxLength = col.maxLength();
        trimString = col.trimString();
        allowNull = col.allowNull();
        defaultValue = col.defaultValue();
      }
      boolean isPublic = Modifier.isPublic(f.getModifiers());
      boolean reflectiveMatch = isPublic && !byAnnotationsOnly;
      if (reflectiveMatch || hasAnnotation) {
        FieldDefinition fieldDef = new FieldDefinition();
        fieldDef.field = f;
        fieldDef.columnName = columnName;
        fieldDef.isAutoIncrement = isAutoIncrement;
        fieldDef.isPrimaryKey = isPrimaryKey;
        fieldDef.maxLength = maxLength;
        fieldDef.trimString = trimString;
        fieldDef.allowNull = allowNull;
        fieldDef.defaultValue = defaultValue;
        fieldDef.dataType = ModelUtils.getDataType(fieldDef, strictTypeMapping);
        fields.add(fieldDef);
      }
    }
    List<String> primaryKey = New.arrayList();
    for (FieldDefinition fieldDef : fields) {
      if (fieldDef.isPrimaryKey) {
        primaryKey.add(fieldDef.columnName);
      }
    }
    if (primaryKey.size() > 0) {
      setPrimaryKey(primaryKey);
    }
  }

  /** Optionally truncates strings to the maximum length */
  private static Object getValue(Object obj, FieldDefinition field) {
    Object value = field.getValue(obj);
    if (field.trimString && field.maxLength > 0) {
      if (value instanceof String) {
        // clip strings
        String s = (String) value;
        if (s.length() > field.maxLength) {
          return s.substring(0, field.maxLength);
        }
        return s;
      }
      return value;
    }
    // standard behavior
    return value;
  }

  long insert(Db db, Object obj, boolean returnKey) {
    SQLStatement stat = new SQLStatement(db);
    StatementBuilder buff = new StatementBuilder("INSERT INTO ");
    buff.append(db.getDialect().getTableName(schemaName, tableName)).append('(');
    for (FieldDefinition field : fields) {
      buff.appendExceptFirst(", ");
      buff.append(field.columnName);
    }
    buff.append(") VALUES(");
    buff.resetCount();
    for (FieldDefinition field : fields) {
      buff.appendExceptFirst(", ");
      buff.append('?');
      Object value = getValue(obj, field);
      stat.addParameter(value);
    }
    buff.append(')');
    stat.setSQL(buff.toString());
    StatementLogger.insert(stat.getSQL());
    if (returnKey) {
      return stat.executeInsert();
    }
    return stat.executeUpdate();
  }

  void merge(Db db, Object obj) {
    if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
      throw new IllegalStateException(
          "No primary key columns defined "
              + "for table "
              + obj.getClass()
              + " - no update possible");
    }
    SQLStatement stat = new SQLStatement(db);
    StatementBuilder buff = new StatementBuilder("MERGE INTO ");
    buff.append(db.getDialect().getTableName(schemaName, tableName)).append(" (");
    buff.resetCount();
    for (FieldDefinition field : fields) {
      buff.appendExceptFirst(", ");
      buff.append(field.columnName);
    }
    buff.append(") KEY(");
    buff.resetCount();
    for (FieldDefinition field : fields) {
      if (field.isPrimaryKey) {
        buff.appendExceptFirst(", ");
        buff.append(field.columnName);
      }
    }
    buff.append(") ");
    buff.resetCount();
    buff.append("VALUES (");
    for (FieldDefinition field : fields) {
      buff.appendExceptFirst(", ");
      buff.append('?');
      Object value = getValue(obj, field);
      stat.addParameter(value);
    }
    buff.append(')');
    stat.setSQL(buff.toString());
    StatementLogger.merge(stat.getSQL());
    stat.executeUpdate();
  }

  void update(Db db, Object obj) {
    if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
      throw new IllegalStateException(
          "No primary key columns defined "
              + "for table "
              + obj.getClass()
              + " - no update possible");
    }
    SQLStatement stat = new SQLStatement(db);
    StatementBuilder buff = new StatementBuilder("UPDATE ");
    buff.append(db.getDialect().getTableName(schemaName, tableName)).append(" SET ");
    buff.resetCount();

    for (FieldDefinition field : fields) {
      if (!field.isPrimaryKey) {
        buff.appendExceptFirst(", ");
        buff.append(field.columnName);
        buff.append(" = ?");
        Object value = getValue(obj, field);
        stat.addParameter(value);
      }
    }
    Object alias = ClassUtils.newObject(obj.getClass());
    Query<Object> query = Query.from(db, alias);
    boolean firstCondition = true;
    for (FieldDefinition field : fields) {
      if (field.isPrimaryKey) {
        Object aliasValue = field.getValue(alias);
        Object value = field.getValue(obj);
        if (!firstCondition) {
          query.addConditionToken(ConditionAndOr.AND);
        }
        firstCondition = false;
        query.addConditionToken(new Condition<Object>(aliasValue, value, CompareType.EQUAL));
      }
    }
    stat.setSQL(buff.toString());
    query.appendWhere(stat);
    StatementLogger.update(stat.getSQL());
    stat.executeUpdate();
  }

  void delete(Db db, Object obj) {
    if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
      throw new IllegalStateException(
          "No primary key columns defined "
              + "for table "
              + obj.getClass()
              + " - no update possible");
    }
    SQLStatement stat = new SQLStatement(db);
    StatementBuilder buff = new StatementBuilder("DELETE FROM ");
    buff.append(db.getDialect().getTableName(schemaName, tableName));
    buff.resetCount();
    Object alias = ClassUtils.newObject(obj.getClass());
    Query<Object> query = Query.from(db, alias);
    boolean firstCondition = true;
    for (FieldDefinition field : fields) {
      if (field.isPrimaryKey) {
        Object aliasValue = field.getValue(alias);
        Object value = field.getValue(obj);
        if (!firstCondition) {
          query.addConditionToken(ConditionAndOr.AND);
        }
        firstCondition = false;
        query.addConditionToken(new Condition<Object>(aliasValue, value, CompareType.EQUAL));
      }
    }
    stat.setSQL(buff.toString());
    query.appendWhere(stat);
    StatementLogger.delete(stat.getSQL());
    stat.executeUpdate();
  }

  TableDefinition<T> createTableIfRequired(Db db) {
    if (!createTableIfRequired) {
      // skip table and index creation
      // but still check for upgrades
      db.upgradeTable(this);
      return this;
    }
    SQLDialect dialect = db.getDialect();
    SQLStatement stat = new SQLStatement(db);
    StatementBuilder buff;
    if (memoryTable && dialect.supportsMemoryTables()) {
      buff = new StatementBuilder("CREATE MEMORY TABLE IF NOT EXISTS ");
    } else {
      buff = new StatementBuilder("CREATE TABLE IF NOT EXISTS ");
    }

    buff.append(dialect.getTableName(schemaName, tableName)).append('(');

    for (FieldDefinition field : fields) {
      buff.appendExceptFirst(", ");
      buff.append(field.columnName).append(' ').append(field.dataType);
      if (field.maxLength > 0) {
        buff.append('(').append(field.maxLength).append(')');
      }

      if (field.isAutoIncrement) {
        buff.append(" AUTO_INCREMENT");
      }

      if (!field.allowNull) {
        buff.append(" NOT NULL");
      }

      // default values
      if (!field.isAutoIncrement && !field.isPrimaryKey) {
        String dv = field.defaultValue;
        if (!StringUtils.isNullOrEmpty(dv)) {
          if (ModelUtils.isProperlyFormattedDefaultValue(dv)
              && ModelUtils.isValidDefaultValue(field.field.getType(), dv)) {
            buff.append(" DEFAULT " + dv);
          }
        }
      }
    }

    // primary key
    if (primaryKeyColumnNames != null && primaryKeyColumnNames.size() > 0) {
      buff.append(", PRIMARY KEY(");
      buff.resetCount();
      for (String n : primaryKeyColumnNames) {
        buff.appendExceptFirst(", ");
        buff.append(n);
      }
      buff.append(')');
    }
    buff.append(')');
    stat.setSQL(buff.toString());
    StatementLogger.create(stat.getSQL());
    stat.executeUpdate();

    // create indexes
    for (IndexDefinition index : indexes) {
      String sql = db.getDialect().getCreateIndex(schemaName, tableName, index);
      stat.setSQL(sql);
      StatementLogger.create(stat.getSQL());
      stat.executeUpdate();
    }

    // tables are created using IF NOT EXISTS
    // but we may still need to upgrade
    db.upgradeTable(this);
    return this;
  }

  /**
   * Retrieve list of columns from index definition.
   *
   * @param index the index columns, separated by space
   * @return the column list
   */
  private static List<String> getColumns(String index) {
    List<String> cols = New.arrayList();
    if (index == null || index.length() == 0) {
      return null;
    }
    String[] cs = index.split("(,|\\s)");
    for (String c : cs) {
      if (c != null && c.trim().length() > 0) {
        cols.add(c.trim());
      }
    }
    if (cols.size() == 0) {
      return null;
    }
    return cols;
  }

  void mapObject(Object obj) {
    fieldMap.clear();
    initObject(obj, fieldMap);

    if (clazz.isAnnotationPresent(JQSchema.class)) {
      JQSchema schemaAnnotation = clazz.getAnnotation(JQSchema.class);
      // setup schema name mapping, if properly annotated
      if (!StringUtils.isNullOrEmpty(schemaAnnotation.name())) {
        schemaName = schemaAnnotation.name();
      }
    }

    if (clazz.isAnnotationPresent(JQTable.class)) {
      JQTable tableAnnotation = clazz.getAnnotation(JQTable.class);

      // setup table name mapping, if properly annotated
      if (!StringUtils.isNullOrEmpty(tableAnnotation.name())) {
        tableName = tableAnnotation.name();
      }

      // allow control over createTableIfRequired()
      createTableIfRequired = tableAnnotation.createIfRequired();

      // model version
      if (tableAnnotation.version() > 0) {
        tableVersion = tableAnnotation.version();
      }

      // setup the primary index, if properly annotated
      List<String> primaryKey = getColumns(tableAnnotation.primaryKey());
      if (primaryKey != null) {
        setPrimaryKey(primaryKey);
      }
    }

    if (clazz.isAnnotationPresent(JQIndex.class)) {
      JQIndex indexAnnotation = clazz.getAnnotation(JQIndex.class);

      // setup the indexes, if properly annotated
      addIndexes(IndexType.STANDARD, indexAnnotation.standard());
      addIndexes(IndexType.UNIQUE, indexAnnotation.unique());
      addIndexes(IndexType.HASH, indexAnnotation.hash());
      addIndexes(IndexType.UNIQUE_HASH, indexAnnotation.uniqueHash());
    }
  }

  void addIndexes(IndexType type, String[] indexes) {
    for (String index : indexes) {
      List<String> validatedColumns = getColumns(index);
      if (validatedColumns == null) {
        return;
      }
      addIndex(type, validatedColumns);
    }
  }

  List<IndexDefinition> getIndexes(IndexType type) {
    List<IndexDefinition> list = New.arrayList();
    for (IndexDefinition def : indexes) {
      if (def.type.equals(type)) {
        list.add(def);
      }
    }
    return list;
  }

  void initObject(Object obj, Map<Object, FieldDefinition> map) {
    for (FieldDefinition def : fields) {
      def.initWithNewObject(obj);
      map.put(def.getValue(obj), def);
    }
  }

  void initSelectObject(SelectTable<T> table, Object obj, Map<Object, SelectColumn<T>> map) {
    for (FieldDefinition def : fields) {
      def.initWithNewObject(obj);
      SelectColumn<T> column = new SelectColumn<T>(table, def);
      map.put(def.getValue(obj), column);
    }
  }

  void readRow(Object item, ResultSet rs) {
    for (int i = 0; i < fields.size(); i++) {
      FieldDefinition def = fields.get(i);
      Object o = def.read(rs, i + 1);
      def.setValue(item, o);
    }
  }

  void appendSelectList(SQLStatement stat) {
    for (int i = 0; i < fields.size(); i++) {
      if (i > 0) {
        stat.appendSQL(", ");
      }
      FieldDefinition def = fields.get(i);
      stat.appendSQL(def.columnName);
    }
  }

  <Y, X> void appendSelectList(SQLStatement stat, Query<Y> query, X x) {
    for (int i = 0; i < fields.size(); i++) {
      if (i > 0) {
        stat.appendSQL(", ");
      }
      FieldDefinition def = fields.get(i);
      Object obj = def.getValue(x);
      query.appendSQL(stat, obj);
    }
  }

  <Y, X> void copyAttributeValues(Query<Y> query, X to, X map) {
    for (FieldDefinition def : fields) {
      Object obj = def.getValue(map);
      SelectColumn<Y> col = query.getSelectColumn(obj);
      Object value = col.getCurrentValue();
      def.setValue(to, value);
    }
  }
}
예제 #16
0
/**
 * This class represents a query.
 *
 * @param <T> the return type
 */
public class Query<T> {

  private final Db db;
  private SelectTable<T> from;
  private final ArrayList<Token> conditions = New.arrayList();
  private final ArrayList<UpdateColumn> updateColumnDeclarations = New.arrayList();
  private final ArrayList<SelectTable<?>> joins = New.arrayList();
  private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = ClassUtils.newIdentityHashMap();
  private final ArrayList<OrderExpression<T>> orderByList = New.arrayList();
  private Object[] groupByExpressions;
  private long limit;
  private long offset;

  Query(Db db) {
    this.db = db;
  }

  @SuppressWarnings("unchecked")
  static <T> Query<T> from(Db db, T alias) {
    Query<T> query = new Query<T>(db);
    TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
    query.from = new SelectTable<T>(db, query, alias, false);
    def.initSelectObject(query.from, alias, query.aliasMap);
    return query;
  }

  public long selectCount() {
    SQLStatement stat = getSelectStatement(false);
    stat.appendSQL("COUNT(*) ");
    appendFromWhere(stat);
    ResultSet rs = stat.executeQuery();
    Statement s = null;
    try {
      s = rs.getStatement();
      rs.next();
      long value = rs.getLong(1);
      return value;
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      JdbcUtils.closeSilently(rs);
      JdbcUtils.closeSilently(s);
    }
  }

  public List<T> select() {
    return select(false);
  }

  public T selectFirst() {
    return select(false).get(0);
  }

  public List<T> selectDistinct() {
    return select(true);
  }

  @SuppressWarnings("unchecked")
  public <X, Z> X selectFirst(Z x) {
    List<X> list = (List<X>) select(x);
    return list.isEmpty() ? null : list.get(0);
  }

  public String getSQL() {
    SQLStatement stat = getSelectStatement(false);
    stat.appendSQL("*");
    appendFromWhere(stat);
    return stat.getSQL().trim();
  }

  private List<T> select(boolean distinct) {
    List<T> result = New.arrayList();
    TableDefinition<T> def = from.getAliasDefinition();
    SQLStatement stat = getSelectStatement(distinct);
    def.appendSelectList(stat);
    appendFromWhere(stat);
    ResultSet rs = stat.executeQuery();
    Statement s = null;
    try {
      s = rs.getStatement();
      while (rs.next()) {
        T item = from.newObject();
        from.getAliasDefinition().readRow(item, rs);
        result.add(item);
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      JdbcUtils.closeSilently(rs);
      JdbcUtils.closeSilently(s);
    }
    return result;
  }

  public int delete() {
    SQLStatement stat = new SQLStatement(db);
    stat.appendSQL("DELETE FROM ");
    from.appendSQL(stat);
    appendWhere(stat);
    StatementLogger.delete(stat.getSQL());
    return stat.executeUpdate();
  }

  public <A> UpdateColumnSet<T, A> set(A field) {
    return new UpdateColumnSet<T, A>(this, field);
  }

  public <A> UpdateColumnIncrement<T, A> increment(A field) {
    return new UpdateColumnIncrement<T, A>(this, field);
  }

  public int update() {
    if (updateColumnDeclarations.size() == 0) {
      throw new RuntimeException("Missing set or increment call.");
    }
    SQLStatement stat = new SQLStatement(db);
    stat.appendSQL("UPDATE ");
    from.appendSQL(stat);
    stat.appendSQL(" SET ");
    int i = 0;
    for (UpdateColumn declaration : updateColumnDeclarations) {
      if (i++ > 0) {
        stat.appendSQL(", ");
      }
      declaration.appendSQL(stat);
    }
    appendWhere(stat);
    StatementLogger.update(stat.getSQL());
    return stat.executeUpdate();
  }

  public <X, Z> List<X> selectDistinct(Z x) {
    return select(x, true);
  }

  public <X, Z> List<X> select(Z x) {
    return select(x, false);
  }

  @SuppressWarnings("unchecked")
  private <X, Z> List<X> select(Z x, boolean distinct) {
    Class<?> clazz = x.getClass();
    if (ClassUtils.isSimpleType(clazz)) {
      return selectSimple((X) x, distinct);
    }
    clazz = clazz.getSuperclass();
    return select((Class<X>) clazz, (X) x, distinct);
  }

  private <X> List<X> select(Class<X> clazz, X x, boolean distinct) {
    List<X> result = New.arrayList();
    TableDefinition<X> def = db.define(clazz);
    SQLStatement stat = getSelectStatement(distinct);
    def.appendSelectList(stat, this, x);
    appendFromWhere(stat);
    ResultSet rs = stat.executeQuery();
    Statement s = null;
    try {
      s = rs.getStatement();
      while (rs.next()) {
        X row = ClassUtils.newObject(clazz);
        def.readRow(row, rs);
        result.add(row);
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      JdbcUtils.closeSilently(rs);
      JdbcUtils.closeSilently(s);
    }
    return result;
  }

  @SuppressWarnings("unchecked")
  private <X> List<X> selectSimple(X x, boolean distinct) {
    SQLStatement stat = getSelectStatement(distinct);
    appendSQL(stat, x);
    appendFromWhere(stat);
    ResultSet rs = stat.executeQuery();
    List<X> result = New.arrayList();
    Statement s = null;
    try {
      s = rs.getStatement();
      while (rs.next()) {
        try {
          X value;
          Object o = rs.getObject(1);
          int convertHereIsProbablyWrong;
          if (Clob.class.isAssignableFrom(o.getClass())) {
            value = (X) ClassUtils.convert(o, String.class);
          } else {
            value = (X) o;
          }
          result.add(value);
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    } finally {
      JdbcUtils.closeSilently(rs);
      JdbcUtils.closeSilently(s);
    }
    return result;
  }

  private SQLStatement getSelectStatement(boolean distinct) {
    SQLStatement stat = new SQLStatement(db);
    stat.appendSQL("SELECT ");
    if (distinct) {
      stat.appendSQL("DISTINCT ");
    }
    return stat;
  }

  public <A> QueryCondition<T, A> where(A x) {
    return new QueryCondition<T, A>(this, x);
  }

  public <A> QueryWhere<T> where(Filter filter) {
    HashMap<String, Object> fieldMap = New.hashMap();
    for (Field f : filter.getClass().getDeclaredFields()) {
      f.setAccessible(true);
      try {
        Object obj = f.get(filter);
        if (obj == from.getAlias()) {
          List<TableDefinition.FieldDefinition> fields = from.getAliasDefinition().getFields();
          String name = f.getName();
          for (TableDefinition.FieldDefinition field : fields) {
            String n = name + "." + field.field.getName();
            Object o = field.field.get(obj);
            fieldMap.put(n, o);
          }
        }
        fieldMap.put(f.getName(), f.get(filter));
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
    Token filterCode = new ClassReader().decompile(filter, fieldMap, "where");
    // String filterQuery = filterCode.toString();
    conditions.add(filterCode);
    return new QueryWhere<T>(this);
  }

  public QueryWhere<T> whereTrue(Boolean condition) {
    Token token = new Function("", condition);
    addConditionToken(token);
    return new QueryWhere<T>(this);
  }

  /**
   * Sets the Limit and Offset of a query.
   *
   * @return the query
   */
  public Query<T> limit(long limit) {
    this.limit = limit;
    return this;
  }

  public Query<T> offset(long offset) {
    this.offset = offset;
    return this;
  }

  /**
   * Order by a number of columns.
   *
   * @param expressions the columns
   * @return the query
   */
  public Query<T> orderBy(Object... expressions) {
    for (Object expr : expressions) {
      OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
      addOrderBy(e);
    }
    return this;
  }

  public Query<T> orderByDesc(Object expr) {
    OrderExpression<T> e = new OrderExpression<T>(this, expr, true, false, false);
    addOrderBy(e);
    return this;
  }

  public Query<T> groupBy(Object... groupBy) {
    this.groupByExpressions = groupBy;
    return this;
  }

  /**
   * INTERNAL
   *
   * @param stat the statement
   * @param x the alias object
   */
  public void appendSQL(SQLStatement stat, Object x) {
    if (x == Function.count()) {
      stat.appendSQL("COUNT(*)");
      return;
    }
    Token token = Db.getToken(x);
    if (token != null) {
      token.appendSQL(stat, this);
      return;
    }
    SelectColumn<T> col = aliasMap.get(x);
    if (col != null) {
      col.appendSQL(stat);
      return;
    }
    stat.appendSQL("?");
    stat.addParameter(x);
  }

  void addConditionToken(Token condition) {
    conditions.add(condition);
  }

  void addUpdateColumnDeclaration(UpdateColumn declaration) {
    updateColumnDeclarations.add(declaration);
  }

  void appendWhere(SQLStatement stat) {
    if (!conditions.isEmpty()) {
      stat.appendSQL(" WHERE ");
      for (Token token : conditions) {
        token.appendSQL(stat, this);
        stat.appendSQL(" ");
      }
    }
  }

  @SuppressWarnings("unchecked")
  void appendFromWhere(SQLStatement stat) {
    stat.appendSQL(" FROM ");
    from.appendSQL(stat);
    for (SelectTable join : joins) {
      join.appendSQLAsJoin(stat, this);
    }
    appendWhere(stat);
    if (groupByExpressions != null) {
      stat.appendSQL(" GROUP BY ");
      int i = 0;
      for (Object obj : groupByExpressions) {
        if (i++ > 0) {
          stat.appendSQL(", ");
        }
        appendSQL(stat, obj);
        stat.appendSQL(" ");
      }
    }
    if (!orderByList.isEmpty()) {
      stat.appendSQL(" ORDER BY ");
      int i = 0;
      for (OrderExpression<T> o : orderByList) {
        if (i++ > 0) {
          stat.appendSQL(", ");
        }
        o.appendSQL(stat);
        stat.appendSQL(" ");
      }
    }
    if (limit > 0) {
      db.getDialect().appendLimit(stat, limit);
    }
    if (offset > 0) {
      db.getDialect().appendOffset(stat, offset);
    }
    StatementLogger.select(stat.getSQL());
  }

  /**
   * Join another table.
   *
   * @param alias an alias for the table to join
   * @return the joined query
   */
  @SuppressWarnings("unchecked")
  public <U> QueryJoin innerJoin(U alias) {
    TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
    SelectTable<T> join = new SelectTable(db, this, alias, false);
    def.initSelectObject(join, alias, aliasMap);
    joins.add(join);
    return new QueryJoin(this, join);
  }

  Db getDb() {
    return db;
  }

  boolean isJoin() {
    return !joins.isEmpty();
  }

  SelectColumn<T> getSelectColumn(Object obj) {
    return aliasMap.get(obj);
  }

  void addOrderBy(OrderExpression<T> expr) {
    orderByList.add(expr);
  }
}