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(); }
@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; }
@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); }
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]); } }); }
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"); } }); }
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); } }
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(")"); } }); }
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); } } }); }
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; }
public static Long count(Object x) { return Db.registerToken(ClassUtils.newObject(Long.class), new Function("COUNT", x)); }
@SuppressWarnings("unchecked") public static <T extends Number> T sum(T x) { return (T) Db.registerToken(ClassUtils.newObject(x.getClass()), new Function("SUM", x)); }
public static Integer length(Object x) { return Db.registerToken(ClassUtils.newObject(Integer.class), new Function("LENGTH", x)); }
@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)); }
void initWithNewObject(Object obj) { Object o = ClassUtils.newObject(field.getType()); setValue(obj, o); }
/** * 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); } } }
/** * 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); } }