123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092 |
- /*
- * Copyright 2004-2011 H2 Group.
- * Copyright 2011 James Moger.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- package com.iciql;
-
- import com.iciql.Iciql.DataTypeAdapter;
- import com.iciql.Iciql.EnumType;
- import com.iciql.NestedConditions.And;
- import com.iciql.NestedConditions.Or;
- import com.iciql.bytecode.ClassReader;
- import com.iciql.util.IciqlLogger;
- import com.iciql.util.JdbcUtils;
- import com.iciql.util.Utils;
-
- import java.lang.reflect.Field;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.IdentityHashMap;
- import java.util.List;
-
- /**
- * This class represents a query.
- *
- * @param <T> the return type
- */
-
- public class Query<T> {
-
- private Db db;
- private SelectTable<T> from;
- private ArrayList<Token> conditions = Utils.newArrayList();
- private ArrayList<UpdateColumn> updateColumnDeclarations = Utils.newArrayList();
- private int conditionDepth = 0;
- private ArrayList<SelectTable<T>> joins = Utils.newArrayList();
- private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
- private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
- private ArrayList<Object> groupByExpressions = Utils.newArrayList();
- private long limit;
- private long offset;
-
- private Query(Db db) {
- this.db = db;
- }
-
- /**
- * from() is a static factory method to build a Query object.
- *
- * @param db
- * @param alias
- * @return a query object
- */
- @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, false);
- return query;
- }
-
- @SuppressWarnings("unchecked")
- static <T> Query<T> rebuild(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, true);
- return query;
- }
-
- public long selectCount() {
- SQLStatement stat = getSelectStatement(false);
- stat.appendSQL("COUNT(*) ");
- appendFromWhere(stat);
- ResultSet rs = stat.executeQuery();
- try {
- rs.next();
- long value = rs.getLong(1);
- return value;
- } catch (SQLException e) {
- throw IciqlException.fromSQL(stat.getSQL(), e);
- } finally {
- JdbcUtils.closeSilently(rs, true);
- }
- }
-
- public List<T> select() {
- return select(false);
- }
-
- public T selectFirst() {
- List<T> list = limit(1).select(false);
- return list.isEmpty() ? null : list.get(0);
- }
-
- public List<T> selectDistinct() {
- return select(true);
- }
-
- public <X, Z> X selectFirst(Z x) {
- List<X> list = limit(1).select(x);
- return list.isEmpty() ? null : list.get(0);
- }
-
- public <X> void createView(Class<X> viewClass) {
- TableDefinition<X> viewDef = db.define(viewClass);
-
- SQLStatement fromWhere = new SQLStatement(db);
- appendFromWhere(fromWhere, false);
-
- SQLStatement stat = new SQLStatement(db);
- db.getDialect().prepareCreateView(stat, viewDef, fromWhere.toSQL());
- IciqlLogger.create(stat.toSQL());
- stat.execute();
- }
-
- public <X> void replaceView(Class<X> viewClass) {
- db.dropView(viewClass);
- createView(viewClass);
- }
-
- public String getSQL() {
- SQLStatement stat = getSelectStatement(false);
- stat.appendSQL("*");
- appendFromWhere(stat);
- return stat.getSQL().trim();
- }
-
- /**
- * toSQL returns a static string version of the query with runtime variables
- * properly encoded. This method is also useful when combined with the where
- * clause methods like isParameter() or atLeastParameter() which allows
- * iciql to generate re-usable parameterized string statements.
- *
- * @return the sql query as plain text
- */
- public String toSQL() {
- return toSQL(false);
- }
-
- /**
- * toSQL returns a static string version of the query with runtime variables
- * properly encoded. This method is also useful when combined with the where
- * clause methods like isParameter() or atLeastParameter() which allows
- * iciql to generate re-usable parameterized string statements.
- *
- * @param distinct if true SELECT DISTINCT is used for the query
- * @return the sql query as plain text
- */
- public String toSQL(boolean distinct) {
- return toSQL(distinct, null);
- }
-
- /**
- * toSQL returns a static string version of the query with runtime variables
- * properly encoded. This method is also useful when combined with the where
- * clause methods like isParameter() or atLeastParameter() which allows
- * iciql to generate re-usable parameterized string statements.
- *
- * @param distinct if true SELECT DISTINCT is used for the query
- * @param k k is used to select only the columns of the specified alias
- * for an inner join statement. An example of a generated
- * statement is: SELECT DISTINCT t1.* FROM sometable AS t1 INNER
- * JOIN othertable AS t2 ON t1.id = t2.id WHERE t2.flag = true
- * without the alias parameter the statement would start with
- * SELECT DISTINCT * FROM...
- * @return the sql query as plain text
- */
- public <K> String toSQL(boolean distinct, K k) {
- SQLStatement stat = new SQLStatement(getDb());
- if (updateColumnDeclarations.size() > 0) {
- 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);
- } else {
- stat.appendSQL("SELECT ");
- if (distinct) {
- stat.appendSQL("DISTINCT ");
- }
- if (k != null) {
- SelectTable<?> sel = getSelectTable(k);
- if (sel == null) {
- // unknown alias, use wildcard
- IciqlLogger.warn("Alias {0} is not defined in the statement!", k.getClass());
- stat.appendSQL("*");
- } else if (isJoin()) {
- // join query, use AS alias
- String as = sel.getAs();
- stat.appendSQL(as + ".*");
- } else {
- // schema.table.*
- String schema = sel.getAliasDefinition().schemaName;
- String table = sel.getAliasDefinition().tableName;
- String as = getDb().getDialect().prepareTableName(schema, table);
- stat.appendSQL(as + ".*");
- }
- } else {
- // alias unspecified, use wildcard
- stat.appendSQL("*");
- }
- appendFromWhere(stat);
- }
- return stat.toSQL().trim();
- }
-
- <Z> String toSubQuery(Z z) {
- SQLStatement stat = getSelectStatement(false);
- SelectColumn<T> col = aliasMap.get(z);
- String columnName = col.getFieldDefinition().columnName;
- stat.appendColumn(columnName);
- appendFromWhere(stat);
- return stat.toSQL();
- }
-
- private List<T> select(boolean distinct) {
- List<T> result = Utils.newArrayList();
- TableDefinition<T> def = from.getAliasDefinition();
- SQLStatement stat = getSelectStatement(distinct);
- def.appendSelectList(stat);
- appendFromWhere(stat);
- ResultSet rs = stat.executeQuery();
- try {
- // SQLite returns pre-closed ResultSets for query results with 0 rows
- if (!rs.isClosed()) {
- int[] columns = def.mapColumns(db.getDialect(), false, rs);
- while (rs.next()) {
- T item = from.newObject();
- def.readRow(db.getDialect(), item, rs, columns);
- result.add(item);
- }
- }
- } catch (SQLException e) {
- throw IciqlException.fromSQL(stat.getSQL(), e);
- } finally {
- JdbcUtils.closeSilently(rs, true);
- }
- return result;
- }
-
- public int delete() {
- SQLStatement stat = new SQLStatement(db);
- stat.appendSQL("DELETE FROM ");
- from.appendSQL(stat);
- appendWhere(stat);
- IciqlLogger.delete(stat.getSQL());
- return stat.executeUpdate();
- }
-
- public <A> Query<T> setNull(A field) {
- return set(field).to(null);
- }
-
- public <A> UpdateColumnSet<T, A> set(A field) {
- from.getAliasDefinition().checkMultipleEnums(field);
- return new UpdateColumnSet<T, A>(this, field);
- }
-
- public UpdateColumnSet<T, Boolean> set(boolean field) {
- from.getAliasDefinition().checkMultipleBooleans();
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Byte> set(byte field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Short> set(short field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Integer> set(int field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Long> set(long field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Float> set(float field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Double> set(double field) {
- return setPrimitive(field);
- }
-
- private <A> UpdateColumnSet<T, A> setPrimitive(A field) {
- A alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- // this will result in an unmapped field exception
- return set(field);
- }
- return set(alias);
- }
-
- public <A> UpdateColumnIncrement<T, A> increment(A field) {
- return new UpdateColumnIncrement<T, A>(this, field);
- }
-
- public UpdateColumnIncrement<T, Byte> increment(byte field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Short> increment(short field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Integer> increment(int field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Long> increment(long field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Float> increment(float field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Double> increment(double field) {
- return incrementPrimitive(field);
- }
-
- private <A> UpdateColumnIncrement<T, A> incrementPrimitive(A field) {
- A alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- // this will result in an unmapped field exception
- return increment(field);
- }
- return increment(alias);
- }
-
- public int update() {
- if (updateColumnDeclarations.size() == 0) {
- throw new IciqlException("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);
- IciqlLogger.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 (Db.isToken(x)) {
- // selecting a function
- return selectFunction((X) x, distinct);
- } else {
- // selecting a column
- SelectColumn<T> col = getColumnByReference(x);
- if (col == null) {
- col = getColumnByReference(getPrimitiveAliasByValue(x));
- }
- if (col != null) {
- return (List<X>) selectColumn(col, clazz, distinct);
- }
- }
-
- // selecting into a new object type
- Class<?> enclosingClass = clazz.getEnclosingClass();
- if (enclosingClass != null) {
- // anonymous inner class
- 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 = Utils.newArrayList();
- TableDefinition<X> def = db.define(clazz);
- SQLStatement stat = getSelectStatement(distinct);
- def.appendSelectList(stat, this, x);
- appendFromWhere(stat);
- ResultSet rs = stat.executeQuery();
- try {
- // SQLite returns pre-closed ResultSets for query results with 0 rows
- if (!rs.isClosed()) {
- int[] columns = def.mapColumns(db.getDialect(), false, rs);
- while (rs.next()) {
- X row = Utils.newObject(clazz);
- def.readRow(db.getDialect(), row, rs, columns);
- result.add(row);
- }
- }
- } catch (SQLException e) {
- throw IciqlException.fromSQL(stat.getSQL(), e);
- } finally {
- JdbcUtils.closeSilently(rs, true);
- }
- return result;
- }
-
- @SuppressWarnings("unchecked")
- private <X> List<X> selectFunction(X x, boolean distinct) {
- SQLStatement stat = getSelectStatement(distinct);
- appendSQL(stat, null, x);
- appendFromWhere(stat);
- ResultSet rs = stat.executeQuery();
- List<X> result = Utils.newArrayList();
- try {
- // SQLite returns pre-closed ResultSets for query results with 0 rows
- if (!rs.isClosed()) {
- while (rs.next()) {
- X value = (X) rs.getObject(1);
- result.add(value);
- }
- }
- } catch (Exception e) {
- throw IciqlException.fromSQL(stat.getSQL(), e);
- } finally {
- JdbcUtils.closeSilently(rs, true);
- }
- return result;
- }
-
- @SuppressWarnings("unchecked")
- private <X> List<X> selectColumn(SelectColumn<T> col, Class<X> clazz, boolean distinct) {
- SQLStatement stat = getSelectStatement(distinct);
- col.appendSQL(stat);
- appendFromWhere(stat);
- ResultSet rs = stat.executeQuery();
- List<X> result = Utils.newArrayList();
- Class<? extends DataTypeAdapter<?>> typeAdapter = col.getFieldDefinition().typeAdapter;
- try {
- // SQLite returns pre-closed ResultSets for query results with 0 rows
- if (!rs.isClosed()) {
- while (rs.next()) {
- X value = (X) db.getDialect().deserialize(rs, 1, clazz, typeAdapter);
- result.add(value);
- }
- }
- } catch (Exception e) {
- throw IciqlException.fromSQL(stat.getSQL(), e);
- } finally {
- JdbcUtils.closeSilently(rs, true);
- }
- return result;
- }
-
- private SQLStatement getSelectStatement(boolean distinct) {
- SQLStatement stat = new SQLStatement(db);
- stat.appendSQL("SELECT ");
- if (distinct) {
- stat.appendSQL("DISTINCT ");
- }
- return stat;
- }
-
- /**
- * Begin a primitive boolean field condition clause.
- *
- * @param x the primitive boolean field to query
- * @return a query condition to continue building the condition
- */
- public QueryCondition<T, Boolean> where(boolean x) {
- from.getAliasDefinition().checkMultipleBooleans();
- return wherePrimitive(x);
- }
-
- /**
- * Begin a primitive short field condition clause.
- *
- * @param x the primitive short field to query
- * @return a query condition to continue building the condition
- */
- public QueryCondition<T, Byte> where(byte x) {
- return wherePrimitive(x);
- }
-
- /**
- * Begin a primitive short field condition clause.
- *
- * @param x the primitive short field to query
- * @return a query condition to continue building the condition
- */
- public QueryCondition<T, Short> where(short x) {
- return wherePrimitive(x);
- }
-
- /**
- * Begin a primitive int field condition clause.
- *
- * @param x the primitive int field to query
- * @return a query condition to continue building the condition
- */
- public QueryCondition<T, Integer> where(int x) {
- return wherePrimitive(x);
- }
-
- /**
- * Begin a primitive long field condition clause.
- *
- * @param x the primitive long field to query
- * @return a query condition to continue building the condition
- */
- public QueryCondition<T, Long> where(long x) {
- return wherePrimitive(x);
- }
-
- /**
- * Begin a primitive float field condition clause.
- *
- * @param x the primitive float field to query
- * @return a query condition to continue building the condition
- */
- public QueryCondition<T, Float> where(float x) {
- return wherePrimitive(x);
- }
-
- /**
- * Begin a primitive double field condition clause.
- *
- * @param x the primitive double field to query
- * @return a query condition to continue building the condition
- */
- public QueryCondition<T, Double> where(double x) {
- return wherePrimitive(x);
- }
-
- /**
- * Begins a primitive field condition clause.
- *
- * @param value
- * @return a query condition to continue building the condition
- */
- private <A> QueryCondition<T, A> wherePrimitive(A value) {
- A alias = getPrimitiveAliasByValue(value);
- if (alias == null) {
- // this will result in an unmapped field exception
- return where(value);
- }
- return where(alias);
- }
-
- /**
- * Begin an Object field condition clause.
- *
- * @param x the mapped object to query
- * @return a query condition to continue building the condition
- */
- public <A> QueryCondition<T, A> where(A x) {
- from.getAliasDefinition().checkMultipleEnums(x);
- return new QueryCondition<T, A>(this, x);
- }
-
- public <A> QueryWhere<T> where(Filter filter) {
- HashMap<String, Object> fieldMap = Utils.newHashMap();
- 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 IciqlException(e);
- }
- }
- Token filterCode = new ClassReader().decompile(filter, fieldMap, "where");
- // String filterQuery = filterCode.toString();
- conditions.add(filterCode);
- return new QueryWhere<T>(this);
- }
-
- public QueryWhere<T> where(String fragment, List<?> args) {
- return this.where(fragment, args.toArray());
- }
-
- public QueryWhere<T> where(String fragment, Object... args) {
- conditions.add(new RuntimeToken(fragment, args));
- return new QueryWhere<T>(this);
- }
-
- public Query<T> where(And<T> conditions) {
- whereTrue();
- addConditionToken(conditions.where.query);
- return this;
- }
-
- public Query<T> where(Or<T> conditions) {
- whereFalse();
- addConditionToken(conditions.where.query);
- return this;
- }
-
- public QueryWhere<T> whereTrue() {
- return whereTrue(true);
- }
-
- public QueryWhere<T> whereFalse() {
- return whereTrue(false);
- }
-
- 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;
- }
-
- public Query<T> orderBy(boolean field) {
- from.getAliasDefinition().checkMultipleBooleans();
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(byte field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(short field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(int field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(long field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(float field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(double field) {
- return orderByPrimitive(field);
- }
-
- Query<T> orderByPrimitive(Object field) {
- Object alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- return orderBy(field);
- }
- return orderBy(alias);
- }
-
- public Query<T> orderBy(Object expr) {
- from.getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
- addOrderBy(e);
- 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) {
- from.getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
- addOrderBy(e);
- }
- return this;
- }
-
- public Query<T> orderByDesc(byte field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(short field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(int field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(long field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(float field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(double field) {
- return orderByDescPrimitive(field);
- }
-
- Query<T> orderByDescPrimitive(Object field) {
- Object alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- return orderByDesc(field);
- }
- return orderByDesc(alias);
- }
-
- 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(boolean field) {
- from.getAliasDefinition().checkMultipleBooleans();
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(byte field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(short field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(int field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(long field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(float field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(double field) {
- return groupByPrimitive(field);
- }
-
- Query<T> groupByPrimitive(Object field) {
- Object alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- return groupBy(field);
- }
- return groupBy(alias);
- }
-
- public Query<T> groupBy(Object expr) {
- from.getAliasDefinition().checkMultipleEnums(expr);
- groupByExpressions.add(expr);
- return this;
- }
-
- public Query<T> groupBy(Object... groupBy) {
- this.groupByExpressions.addAll(Arrays.asList(groupBy));
- return this;
- }
-
- /**
- * INTERNAL
- *
- * @param stat the statement
- * @param alias the alias object (can be null)
- * @param value the value
- */
- public void appendSQL(SQLStatement stat, Object alias, Object value) {
- if (Function.count() == value) {
- stat.appendSQL("COUNT(*)");
- return;
- }
- if (RuntimeParameter.PARAMETER == value) {
- stat.appendSQL("?");
- addParameter(stat, alias, value);
- return;
- }
- Token token = Db.getToken(value);
- if (token != null) {
- token.appendSQL(stat, this);
- return;
- }
- if (alias != null && value != null && value.getClass().isEnum()) {
- // special case:
- // value is first enum constant which is also the alias object.
- // the first enum constant is used as the alias because we can not
- // instantiate an enum reflectively.
- stat.appendSQL("?");
- addParameter(stat, alias, value);
- return;
- }
- SelectColumn<T> col = getColumnByReference(value);
- if (col != null) {
- col.appendSQL(stat);
- return;
- }
- stat.appendSQL("?");
- addParameter(stat, alias, value);
- }
-
- /**
- * INTERNAL
- *
- * @param stat the statement
- * @param alias the alias object (can be null)
- * @param valueLeft the value on the left of the compound clause
- * @param valueRight the value on the right of the compound clause
- * @param compareType the current compare type (e.g. BETWEEN)
- */
- public void appendSQL(SQLStatement stat, Object alias, Object valueLeft, Object valueRight,
- CompareType compareType) {
- stat.appendSQL("?");
- stat.appendSQL(" ");
- switch (compareType) {
- case BETWEEN:
- stat.appendSQL("AND");
- break;
- }
- stat.appendSQL(" ");
- stat.appendSQL("?");
- addParameter(stat, alias, valueLeft);
- addParameter(stat, alias, valueRight);
- }
-
- public void appendSQL(SQLStatement stat, Object alias, Iterable<Object> values,
- CompareType compareType) {
- boolean first = true;
- stat.appendSQL("(");
- for (Object value : values) {
- if (first) {
- first = false;
- } else {
- stat.appendSQL(", ");
- }
- stat.appendSQL("?");
- addParameter(stat, alias, value);
- }
- stat.appendSQL(")");
- }
-
- private void addParameter(SQLStatement stat, Object alias, Object value) {
- SelectColumn<T> col = getColumnByReference(alias);
- if (col != null && value != null && value.getClass().isEnum()) {
- // enum
- TableDefinition.FieldDefinition field = col.getFieldDefinition();
- EnumType type = field.enumType;
- Enum<?> anEnum = (Enum<?>) value;
- Object y = Utils.convertEnum(anEnum, type);
- stat.addParameter(y);
- } else if (col != null) {
- // object
- TableDefinition.FieldDefinition field = col.getFieldDefinition();
- Class<? extends DataTypeAdapter<?>> typeAdapter = field.typeAdapter;
- if (value != null && value instanceof String) {
- if (field.trim && field.length > 0) {
- // clip strings (issue-15)
- String s = (String) value;
- if (s.length() > field.length) {
- value = s.substring(0, field.length);
- }
- }
- }
- Object parameter = db.getDialect().serialize(value, typeAdapter);
- stat.addParameter(parameter);
- } else {
- // primitive
- stat.addParameter(value);
- }
- }
-
- void addConditionToken(Token condition) {
- if (condition == ConditionOpenClose.OPEN) {
- conditionDepth++;
- } else if (condition == ConditionOpenClose.CLOSE) {
- conditionDepth--;
- if (conditionDepth < 0) {
- throw new IciqlException("unmatch condition open-close count");
- }
- }
- conditions.add(condition);
- }
-
- void addConditionToken(Query<T> other) {
- for (Token condition : other.conditions) {
- addConditionToken(condition);
- }
- }
-
- void addUpdateColumnDeclaration(UpdateColumn declaration) {
- updateColumnDeclarations.add(declaration);
- }
-
- void appendWhere(SQLStatement stat) {
- if (conditionDepth != 0) {
- throw new IciqlException("unmatch condition open-close count");
- }
- if (!conditions.isEmpty()) {
- stat.appendSQL(" WHERE ");
-
- boolean skipNextConjunction = false;
-
- for (Token token : conditions) {
-
- if (skipNextConjunction && token instanceof ConditionAndOr) {
- skipNextConjunction = false;
- continue;
- }
-
- token.appendSQL(stat, this);
- stat.appendSQL(" ");
-
- if (ConditionOpenClose.OPEN == token) {
- skipNextConjunction = true;
- }
- }
- }
- }
-
- void appendFromWhere(SQLStatement stat) {
- appendFromWhere(stat, true);
- }
-
- void appendFromWhere(SQLStatement stat, boolean log) {
- stat.appendSQL(" FROM ");
- from.appendSQL(stat);
- for (SelectTable<T> join : joins) {
- join.appendSQLAsJoin(stat, this);
- }
- appendWhere(stat);
- if (!groupByExpressions.isEmpty()) {
- stat.appendSQL(" GROUP BY ");
- int i = 0;
- for (Object obj : groupByExpressions) {
- if (i++ > 0) {
- stat.appendSQL(", ");
- }
- appendSQL(stat, null, 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(" ");
- }
- }
- db.getDialect().appendLimitOffset(stat, limit, offset);
- if (log) {
- IciqlLogger.select(stat.getSQL());
- }
- }
-
- /**
- * Join another table.
- *
- * @param alias an alias for the table to join
- * @return the joined query
- */
-
- public <A> QueryJoin<T> innerJoin(A alias) {
- return join(alias, false);
- }
-
- public <A> QueryJoin<T> leftJoin(A alias) {
- return join(alias, true);
- }
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- private <A> QueryJoin<T> join(A alias, boolean outerJoin) {
- TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
- SelectTable<T> join = new SelectTable(db, this, alias, outerJoin);
- def.initSelectObject(join, alias, aliasMap, false);
- joins.add(join);
- return new QueryJoin(this, join);
- }
-
- Db getDb() {
- return db;
- }
-
- SelectTable<T> getFrom() {
- return from;
- }
-
- boolean isJoin() {
- return !joins.isEmpty();
- }
-
- SelectTable<?> getSelectTable(Object alias) {
- if (from.getAlias() == alias) {
- return from;
- } else {
- for (SelectTable<?> join : joins) {
- if (join.getAlias() == alias) {
- return join;
- }
- }
- }
- return null;
- }
-
- /**
- * This method returns a mapped Object field by its reference.
- *
- * @param obj
- * @return
- */
- private SelectColumn<T> getColumnByReference(Object obj) {
- SelectColumn<T> col = aliasMap.get(obj);
- return col;
- }
-
- /**
- * This method returns the alias of a mapped primitive field by its value.
- *
- * @param obj
- * @return
- */
- @SuppressWarnings("unchecked")
- <A> A getPrimitiveAliasByValue(A obj) {
- for (Object alias : aliasMap.keySet()) {
- if (alias.equals(obj)) {
- SelectColumn<T> match = aliasMap.get(alias);
- if (match.getFieldDefinition().isPrimitive) {
- return (A) alias;
- }
- }
- }
- return null;
- }
-
- void addOrderBy(OrderExpression<T> expr) {
- orderByList.add(expr);
- }
-
- }
|