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 27KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  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(db.getDialect(), 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> Query<T> setNull(A field) {
  252. return set(field).to(null);
  253. }
  254. public <A> UpdateColumnSet<T, A> set(A field) {
  255. from.getAliasDefinition().checkMultipleEnums(field);
  256. return new UpdateColumnSet<T, A>(this, field);
  257. }
  258. public UpdateColumnSet<T, Boolean> set(boolean field) {
  259. from.getAliasDefinition().checkMultipleBooleans();
  260. return setPrimitive(field);
  261. }
  262. public UpdateColumnSet<T, Byte> set(byte field) {
  263. return setPrimitive(field);
  264. }
  265. public UpdateColumnSet<T, Short> set(short field) {
  266. return setPrimitive(field);
  267. }
  268. public UpdateColumnSet<T, Integer> set(int field) {
  269. return setPrimitive(field);
  270. }
  271. public UpdateColumnSet<T, Long> set(long field) {
  272. return setPrimitive(field);
  273. }
  274. public UpdateColumnSet<T, Float> set(float field) {
  275. return setPrimitive(field);
  276. }
  277. public UpdateColumnSet<T, Double> set(double field) {
  278. return setPrimitive(field);
  279. }
  280. private <A> UpdateColumnSet<T, A> setPrimitive(A field) {
  281. A alias = getPrimitiveAliasByValue(field);
  282. if (alias == null) {
  283. // this will result in an unmapped field exception
  284. return set(field);
  285. }
  286. return set(alias);
  287. }
  288. public <A> UpdateColumnIncrement<T, A> increment(A field) {
  289. return new UpdateColumnIncrement<T, A>(this, field);
  290. }
  291. public UpdateColumnIncrement<T, Byte> increment(byte field) {
  292. return incrementPrimitive(field);
  293. }
  294. public UpdateColumnIncrement<T, Short> increment(short field) {
  295. return incrementPrimitive(field);
  296. }
  297. public UpdateColumnIncrement<T, Integer> increment(int field) {
  298. return incrementPrimitive(field);
  299. }
  300. public UpdateColumnIncrement<T, Long> increment(long field) {
  301. return incrementPrimitive(field);
  302. }
  303. public UpdateColumnIncrement<T, Float> increment(float field) {
  304. return incrementPrimitive(field);
  305. }
  306. public UpdateColumnIncrement<T, Double> increment(double field) {
  307. return incrementPrimitive(field);
  308. }
  309. private <A> UpdateColumnIncrement<T, A> incrementPrimitive(A field) {
  310. A alias = getPrimitiveAliasByValue(field);
  311. if (alias == null) {
  312. // this will result in an unmapped field exception
  313. return increment(field);
  314. }
  315. return increment(alias);
  316. }
  317. public int update() {
  318. if (updateColumnDeclarations.size() == 0) {
  319. throw new IciqlException("Missing set or increment call.");
  320. }
  321. SQLStatement stat = new SQLStatement(db);
  322. stat.appendSQL("UPDATE ");
  323. from.appendSQL(stat);
  324. stat.appendSQL(" SET ");
  325. int i = 0;
  326. for (UpdateColumn declaration : updateColumnDeclarations) {
  327. if (i++ > 0) {
  328. stat.appendSQL(", ");
  329. }
  330. declaration.appendSQL(stat);
  331. }
  332. appendWhere(stat);
  333. IciqlLogger.update(stat.getSQL());
  334. return stat.executeUpdate();
  335. }
  336. public <X, Z> List<X> selectDistinct(Z x) {
  337. return select(x, true);
  338. }
  339. public <X, Z> List<X> select(Z x) {
  340. return select(x, false);
  341. }
  342. @SuppressWarnings("unchecked")
  343. private <X, Z> List<X> select(Z x, boolean distinct) {
  344. Class<?> clazz = x.getClass();
  345. if (Utils.isSimpleType(clazz)) {
  346. return selectSimple((X) x, distinct);
  347. }
  348. Class<?> enclosingClass = clazz.getEnclosingClass();
  349. if (enclosingClass != null) {
  350. // anonymous inner class
  351. clazz = clazz.getSuperclass();
  352. }
  353. return select((Class<X>) clazz, (X) x, distinct);
  354. }
  355. private <X> List<X> select(Class<X> clazz, X x, boolean distinct) {
  356. List<X> result = Utils.newArrayList();
  357. TableDefinition<X> def = db.define(clazz);
  358. SQLStatement stat = getSelectStatement(distinct);
  359. def.appendSelectList(stat, this, x);
  360. appendFromWhere(stat);
  361. ResultSet rs = stat.executeQuery();
  362. try {
  363. // SQLite returns pre-closed ResultSets for query results with 0 rows
  364. if (!rs.isClosed()) {
  365. int[] columns = def.mapColumns(db.getDialect(), false, rs);
  366. while (rs.next()) {
  367. X row = Utils.newObject(clazz);
  368. def.readRow(db.getDialect(), row, rs, columns);
  369. result.add(row);
  370. }
  371. }
  372. } catch (SQLException e) {
  373. throw IciqlException.fromSQL(stat.getSQL(), e);
  374. } finally {
  375. JdbcUtils.closeSilently(rs, true);
  376. }
  377. return result;
  378. }
  379. @SuppressWarnings("unchecked")
  380. private <X> List<X> selectSimple(X x, boolean distinct) {
  381. SQLStatement stat = getSelectStatement(distinct);
  382. appendSQL(stat, null, x);
  383. appendFromWhere(stat);
  384. ResultSet rs = stat.executeQuery();
  385. List<X> result = Utils.newArrayList();
  386. Class<? extends DataTypeAdapter<?>> typeAdapter = Utils.getDataTypeAdapter(x.getClass().getAnnotations());
  387. try {
  388. // SQLite returns pre-closed ResultSets for query results with 0 rows
  389. if (!rs.isClosed()) {
  390. while (rs.next()) {
  391. X value = (X) db.getDialect().deserialize(rs, 1, x.getClass(), typeAdapter);
  392. result.add(value);
  393. }
  394. }
  395. } catch (Exception e) {
  396. throw IciqlException.fromSQL(stat.getSQL(), e);
  397. } finally {
  398. JdbcUtils.closeSilently(rs, true);
  399. }
  400. return result;
  401. }
  402. private SQLStatement getSelectStatement(boolean distinct) {
  403. SQLStatement stat = new SQLStatement(db);
  404. stat.appendSQL("SELECT ");
  405. if (distinct) {
  406. stat.appendSQL("DISTINCT ");
  407. }
  408. return stat;
  409. }
  410. /**
  411. * Begin a primitive boolean field condition clause.
  412. *
  413. * @param x
  414. * the primitive boolean field to query
  415. * @return a query condition to continue building the condition
  416. */
  417. public QueryCondition<T, Boolean> where(boolean x) {
  418. from.getAliasDefinition().checkMultipleBooleans();
  419. return wherePrimitive(x);
  420. }
  421. /**
  422. * Begin a primitive short field condition clause.
  423. *
  424. * @param x
  425. * the primitive short field to query
  426. * @return a query condition to continue building the condition
  427. */
  428. public QueryCondition<T, Byte> where(byte x) {
  429. return wherePrimitive(x);
  430. }
  431. /**
  432. * Begin a primitive short field condition clause.
  433. *
  434. * @param x
  435. * the primitive short field to query
  436. * @return a query condition to continue building the condition
  437. */
  438. public QueryCondition<T, Short> where(short x) {
  439. return wherePrimitive(x);
  440. }
  441. /**
  442. * Begin a primitive int field condition clause.
  443. *
  444. * @param x
  445. * the primitive int field to query
  446. * @return a query condition to continue building the condition
  447. */
  448. public QueryCondition<T, Integer> where(int x) {
  449. return wherePrimitive(x);
  450. }
  451. /**
  452. * Begin a primitive long field condition clause.
  453. *
  454. * @param x
  455. * the primitive long field to query
  456. * @return a query condition to continue building the condition
  457. */
  458. public QueryCondition<T, Long> where(long x) {
  459. return wherePrimitive(x);
  460. }
  461. /**
  462. * Begin a primitive float field condition clause.
  463. *
  464. * @param x
  465. * the primitive float field to query
  466. * @return a query condition to continue building the condition
  467. */
  468. public QueryCondition<T, Float> where(float x) {
  469. return wherePrimitive(x);
  470. }
  471. /**
  472. * Begin a primitive double field condition clause.
  473. *
  474. * @param x
  475. * the primitive double field to query
  476. * @return a query condition to continue building the condition
  477. */
  478. public QueryCondition<T, Double> where(double x) {
  479. return wherePrimitive(x);
  480. }
  481. /**
  482. * Begins a primitive field condition clause.
  483. *
  484. * @param value
  485. * @return a query condition to continue building the condition
  486. */
  487. private <A> QueryCondition<T, A> wherePrimitive(A value) {
  488. A alias = getPrimitiveAliasByValue(value);
  489. if (alias == null) {
  490. // this will result in an unmapped field exception
  491. return where(value);
  492. }
  493. return where(alias);
  494. }
  495. /**
  496. * Begin an Object field condition clause.
  497. *
  498. * @param x
  499. * the mapped object to query
  500. * @return a query condition to continue building the condition
  501. */
  502. public <A> QueryCondition<T, A> where(A x) {
  503. from.getAliasDefinition().checkMultipleEnums(x);
  504. return new QueryCondition<T, A>(this, x);
  505. }
  506. public <A> QueryWhere<T> where(Filter filter) {
  507. HashMap<String, Object> fieldMap = Utils.newHashMap();
  508. for (Field f : filter.getClass().getDeclaredFields()) {
  509. f.setAccessible(true);
  510. try {
  511. Object obj = f.get(filter);
  512. if (obj == from.getAlias()) {
  513. List<TableDefinition.FieldDefinition> fields = from.getAliasDefinition().getFields();
  514. String name = f.getName();
  515. for (TableDefinition.FieldDefinition field : fields) {
  516. String n = name + "." + field.field.getName();
  517. Object o = field.field.get(obj);
  518. fieldMap.put(n, o);
  519. }
  520. }
  521. fieldMap.put(f.getName(), f.get(filter));
  522. } catch (Exception e) {
  523. throw new IciqlException(e);
  524. }
  525. }
  526. Token filterCode = new ClassReader().decompile(filter, fieldMap, "where");
  527. // String filterQuery = filterCode.toString();
  528. conditions.add(filterCode);
  529. return new QueryWhere<T>(this);
  530. }
  531. public QueryWhere<T> where(String fragment, List<?> args) {
  532. return this.where(fragment, args.toArray());
  533. }
  534. public QueryWhere<T> where(String fragment, Object... args) {
  535. conditions.add(new RuntimeToken(fragment, args));
  536. return new QueryWhere<T>(this);
  537. }
  538. public Query<T> where(And<T> conditions) {
  539. whereTrue();
  540. addConditionToken(conditions.where.query);
  541. return this;
  542. }
  543. public Query<T> where(Or<T> conditions) {
  544. whereFalse();
  545. addConditionToken(conditions.where.query);
  546. return this;
  547. }
  548. public QueryWhere<T> whereTrue() {
  549. return whereTrue(true);
  550. }
  551. public QueryWhere<T> whereFalse() {
  552. return whereTrue(false);
  553. }
  554. public QueryWhere<T> whereTrue(Boolean condition) {
  555. Token token = new Function("", condition);
  556. addConditionToken(token);
  557. return new QueryWhere<T>(this);
  558. }
  559. /**
  560. * Sets the Limit and Offset of a query.
  561. *
  562. * @return the query
  563. */
  564. public Query<T> limit(long limit) {
  565. this.limit = limit;
  566. return this;
  567. }
  568. public Query<T> offset(long offset) {
  569. this.offset = offset;
  570. return this;
  571. }
  572. public Query<T> orderBy(boolean field) {
  573. from.getAliasDefinition().checkMultipleBooleans();
  574. return orderByPrimitive(field);
  575. }
  576. public Query<T> orderBy(byte field) {
  577. return orderByPrimitive(field);
  578. }
  579. public Query<T> orderBy(short field) {
  580. return orderByPrimitive(field);
  581. }
  582. public Query<T> orderBy(int field) {
  583. return orderByPrimitive(field);
  584. }
  585. public Query<T> orderBy(long field) {
  586. return orderByPrimitive(field);
  587. }
  588. public Query<T> orderBy(float field) {
  589. return orderByPrimitive(field);
  590. }
  591. public Query<T> orderBy(double field) {
  592. return orderByPrimitive(field);
  593. }
  594. Query<T> orderByPrimitive(Object field) {
  595. Object alias = getPrimitiveAliasByValue(field);
  596. if (alias == null) {
  597. return orderBy(field);
  598. }
  599. return orderBy(alias);
  600. }
  601. public Query<T> orderBy(Object expr) {
  602. from.getAliasDefinition().checkMultipleEnums(expr);
  603. OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
  604. addOrderBy(e);
  605. return this;
  606. }
  607. /**
  608. * Order by a number of columns.
  609. *
  610. * @param expressions
  611. * the columns
  612. * @return the query
  613. */
  614. public Query<T> orderBy(Object... expressions) {
  615. for (Object expr : expressions) {
  616. from.getAliasDefinition().checkMultipleEnums(expr);
  617. OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
  618. addOrderBy(e);
  619. }
  620. return this;
  621. }
  622. public Query<T> orderByDesc(Object expr) {
  623. OrderExpression<T> e = new OrderExpression<T>(this, expr, true, false, false);
  624. addOrderBy(e);
  625. return this;
  626. }
  627. public Query<T> groupBy(boolean field) {
  628. from.getAliasDefinition().checkMultipleBooleans();
  629. return groupByPrimitive(field);
  630. }
  631. public Query<T> groupBy(byte field) {
  632. return groupByPrimitive(field);
  633. }
  634. public Query<T> groupBy(short field) {
  635. return groupByPrimitive(field);
  636. }
  637. public Query<T> groupBy(int field) {
  638. return groupByPrimitive(field);
  639. }
  640. public Query<T> groupBy(long field) {
  641. return groupByPrimitive(field);
  642. }
  643. public Query<T> groupBy(float field) {
  644. return groupByPrimitive(field);
  645. }
  646. public Query<T> groupBy(double field) {
  647. return groupByPrimitive(field);
  648. }
  649. Query<T> groupByPrimitive(Object field) {
  650. Object alias = getPrimitiveAliasByValue(field);
  651. if (alias == null) {
  652. return groupBy(field);
  653. }
  654. return groupBy(alias);
  655. }
  656. public Query<T> groupBy(Object expr) {
  657. from.getAliasDefinition().checkMultipleEnums(expr);
  658. groupByExpressions.add(expr);
  659. return this;
  660. }
  661. public Query<T> groupBy(Object... groupBy) {
  662. this.groupByExpressions.addAll(Arrays.asList(groupBy));
  663. return this;
  664. }
  665. /**
  666. * INTERNAL
  667. *
  668. * @param stat
  669. * the statement
  670. * @param alias
  671. * the alias object (can be null)
  672. * @param value
  673. * the value
  674. */
  675. public void appendSQL(SQLStatement stat, Object alias, Object value) {
  676. if (Function.count() == value) {
  677. stat.appendSQL("COUNT(*)");
  678. return;
  679. }
  680. if (RuntimeParameter.PARAMETER == value) {
  681. stat.appendSQL("?");
  682. addParameter(stat, alias, value);
  683. return;
  684. }
  685. Token token = Db.getToken(value);
  686. if (token != null) {
  687. token.appendSQL(stat, this);
  688. return;
  689. }
  690. if (alias != null && value != null && value.getClass().isEnum()) {
  691. // special case:
  692. // value is first enum constant which is also the alias object.
  693. // the first enum constant is used as the alias because we can not
  694. // instantiate an enum reflectively.
  695. stat.appendSQL("?");
  696. addParameter(stat, alias, value);
  697. return;
  698. }
  699. SelectColumn<T> col = getColumnByReference(value);
  700. if (col != null) {
  701. col.appendSQL(stat);
  702. return;
  703. }
  704. stat.appendSQL("?");
  705. addParameter(stat, alias, value);
  706. }
  707. /**
  708. * INTERNAL
  709. *
  710. * @param stat
  711. * the statement
  712. * @param alias
  713. * the alias object (can be null)
  714. * @param valueLeft
  715. * the value on the left of the compound clause
  716. * @param valueRight
  717. * the value on the right of the compound clause
  718. * @param compareType
  719. * the current compare type (e.g. BETWEEN)
  720. */
  721. public void appendSQL(SQLStatement stat, Object alias, Object valueLeft, Object valueRight,
  722. CompareType compareType) {
  723. stat.appendSQL("?");
  724. stat.appendSQL(" ");
  725. switch (compareType) {
  726. case BETWEEN:
  727. stat.appendSQL("AND");
  728. break;
  729. }
  730. stat.appendSQL(" ");
  731. stat.appendSQL("?");
  732. addParameter(stat, alias, valueLeft);
  733. addParameter(stat, alias, valueRight);
  734. }
  735. public void appendSQL(SQLStatement stat, Object alias, Iterable<Object> values,
  736. CompareType compareType) {
  737. boolean first = true;
  738. stat.appendSQL("(");
  739. for (Object value : values) {
  740. if (first) {
  741. first = false;
  742. } else {
  743. stat.appendSQL(", ");
  744. }
  745. stat.appendSQL("?");
  746. addParameter(stat, alias, value);
  747. }
  748. stat.appendSQL(")");
  749. }
  750. private void addParameter(SQLStatement stat, Object alias, Object value) {
  751. SelectColumn<T> col = getColumnByReference(alias);
  752. if (col != null && value != null && value.getClass().isEnum()) {
  753. // enum
  754. EnumType type = col.getFieldDefinition().enumType;
  755. Enum<?> anEnum = (Enum<?>) value;
  756. Object y = Utils.convertEnum(anEnum, type);
  757. stat.addParameter(y);
  758. } else if (col != null) {
  759. // object
  760. Class<? extends DataTypeAdapter<?>> typeAdapter = col.getFieldDefinition().typeAdapter;
  761. Object parameter = db.getDialect().serialize(value, typeAdapter);
  762. stat.addParameter(parameter);
  763. } else {
  764. // primitive
  765. stat.addParameter(value);
  766. }
  767. }
  768. void addConditionToken(Token condition) {
  769. if (condition == ConditionOpenClose.OPEN) {
  770. conditionDepth ++;
  771. } else if (condition == ConditionOpenClose.CLOSE) {
  772. conditionDepth --;
  773. if (conditionDepth < 0) {
  774. throw new IciqlException("unmatch condition open-close count");
  775. }
  776. }
  777. conditions.add(condition);
  778. }
  779. void addConditionToken(Query<T> other) {
  780. for (Token condition : other.conditions) {
  781. addConditionToken(condition);
  782. }
  783. }
  784. void addUpdateColumnDeclaration(UpdateColumn declaration) {
  785. updateColumnDeclarations.add(declaration);
  786. }
  787. void appendWhere(SQLStatement stat) {
  788. if (conditionDepth != 0) {
  789. throw new IciqlException("unmatch condition open-close count");
  790. }
  791. if (!conditions.isEmpty()) {
  792. stat.appendSQL(" WHERE ");
  793. boolean skipNextConjunction = false;
  794. for (Token token : conditions) {
  795. if (skipNextConjunction && token instanceof ConditionAndOr) {
  796. skipNextConjunction = false;
  797. continue;
  798. }
  799. token.appendSQL(stat, this);
  800. stat.appendSQL(" ");
  801. if (ConditionOpenClose.OPEN == token) {
  802. skipNextConjunction = true;
  803. }
  804. }
  805. }
  806. }
  807. void appendFromWhere(SQLStatement stat) {
  808. appendFromWhere(stat, true);
  809. }
  810. void appendFromWhere(SQLStatement stat, boolean log) {
  811. stat.appendSQL(" FROM ");
  812. from.appendSQL(stat);
  813. for (SelectTable<T> join : joins) {
  814. join.appendSQLAsJoin(stat, this);
  815. }
  816. appendWhere(stat);
  817. if (!groupByExpressions.isEmpty()) {
  818. stat.appendSQL(" GROUP BY ");
  819. int i = 0;
  820. for (Object obj : groupByExpressions) {
  821. if (i++ > 0) {
  822. stat.appendSQL(", ");
  823. }
  824. appendSQL(stat, null, obj);
  825. stat.appendSQL(" ");
  826. }
  827. }
  828. if (!orderByList.isEmpty()) {
  829. stat.appendSQL(" ORDER BY ");
  830. int i = 0;
  831. for (OrderExpression<T> o : orderByList) {
  832. if (i++ > 0) {
  833. stat.appendSQL(", ");
  834. }
  835. o.appendSQL(stat);
  836. stat.appendSQL(" ");
  837. }
  838. }
  839. db.getDialect().appendLimitOffset(stat, limit, offset);
  840. if (log) {
  841. IciqlLogger.select(stat.getSQL());
  842. }
  843. }
  844. /**
  845. * Join another table.
  846. *
  847. * @param alias
  848. * an alias for the table to join
  849. * @return the joined query
  850. */
  851. public <A> QueryJoin<T> innerJoin(A alias) {
  852. return join(alias, false);
  853. }
  854. public <A> QueryJoin<T> leftJoin(A alias) {
  855. return join(alias, true);
  856. }
  857. @SuppressWarnings({ "unchecked", "rawtypes" })
  858. private <A> QueryJoin<T> join(A alias, boolean outerJoin) {
  859. TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
  860. SelectTable<T> join = new SelectTable(db, this, alias, outerJoin);
  861. def.initSelectObject(join, alias, aliasMap, false);
  862. joins.add(join);
  863. return new QueryJoin(this, join);
  864. }
  865. Db getDb() {
  866. return db;
  867. }
  868. SelectTable<T> getFrom() {
  869. return from;
  870. }
  871. boolean isJoin() {
  872. return !joins.isEmpty();
  873. }
  874. SelectTable<?> getSelectTable(Object alias) {
  875. if (from.getAlias() == alias) {
  876. return from;
  877. } else {
  878. for (SelectTable<?> join : joins) {
  879. if (join.getAlias() == alias) {
  880. return join;
  881. }
  882. }
  883. }
  884. return null;
  885. }
  886. /**
  887. * This method returns a mapped Object field by its reference.
  888. *
  889. * @param obj
  890. * @return
  891. */
  892. private SelectColumn<T> getColumnByReference(Object obj) {
  893. SelectColumn<T> col = aliasMap.get(obj);
  894. return col;
  895. }
  896. /**
  897. * This method returns the alias of a mapped primitive field by its value.
  898. *
  899. * @param obj
  900. * @return
  901. */
  902. @SuppressWarnings("unchecked")
  903. <A> A getPrimitiveAliasByValue(A obj) {
  904. for (Object alias : aliasMap.keySet()) {
  905. if (alias.equals(obj)) {
  906. SelectColumn<T> match = aliasMap.get(alias);
  907. if (match.getFieldDefinition().isPrimitive) {
  908. return (A) alias;
  909. }
  910. }
  911. }
  912. return null;
  913. }
  914. void addOrderBy(OrderExpression<T> expr) {
  915. orderByList.add(expr);
  916. }
  917. }