You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Query.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. /*
  2. * Copyright 2004-2011 H2 Group.
  3. * Copyright 2011 James Moger.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package com.iciql;
  18. import java.lang.reflect.Field;
  19. import java.sql.ResultSet;
  20. import java.sql.SQLException;
  21. import java.util.ArrayList;
  22. import java.util.Arrays;
  23. import java.util.HashMap;
  24. import java.util.IdentityHashMap;
  25. import java.util.List;
  26. import com.iciql.Iciql.DataTypeAdapter;
  27. import com.iciql.Iciql.EnumType;
  28. import com.iciql.NestedConditions.And;
  29. import com.iciql.NestedConditions.Or;
  30. import com.iciql.bytecode.ClassReader;
  31. import com.iciql.util.IciqlLogger;
  32. import com.iciql.util.JdbcUtils;
  33. import com.iciql.util.Utils;
  34. /**
  35. * This class represents a query.
  36. *
  37. * @param <T>
  38. * the return type
  39. */
  40. public class Query<T> {
  41. private Db db;
  42. private SelectTable<T> from;
  43. private ArrayList<Token> conditions = Utils.newArrayList();
  44. private ArrayList<UpdateColumn> updateColumnDeclarations = Utils.newArrayList();
  45. private int conditionDepth = 0;
  46. private ArrayList<SelectTable<T>> joins = Utils.newArrayList();
  47. private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
  48. private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
  49. private ArrayList<Object> groupByExpressions = Utils.newArrayList();
  50. private long limit;
  51. private long offset;
  52. private Query(Db db) {
  53. this.db = db;
  54. }
  55. /**
  56. * from() is a static factory method to build a Query object.
  57. *
  58. * @param db
  59. * @param alias
  60. * @return a query object
  61. */
  62. @SuppressWarnings("unchecked")
  63. static <T> Query<T> from(Db db, T alias) {
  64. Query<T> query = new Query<T>(db);
  65. TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
  66. query.from = new SelectTable<T>(db, query, alias, false);
  67. def.initSelectObject(query.from, alias, query.aliasMap, false);
  68. return query;
  69. }
  70. @SuppressWarnings("unchecked")
  71. static <T> Query<T> rebuild(Db db, T alias) {
  72. Query<T> query = new Query<T>(db);
  73. TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
  74. query.from = new SelectTable<T>(db, query, alias, false);
  75. def.initSelectObject(query.from, alias, query.aliasMap, true);
  76. return query;
  77. }
  78. public long selectCount() {
  79. SQLStatement stat = getSelectStatement(false);
  80. stat.appendSQL("COUNT(*) ");
  81. appendFromWhere(stat);
  82. ResultSet rs = stat.executeQuery();
  83. try {
  84. rs.next();
  85. long value = rs.getLong(1);
  86. return value;
  87. } catch (SQLException e) {
  88. throw IciqlException.fromSQL(stat.getSQL(), e);
  89. } finally {
  90. JdbcUtils.closeSilently(rs, true);
  91. }
  92. }
  93. public List<T> select() {
  94. return select(false);
  95. }
  96. public T selectFirst() {
  97. List<T> list = limit(1).select(false);
  98. return list.isEmpty() ? null : list.get(0);
  99. }
  100. public List<T> selectDistinct() {
  101. return select(true);
  102. }
  103. public <X, Z> X selectFirst(Z x) {
  104. List<X> list = limit(1).select(x);
  105. return list.isEmpty() ? null : list.get(0);
  106. }
  107. public <X> void createView(Class<X> viewClass) {
  108. TableDefinition<X> viewDef = db.define(viewClass);
  109. SQLStatement fromWhere = new SQLStatement(db);
  110. appendFromWhere(fromWhere, false);
  111. SQLStatement stat = new SQLStatement(db);
  112. db.getDialect().prepareCreateView(stat, viewDef, fromWhere.toSQL());
  113. IciqlLogger.create(stat.toSQL());
  114. stat.execute();
  115. }
  116. public <X> void replaceView(Class<X> viewClass) {
  117. db.dropView(viewClass);
  118. createView(viewClass);
  119. }
  120. public String getSQL() {
  121. SQLStatement stat = getSelectStatement(false);
  122. stat.appendSQL("*");
  123. appendFromWhere(stat);
  124. return stat.getSQL().trim();
  125. }
  126. /**
  127. * toSQL returns a static string version of the query with runtime variables
  128. * properly encoded. This method is also useful when combined with the where
  129. * clause methods like isParameter() or atLeastParameter() which allows
  130. * iciql to generate re-usable parameterized string statements.
  131. *
  132. * @return the sql query as plain text
  133. */
  134. public String toSQL() {
  135. return toSQL(false);
  136. }
  137. /**
  138. * toSQL returns a static string version of the query with runtime variables
  139. * properly encoded. This method is also useful when combined with the where
  140. * clause methods like isParameter() or atLeastParameter() which allows
  141. * iciql to generate re-usable parameterized string statements.
  142. *
  143. * @param distinct
  144. * if true SELECT DISTINCT is used for the query
  145. * @return the sql query as plain text
  146. */
  147. public String toSQL(boolean distinct) {
  148. return toSQL(distinct, null);
  149. }
  150. /**
  151. * toSQL returns a static string version of the query with runtime variables
  152. * properly encoded. This method is also useful when combined with the where
  153. * clause methods like isParameter() or atLeastParameter() which allows
  154. * iciql to generate re-usable parameterized string statements.
  155. *
  156. * @param distinct
  157. * if true SELECT DISTINCT is used for the query
  158. * @param k
  159. * k is used to select only the columns of the specified alias
  160. * for an inner join statement. An example of a generated
  161. * statement is: SELECT DISTINCT t1.* FROM sometable AS t1 INNER
  162. * JOIN othertable AS t2 ON t1.id = t2.id WHERE t2.flag = true
  163. * without the alias parameter the statement would start with
  164. * SELECT DISTINCT * FROM...
  165. * @return the sql query as plain text
  166. */
  167. public <K> String toSQL(boolean distinct, K k) {
  168. SQLStatement stat = new SQLStatement(getDb());
  169. if (updateColumnDeclarations.size() > 0) {
  170. stat.appendSQL("UPDATE ");
  171. from.appendSQL(stat);
  172. stat.appendSQL(" SET ");
  173. int i = 0;
  174. for (UpdateColumn declaration : updateColumnDeclarations) {
  175. if (i++ > 0) {
  176. stat.appendSQL(", ");
  177. }
  178. declaration.appendSQL(stat);
  179. }
  180. appendWhere(stat);
  181. } else {
  182. stat.appendSQL("SELECT ");
  183. if (distinct) {
  184. stat.appendSQL("DISTINCT ");
  185. }
  186. if (k != null) {
  187. SelectTable<?> sel = getSelectTable(k);
  188. if (sel == null) {
  189. // unknown alias, use wildcard
  190. IciqlLogger.warn("Alias {0} is not defined in the statement!", k.getClass());
  191. stat.appendSQL("*");
  192. } else if (isJoin()) {
  193. // join query, use AS alias
  194. String as = sel.getAs();
  195. stat.appendSQL(as + ".*");
  196. } else {
  197. // schema.table.*
  198. String schema = sel.getAliasDefinition().schemaName;
  199. String table = sel.getAliasDefinition().tableName;
  200. String as = getDb().getDialect().prepareTableName(schema, table);
  201. stat.appendSQL(as + ".*");
  202. }
  203. } else {
  204. // alias unspecified, use wildcard
  205. stat.appendSQL("*");
  206. }
  207. appendFromWhere(stat);
  208. }
  209. return stat.toSQL().trim();
  210. }
  211. <Z> String toSubQuery(Z z) {
  212. SQLStatement stat = getSelectStatement(false);
  213. SelectColumn<T> col = aliasMap.get(z);
  214. String columnName = col.getFieldDefinition().columnName;
  215. stat.appendColumn(columnName);
  216. appendFromWhere(stat);
  217. return stat.toSQL();
  218. }
  219. private List<T> select(boolean distinct) {
  220. List<T> result = Utils.newArrayList();
  221. TableDefinition<T> def = from.getAliasDefinition();
  222. SQLStatement stat = getSelectStatement(distinct);
  223. def.appendSelectList(stat);
  224. appendFromWhere(stat);
  225. ResultSet rs = stat.executeQuery();
  226. try {
  227. // SQLite returns pre-closed ResultSets for query results with 0 rows
  228. if (!rs.isClosed()) {
  229. int[] columns = def.mapColumns(false, rs);
  230. while (rs.next()) {
  231. T item = from.newObject();
  232. def.readRow(db.getDialect(), item, rs, columns);
  233. result.add(item);
  234. }
  235. }
  236. } catch (SQLException e) {
  237. throw IciqlException.fromSQL(stat.getSQL(), e);
  238. } finally {
  239. JdbcUtils.closeSilently(rs, true);
  240. }
  241. return result;
  242. }
  243. public int delete() {
  244. SQLStatement stat = new SQLStatement(db);
  245. stat.appendSQL("DELETE FROM ");
  246. from.appendSQL(stat);
  247. appendWhere(stat);
  248. IciqlLogger.delete(stat.getSQL());
  249. return stat.executeUpdate();
  250. }
  251. public <A> UpdateColumnSet<T, A> set(A field) {
  252. from.getAliasDefinition().checkMultipleEnums(field);
  253. return new UpdateColumnSet<T, A>(this, field);
  254. }
  255. public UpdateColumnSet<T, Boolean> set(boolean field) {
  256. from.getAliasDefinition().checkMultipleBooleans();
  257. return setPrimitive(field);
  258. }
  259. public UpdateColumnSet<T, Byte> set(byte field) {
  260. return setPrimitive(field);
  261. }
  262. public UpdateColumnSet<T, Short> set(short field) {
  263. return setPrimitive(field);
  264. }
  265. public UpdateColumnSet<T, Integer> set(int field) {
  266. return setPrimitive(field);
  267. }
  268. public UpdateColumnSet<T, Long> set(long field) {
  269. return setPrimitive(field);
  270. }
  271. public UpdateColumnSet<T, Float> set(float field) {
  272. return setPrimitive(field);
  273. }
  274. public UpdateColumnSet<T, Double> set(double field) {
  275. return setPrimitive(field);
  276. }
  277. private <A> UpdateColumnSet<T, A> setPrimitive(A field) {
  278. A alias = getPrimitiveAliasByValue(field);
  279. if (alias == null) {
  280. // this will result in an unmapped field exception
  281. return set(field);
  282. }
  283. return set(alias);
  284. }
  285. public <A> UpdateColumnIncrement<T, A> increment(A field) {
  286. return new UpdateColumnIncrement<T, A>(this, field);
  287. }
  288. public UpdateColumnIncrement<T, Byte> increment(byte field) {
  289. return incrementPrimitive(field);
  290. }
  291. public UpdateColumnIncrement<T, Short> increment(short field) {
  292. return incrementPrimitive(field);
  293. }
  294. public UpdateColumnIncrement<T, Integer> increment(int field) {
  295. return incrementPrimitive(field);
  296. }
  297. public UpdateColumnIncrement<T, Long> increment(long field) {
  298. return incrementPrimitive(field);
  299. }
  300. public UpdateColumnIncrement<T, Float> increment(float field) {
  301. return incrementPrimitive(field);
  302. }
  303. public UpdateColumnIncrement<T, Double> increment(double field) {
  304. return incrementPrimitive(field);
  305. }
  306. private <A> UpdateColumnIncrement<T, A> incrementPrimitive(A field) {
  307. A alias = getPrimitiveAliasByValue(field);
  308. if (alias == null) {
  309. // this will result in an unmapped field exception
  310. return increment(field);
  311. }
  312. return increment(alias);
  313. }
  314. public int update() {
  315. if (updateColumnDeclarations.size() == 0) {
  316. throw new IciqlException("Missing set or increment call.");
  317. }
  318. SQLStatement stat = new SQLStatement(db);
  319. stat.appendSQL("UPDATE ");
  320. from.appendSQL(stat);
  321. stat.appendSQL(" SET ");
  322. int i = 0;
  323. for (UpdateColumn declaration : updateColumnDeclarations) {
  324. if (i++ > 0) {
  325. stat.appendSQL(", ");
  326. }
  327. declaration.appendSQL(stat);
  328. }
  329. appendWhere(stat);
  330. IciqlLogger.update(stat.getSQL());
  331. return stat.executeUpdate();
  332. }
  333. public <X, Z> List<X> selectDistinct(Z x) {
  334. return select(x, true);
  335. }
  336. public <X, Z> List<X> select(Z x) {
  337. return select(x, false);
  338. }
  339. @SuppressWarnings("unchecked")
  340. private <X, Z> List<X> select(Z x, boolean distinct) {
  341. Class<?> clazz = x.getClass();
  342. if (Utils.isSimpleType(clazz)) {
  343. return selectSimple((X) x, distinct);
  344. }
  345. Class<?> enclosingClass = clazz.getEnclosingClass();
  346. if (enclosingClass != null) {
  347. // anonymous inner class
  348. clazz = clazz.getSuperclass();
  349. }
  350. return select((Class<X>) clazz, (X) x, distinct);
  351. }
  352. private <X> List<X> select(Class<X> clazz, X x, boolean distinct) {
  353. List<X> result = Utils.newArrayList();
  354. TableDefinition<X> def = db.define(clazz);
  355. SQLStatement stat = getSelectStatement(distinct);
  356. def.appendSelectList(stat, this, x);
  357. appendFromWhere(stat);
  358. ResultSet rs = stat.executeQuery();
  359. try {
  360. // SQLite returns pre-closed ResultSets for query results with 0 rows
  361. if (!rs.isClosed()) {
  362. int[] columns = def.mapColumns(false, rs);
  363. while (rs.next()) {
  364. X row = Utils.newObject(clazz);
  365. def.readRow(db.getDialect(), row, rs, columns);
  366. result.add(row);
  367. }
  368. }
  369. } catch (SQLException e) {
  370. throw IciqlException.fromSQL(stat.getSQL(), e);
  371. } finally {
  372. JdbcUtils.closeSilently(rs, true);
  373. }
  374. return result;
  375. }
  376. @SuppressWarnings("unchecked")
  377. private <X> List<X> selectSimple(X x, boolean distinct) {
  378. SQLStatement stat = getSelectStatement(distinct);
  379. appendSQL(stat, null, x);
  380. appendFromWhere(stat);
  381. ResultSet rs = stat.executeQuery();
  382. List<X> result = Utils.newArrayList();
  383. Class<? extends DataTypeAdapter<?>> typeAdapter = Utils.getDataTypeAdapter(x.getClass().getAnnotations());
  384. try {
  385. // SQLite returns pre-closed ResultSets for query results with 0 rows
  386. if (!rs.isClosed()) {
  387. while (rs.next()) {
  388. X value = (X) db.getDialect().deserialize(rs, 1, x.getClass(), typeAdapter);
  389. result.add(value);
  390. }
  391. }
  392. } catch (Exception e) {
  393. throw IciqlException.fromSQL(stat.getSQL(), e);
  394. } finally {
  395. JdbcUtils.closeSilently(rs, true);
  396. }
  397. return result;
  398. }
  399. private SQLStatement getSelectStatement(boolean distinct) {
  400. SQLStatement stat = new SQLStatement(db);
  401. stat.appendSQL("SELECT ");
  402. if (distinct) {
  403. stat.appendSQL("DISTINCT ");
  404. }
  405. return stat;
  406. }
  407. /**
  408. * Begin a primitive boolean field condition clause.
  409. *
  410. * @param x
  411. * the primitive boolean field to query
  412. * @return a query condition to continue building the condition
  413. */
  414. public QueryCondition<T, Boolean> where(boolean x) {
  415. from.getAliasDefinition().checkMultipleBooleans();
  416. return wherePrimitive(x);
  417. }
  418. /**
  419. * Begin a primitive short field condition clause.
  420. *
  421. * @param x
  422. * the primitive short field to query
  423. * @return a query condition to continue building the condition
  424. */
  425. public QueryCondition<T, Byte> where(byte x) {
  426. return wherePrimitive(x);
  427. }
  428. /**
  429. * Begin a primitive short field condition clause.
  430. *
  431. * @param x
  432. * the primitive short field to query
  433. * @return a query condition to continue building the condition
  434. */
  435. public QueryCondition<T, Short> where(short x) {
  436. return wherePrimitive(x);
  437. }
  438. /**
  439. * Begin a primitive int field condition clause.
  440. *
  441. * @param x
  442. * the primitive int field to query
  443. * @return a query condition to continue building the condition
  444. */
  445. public QueryCondition<T, Integer> where(int x) {
  446. return wherePrimitive(x);
  447. }
  448. /**
  449. * Begin a primitive long field condition clause.
  450. *
  451. * @param x
  452. * the primitive long field to query
  453. * @return a query condition to continue building the condition
  454. */
  455. public QueryCondition<T, Long> where(long x) {
  456. return wherePrimitive(x);
  457. }
  458. /**
  459. * Begin a primitive float field condition clause.
  460. *
  461. * @param x
  462. * the primitive float field to query
  463. * @return a query condition to continue building the condition
  464. */
  465. public QueryCondition<T, Float> where(float x) {
  466. return wherePrimitive(x);
  467. }
  468. /**
  469. * Begin a primitive double field condition clause.
  470. *
  471. * @param x
  472. * the primitive double field to query
  473. * @return a query condition to continue building the condition
  474. */
  475. public QueryCondition<T, Double> where(double x) {
  476. return wherePrimitive(x);
  477. }
  478. /**
  479. * Begins a primitive field condition clause.
  480. *
  481. * @param value
  482. * @return a query condition to continue building the condition
  483. */
  484. private <A> QueryCondition<T, A> wherePrimitive(A value) {
  485. A alias = getPrimitiveAliasByValue(value);
  486. if (alias == null) {
  487. // this will result in an unmapped field exception
  488. return where(value);
  489. }
  490. return where(alias);
  491. }
  492. /**
  493. * Begin an Object field condition clause.
  494. *
  495. * @param x
  496. * the mapped object to query
  497. * @return a query condition to continue building the condition
  498. */
  499. public <A> QueryCondition<T, A> where(A x) {
  500. from.getAliasDefinition().checkMultipleEnums(x);
  501. return new QueryCondition<T, A>(this, x);
  502. }
  503. public <A> QueryWhere<T> where(Filter filter) {
  504. HashMap<String, Object> fieldMap = Utils.newHashMap();
  505. for (Field f : filter.getClass().getDeclaredFields()) {
  506. f.setAccessible(true);
  507. try {
  508. Object obj = f.get(filter);
  509. if (obj == from.getAlias()) {
  510. List<TableDefinition.FieldDefinition> fields = from.getAliasDefinition().getFields();
  511. String name = f.getName();
  512. for (TableDefinition.FieldDefinition field : fields) {
  513. String n = name + "." + field.field.getName();
  514. Object o = field.field.get(obj);
  515. fieldMap.put(n, o);
  516. }
  517. }
  518. fieldMap.put(f.getName(), f.get(filter));
  519. } catch (Exception e) {
  520. throw new IciqlException(e);
  521. }
  522. }
  523. Token filterCode = new ClassReader().decompile(filter, fieldMap, "where");
  524. // String filterQuery = filterCode.toString();
  525. conditions.add(filterCode);
  526. return new QueryWhere<T>(this);
  527. }
  528. public QueryWhere<T> where(String fragment, List<?> args) {
  529. return this.where(fragment, args.toArray());
  530. }
  531. public QueryWhere<T> where(String fragment, Object... args) {
  532. conditions.add(new RuntimeToken(fragment, args));
  533. return new QueryWhere<T>(this);
  534. }
  535. public Query<T> where(And<T> conditions) {
  536. whereTrue();
  537. addConditionToken(conditions.where.query);
  538. return this;
  539. }
  540. public Query<T> where(Or<T> conditions) {
  541. whereFalse();
  542. addConditionToken(conditions.where.query);
  543. return this;
  544. }
  545. public QueryWhere<T> whereTrue() {
  546. return whereTrue(true);
  547. }
  548. public QueryWhere<T> whereFalse() {
  549. return whereTrue(false);
  550. }
  551. public QueryWhere<T> whereTrue(Boolean condition) {
  552. Token token = new Function("", condition);
  553. addConditionToken(token);
  554. return new QueryWhere<T>(this);
  555. }
  556. /**
  557. * Sets the Limit and Offset of a query.
  558. *
  559. * @return the query
  560. */
  561. public Query<T> limit(long limit) {
  562. this.limit = limit;
  563. return this;
  564. }
  565. public Query<T> offset(long offset) {
  566. this.offset = offset;
  567. return this;
  568. }
  569. public Query<T> orderBy(boolean field) {
  570. from.getAliasDefinition().checkMultipleBooleans();
  571. return orderByPrimitive(field);
  572. }
  573. public Query<T> orderBy(byte field) {
  574. return orderByPrimitive(field);
  575. }
  576. public Query<T> orderBy(short field) {
  577. return orderByPrimitive(field);
  578. }
  579. public Query<T> orderBy(int field) {
  580. return orderByPrimitive(field);
  581. }
  582. public Query<T> orderBy(long field) {
  583. return orderByPrimitive(field);
  584. }
  585. public Query<T> orderBy(float field) {
  586. return orderByPrimitive(field);
  587. }
  588. public Query<T> orderBy(double field) {
  589. return orderByPrimitive(field);
  590. }
  591. Query<T> orderByPrimitive(Object field) {
  592. Object alias = getPrimitiveAliasByValue(field);
  593. if (alias == null) {
  594. return orderBy(field);
  595. }
  596. return orderBy(alias);
  597. }
  598. public Query<T> orderBy(Object expr) {
  599. from.getAliasDefinition().checkMultipleEnums(expr);
  600. OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
  601. addOrderBy(e);
  602. return this;
  603. }
  604. /**
  605. * Order by a number of columns.
  606. *
  607. * @param expressions
  608. * the columns
  609. * @return the query
  610. */
  611. public Query<T> orderBy(Object... expressions) {
  612. for (Object expr : expressions) {
  613. from.getAliasDefinition().checkMultipleEnums(expr);
  614. OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
  615. addOrderBy(e);
  616. }
  617. return this;
  618. }
  619. public Query<T> orderByDesc(Object expr) {
  620. OrderExpression<T> e = new OrderExpression<T>(this, expr, true, false, false);
  621. addOrderBy(e);
  622. return this;
  623. }
  624. public Query<T> groupBy(boolean field) {
  625. from.getAliasDefinition().checkMultipleBooleans();
  626. return groupByPrimitive(field);
  627. }
  628. public Query<T> groupBy(byte field) {
  629. return groupByPrimitive(field);
  630. }
  631. public Query<T> groupBy(short field) {
  632. return groupByPrimitive(field);
  633. }
  634. public Query<T> groupBy(int field) {
  635. return groupByPrimitive(field);
  636. }
  637. public Query<T> groupBy(long field) {
  638. return groupByPrimitive(field);
  639. }
  640. public Query<T> groupBy(float field) {
  641. return groupByPrimitive(field);
  642. }
  643. public Query<T> groupBy(double field) {
  644. return groupByPrimitive(field);
  645. }
  646. Query<T> groupByPrimitive(Object field) {
  647. Object alias = getPrimitiveAliasByValue(field);
  648. if (alias == null) {
  649. return groupBy(field);
  650. }
  651. return groupBy(alias);
  652. }
  653. public Query<T> groupBy(Object expr) {
  654. from.getAliasDefinition().checkMultipleEnums(expr);
  655. groupByExpressions.add(expr);
  656. return this;
  657. }
  658. public Query<T> groupBy(Object... groupBy) {
  659. this.groupByExpressions.addAll(Arrays.asList(groupBy));
  660. return this;
  661. }
  662. /**
  663. * INTERNAL
  664. *
  665. * @param stat
  666. * the statement
  667. * @param alias
  668. * the alias object (can be null)
  669. * @param value
  670. * the value
  671. */
  672. public void appendSQL(SQLStatement stat, Object alias, Object value) {
  673. if (Function.count() == value) {
  674. stat.appendSQL("COUNT(*)");
  675. return;
  676. }
  677. if (RuntimeParameter.PARAMETER == value) {
  678. stat.appendSQL("?");
  679. addParameter(stat, alias, value);
  680. return;
  681. }
  682. Token token = Db.getToken(value);
  683. if (token != null) {
  684. token.appendSQL(stat, this);
  685. return;
  686. }
  687. if (alias != null && value.getClass().isEnum()) {
  688. // special case:
  689. // value is first enum constant which is also the alias object.
  690. // the first enum constant is used as the alias because we can not
  691. // instantiate an enum reflectively.
  692. stat.appendSQL("?");
  693. addParameter(stat, alias, value);
  694. return;
  695. }
  696. SelectColumn<T> col = getColumnByReference(value);
  697. if (col != null) {
  698. col.appendSQL(stat);
  699. return;
  700. }
  701. stat.appendSQL("?");
  702. addParameter(stat, alias, value);
  703. }
  704. /**
  705. * INTERNAL
  706. *
  707. * @param stat
  708. * the statement
  709. * @param alias
  710. * the alias object (can be null)
  711. * @param valueLeft
  712. * the value on the left of the compound clause
  713. * @param valueRight
  714. * the value on the right of the compound clause
  715. * @param compareType
  716. * the current compare type (e.g. BETWEEN)
  717. */
  718. public void appendSQL(SQLStatement stat, Object alias, Object valueLeft, Object valueRight,
  719. CompareType compareType) {
  720. stat.appendSQL("?");
  721. stat.appendSQL(" ");
  722. switch (compareType) {
  723. case BETWEEN:
  724. stat.appendSQL("AND");
  725. break;
  726. }
  727. stat.appendSQL(" ");
  728. stat.appendSQL("?");
  729. addParameter(stat, alias, valueLeft);
  730. addParameter(stat, alias, valueRight);
  731. }
  732. public void appendSQL(SQLStatement stat, Object alias, Iterable<Object> values,
  733. CompareType compareType) {
  734. boolean first = true;
  735. stat.appendSQL("(");
  736. for (Object value : values) {
  737. if (first) {
  738. first = false;
  739. } else {
  740. stat.appendSQL(", ");
  741. }
  742. stat.appendSQL("?");
  743. addParameter(stat, alias, value);
  744. }
  745. stat.appendSQL(")");
  746. }
  747. private void addParameter(SQLStatement stat, Object alias, Object value) {
  748. SelectColumn<T> col = getColumnByReference(alias);
  749. if (col != null && value.getClass().isEnum()) {
  750. // enum
  751. EnumType type = col.getFieldDefinition().enumType;
  752. Enum<?> anEnum = (Enum<?>) value;
  753. Object y = Utils.convertEnum(anEnum, type);
  754. stat.addParameter(y);
  755. } else if (col != null) {
  756. // object
  757. Class<? extends DataTypeAdapter<?>> typeAdapter = col.getFieldDefinition().typeAdapter;
  758. Object parameter = db.getDialect().serialize(value, typeAdapter);
  759. stat.addParameter(parameter);
  760. } else {
  761. // primitive
  762. stat.addParameter(value);
  763. }
  764. }
  765. void addConditionToken(Token condition) {
  766. if (condition == ConditionOpenClose.OPEN) {
  767. conditionDepth ++;
  768. } else if (condition == ConditionOpenClose.CLOSE) {
  769. conditionDepth --;
  770. if (conditionDepth < 0) {
  771. throw new IciqlException("unmatch condition open-close count");
  772. }
  773. }
  774. conditions.add(condition);
  775. }
  776. void addConditionToken(Query<T> other) {
  777. for (Token condition : other.conditions) {
  778. addConditionToken(condition);
  779. }
  780. }
  781. void addUpdateColumnDeclaration(UpdateColumn declaration) {
  782. updateColumnDeclarations.add(declaration);
  783. }
  784. void appendWhere(SQLStatement stat) {
  785. if (conditionDepth != 0) {
  786. throw new IciqlException("unmatch condition open-close count");
  787. }
  788. if (!conditions.isEmpty()) {
  789. stat.appendSQL(" WHERE ");
  790. boolean skipNextConjunction = false;
  791. for (Token token : conditions) {
  792. if (skipNextConjunction && token instanceof ConditionAndOr) {
  793. skipNextConjunction = false;
  794. continue;
  795. }
  796. token.appendSQL(stat, this);
  797. stat.appendSQL(" ");
  798. if (ConditionOpenClose.OPEN == token) {
  799. skipNextConjunction = true;
  800. }
  801. }
  802. }
  803. }
  804. void appendFromWhere(SQLStatement stat) {
  805. appendFromWhere(stat, true);
  806. }
  807. void appendFromWhere(SQLStatement stat, boolean log) {
  808. stat.appendSQL(" FROM ");
  809. from.appendSQL(stat);
  810. for (SelectTable<T> join : joins) {
  811. join.appendSQLAsJoin(stat, this);
  812. }
  813. appendWhere(stat);
  814. if (!groupByExpressions.isEmpty()) {
  815. stat.appendSQL(" GROUP BY ");
  816. int i = 0;
  817. for (Object obj : groupByExpressions) {
  818. if (i++ > 0) {
  819. stat.appendSQL(", ");
  820. }
  821. appendSQL(stat, null, obj);
  822. stat.appendSQL(" ");
  823. }
  824. }
  825. if (!orderByList.isEmpty()) {
  826. stat.appendSQL(" ORDER BY ");
  827. int i = 0;
  828. for (OrderExpression<T> o : orderByList) {
  829. if (i++ > 0) {
  830. stat.appendSQL(", ");
  831. }
  832. o.appendSQL(stat);
  833. stat.appendSQL(" ");
  834. }
  835. }
  836. db.getDialect().appendLimitOffset(stat, limit, offset);
  837. if (log) {
  838. IciqlLogger.select(stat.getSQL());
  839. }
  840. }
  841. /**
  842. * Join another table.
  843. *
  844. * @param alias
  845. * an alias for the table to join
  846. * @return the joined query
  847. */
  848. public <A> QueryJoin<T> innerJoin(A alias) {
  849. return join(alias, false);
  850. }
  851. public <A> QueryJoin<T> leftJoin(A alias) {
  852. return join(alias, true);
  853. }
  854. @SuppressWarnings({ "unchecked", "rawtypes" })
  855. private <A> QueryJoin<T> join(A alias, boolean outerJoin) {
  856. TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
  857. SelectTable<T> join = new SelectTable(db, this, alias, outerJoin);
  858. def.initSelectObject(join, alias, aliasMap, false);
  859. joins.add(join);
  860. return new QueryJoin(this, join);
  861. }
  862. Db getDb() {
  863. return db;
  864. }
  865. SelectTable<T> getFrom() {
  866. return from;
  867. }
  868. boolean isJoin() {
  869. return !joins.isEmpty();
  870. }
  871. SelectTable<?> getSelectTable(Object alias) {
  872. if (from.getAlias() == alias) {
  873. return from;
  874. } else {
  875. for (SelectTable<?> join : joins) {
  876. if (join.getAlias() == alias) {
  877. return join;
  878. }
  879. }
  880. }
  881. return null;
  882. }
  883. /**
  884. * This method returns a mapped Object field by its reference.
  885. *
  886. * @param obj
  887. * @return
  888. */
  889. private SelectColumn<T> getColumnByReference(Object obj) {
  890. SelectColumn<T> col = aliasMap.get(obj);
  891. return col;
  892. }
  893. /**
  894. * This method returns the alias of a mapped primitive field by its value.
  895. *
  896. * @param obj
  897. * @return
  898. */
  899. @SuppressWarnings("unchecked")
  900. <A> A getPrimitiveAliasByValue(A obj) {
  901. for (Object alias : aliasMap.keySet()) {
  902. if (alias.equals(obj)) {
  903. SelectColumn<T> match = aliasMap.get(alias);
  904. if (match.getFieldDefinition().isPrimitive) {
  905. return (A) alias;
  906. }
  907. }
  908. }
  909. return null;
  910. }
  911. void addOrderBy(OrderExpression<T> expr) {
  912. orderByList.add(expr);
  913. }
  914. }