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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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.Clob;
  20. import java.sql.ResultSet;
  21. import java.sql.SQLException;
  22. import java.util.ArrayList;
  23. import java.util.HashMap;
  24. import java.util.IdentityHashMap;
  25. import java.util.List;
  26. import com.iciql.bytecode.ClassReader;
  27. import com.iciql.util.JdbcUtils;
  28. import com.iciql.util.StatementLogger;
  29. import com.iciql.util.Utils;
  30. /**
  31. * This class represents a query.
  32. *
  33. * @param <T>
  34. * the return type
  35. */
  36. public class Query<T> {
  37. private Db db;
  38. private SelectTable<T> from;
  39. private ArrayList<Token> conditions = Utils.newArrayList();
  40. private ArrayList<UpdateColumn> updateColumnDeclarations = Utils.newArrayList();
  41. private ArrayList<SelectTable<T>> joins = Utils.newArrayList();
  42. private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
  43. private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
  44. private Object[] groupByExpressions;
  45. private long limit;
  46. private long offset;
  47. private Query(Db db) {
  48. this.db = db;
  49. }
  50. /**
  51. * from() is a static factory method to build a Query object.
  52. *
  53. * @param db
  54. * @param alias
  55. * @return a query object
  56. */
  57. @SuppressWarnings("unchecked")
  58. static <T> Query<T> from(Db db, T alias) {
  59. Query<T> query = new Query<T>(db);
  60. TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
  61. query.from = new SelectTable<T>(db, query, alias, false);
  62. def.initSelectObject(query.from, alias, query.aliasMap);
  63. return query;
  64. }
  65. public long selectCount() {
  66. SQLStatement stat = getSelectStatement(false);
  67. stat.appendSQL("COUNT(*) ");
  68. appendFromWhere(stat);
  69. ResultSet rs = stat.executeQuery();
  70. try {
  71. rs.next();
  72. long value = rs.getLong(1);
  73. return value;
  74. } catch (SQLException e) {
  75. throw new IciqlException(e);
  76. } finally {
  77. JdbcUtils.closeSilently(rs, true);
  78. }
  79. }
  80. public List<T> select() {
  81. return select(false);
  82. }
  83. public T selectFirst() {
  84. return select(false).get(0);
  85. }
  86. public List<T> selectDistinct() {
  87. return select(true);
  88. }
  89. @SuppressWarnings("unchecked")
  90. public <X, Z> X selectFirst(Z x) {
  91. List<X> list = (List<X>) select(x);
  92. return list.isEmpty() ? null : list.get(0);
  93. }
  94. public String getSQL() {
  95. SQLStatement stat = getSelectStatement(false);
  96. stat.appendSQL("*");
  97. appendFromWhere(stat);
  98. return stat.getSQL().trim();
  99. }
  100. private List<T> select(boolean distinct) {
  101. List<T> result = Utils.newArrayList();
  102. TableDefinition<T> def = from.getAliasDefinition();
  103. SQLStatement stat = getSelectStatement(distinct);
  104. def.appendSelectList(stat);
  105. appendFromWhere(stat);
  106. ResultSet rs = stat.executeQuery();
  107. try {
  108. while (rs.next()) {
  109. T item = from.newObject();
  110. from.getAliasDefinition().readRow(item, rs);
  111. result.add(item);
  112. }
  113. } catch (SQLException e) {
  114. throw new IciqlException(e);
  115. } finally {
  116. JdbcUtils.closeSilently(rs, true);
  117. }
  118. return result;
  119. }
  120. public int delete() {
  121. SQLStatement stat = new SQLStatement(db);
  122. stat.appendSQL("DELETE FROM ");
  123. from.appendSQL(stat);
  124. appendWhere(stat);
  125. StatementLogger.delete(stat.getSQL());
  126. return stat.executeUpdate();
  127. }
  128. public <A> UpdateColumnSet<T, A> set(A field) {
  129. return new UpdateColumnSet<T, A>(this, field);
  130. }
  131. public <A> UpdateColumnIncrement<T, A> increment(A field) {
  132. return new UpdateColumnIncrement<T, A>(this, field);
  133. }
  134. public int update() {
  135. if (updateColumnDeclarations.size() == 0) {
  136. throw new IciqlException("Missing set or increment call.");
  137. }
  138. SQLStatement stat = new SQLStatement(db);
  139. stat.appendSQL("UPDATE ");
  140. from.appendSQL(stat);
  141. stat.appendSQL(" SET ");
  142. int i = 0;
  143. for (UpdateColumn declaration : updateColumnDeclarations) {
  144. if (i++ > 0) {
  145. stat.appendSQL(", ");
  146. }
  147. declaration.appendSQL(stat);
  148. }
  149. appendWhere(stat);
  150. StatementLogger.update(stat.getSQL());
  151. return stat.executeUpdate();
  152. }
  153. public <X, Z> List<X> selectDistinct(Z x) {
  154. return select(x, true);
  155. }
  156. public <X, Z> List<X> select(Z x) {
  157. return select(x, false);
  158. }
  159. @SuppressWarnings("unchecked")
  160. private <X, Z> List<X> select(Z x, boolean distinct) {
  161. Class<?> clazz = x.getClass();
  162. if (Utils.isSimpleType(clazz)) {
  163. return selectSimple((X) x, distinct);
  164. }
  165. clazz = clazz.getSuperclass();
  166. return select((Class<X>) clazz, (X) x, distinct);
  167. }
  168. private <X> List<X> select(Class<X> clazz, X x, boolean distinct) {
  169. List<X> result = Utils.newArrayList();
  170. TableDefinition<X> def = db.define(clazz);
  171. SQLStatement stat = getSelectStatement(distinct);
  172. def.appendSelectList(stat, this, x);
  173. appendFromWhere(stat);
  174. ResultSet rs = stat.executeQuery();
  175. try {
  176. while (rs.next()) {
  177. X row = Utils.newObject(clazz);
  178. def.readRow(row, rs);
  179. result.add(row);
  180. }
  181. } catch (SQLException e) {
  182. throw new IciqlException(e);
  183. } finally {
  184. JdbcUtils.closeSilently(rs, true);
  185. }
  186. return result;
  187. }
  188. @SuppressWarnings("unchecked")
  189. private <X> List<X> selectSimple(X x, boolean distinct) {
  190. SQLStatement stat = getSelectStatement(distinct);
  191. appendSQL(stat, x);
  192. appendFromWhere(stat);
  193. ResultSet rs = stat.executeQuery();
  194. List<X> result = Utils.newArrayList();
  195. try {
  196. while (rs.next()) {
  197. try {
  198. X value;
  199. Object o = rs.getObject(1);
  200. if (Clob.class.isAssignableFrom(o.getClass())) {
  201. value = (X) Utils.convert(o, String.class);
  202. } else {
  203. value = (X) o;
  204. }
  205. result.add(value);
  206. } catch (Exception e) {
  207. throw new IciqlException(e);
  208. }
  209. }
  210. } catch (SQLException e) {
  211. throw new IciqlException(e);
  212. } finally {
  213. JdbcUtils.closeSilently(rs, true);
  214. }
  215. return result;
  216. }
  217. private SQLStatement getSelectStatement(boolean distinct) {
  218. SQLStatement stat = new SQLStatement(db);
  219. stat.appendSQL("SELECT ");
  220. if (distinct) {
  221. stat.appendSQL("DISTINCT ");
  222. }
  223. return stat;
  224. }
  225. public <A> QueryCondition<T, A> where(A x) {
  226. return new QueryCondition<T, A>(this, x);
  227. }
  228. public <A> QueryWhere<T> where(Filter filter) {
  229. HashMap<String, Object> fieldMap = Utils.newHashMap();
  230. for (Field f : filter.getClass().getDeclaredFields()) {
  231. f.setAccessible(true);
  232. try {
  233. Object obj = f.get(filter);
  234. if (obj == from.getAlias()) {
  235. List<TableDefinition.FieldDefinition> fields = from.getAliasDefinition().getFields();
  236. String name = f.getName();
  237. for (TableDefinition.FieldDefinition field : fields) {
  238. String n = name + "." + field.field.getName();
  239. Object o = field.field.get(obj);
  240. fieldMap.put(n, o);
  241. }
  242. }
  243. fieldMap.put(f.getName(), f.get(filter));
  244. } catch (Exception e) {
  245. throw new IciqlException(e);
  246. }
  247. }
  248. Token filterCode = new ClassReader().decompile(filter, fieldMap, "where");
  249. // String filterQuery = filterCode.toString();
  250. conditions.add(filterCode);
  251. return new QueryWhere<T>(this);
  252. }
  253. public QueryWhere<T> where(String fragment, Object... args) {
  254. conditions.add(new RuntimeToken(fragment, args));
  255. return new QueryWhere<T>(this);
  256. }
  257. public QueryWhere<T> whereTrue(Boolean condition) {
  258. Token token = new Function("", condition);
  259. addConditionToken(token);
  260. return new QueryWhere<T>(this);
  261. }
  262. /**
  263. * Sets the Limit and Offset of a query.
  264. *
  265. * @return the query
  266. */
  267. public Query<T> limit(long limit) {
  268. this.limit = limit;
  269. return this;
  270. }
  271. public Query<T> offset(long offset) {
  272. this.offset = offset;
  273. return this;
  274. }
  275. /**
  276. * Order by a number of columns.
  277. *
  278. * @param expressions
  279. * the columns
  280. * @return the query
  281. */
  282. public Query<T> orderBy(Object... expressions) {
  283. for (Object expr : expressions) {
  284. OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
  285. addOrderBy(e);
  286. }
  287. return this;
  288. }
  289. public Query<T> orderByDesc(Object expr) {
  290. OrderExpression<T> e = new OrderExpression<T>(this, expr, true, false, false);
  291. addOrderBy(e);
  292. return this;
  293. }
  294. public Query<T> groupBy(Object... groupBy) {
  295. this.groupByExpressions = groupBy;
  296. return this;
  297. }
  298. /**
  299. * INTERNAL
  300. *
  301. * @param stat
  302. * the statement
  303. * @param x
  304. * the alias object
  305. */
  306. public void appendSQL(SQLStatement stat, Object x) {
  307. if (x == Function.count()) {
  308. stat.appendSQL("COUNT(*)");
  309. return;
  310. }
  311. Token token = Db.getToken(x);
  312. if (token != null) {
  313. token.appendSQL(stat, this);
  314. return;
  315. }
  316. SelectColumn<T> col = aliasMap.get(x);
  317. if (col != null) {
  318. col.appendSQL(stat);
  319. return;
  320. }
  321. stat.appendSQL("?");
  322. stat.addParameter(x);
  323. }
  324. void addConditionToken(Token condition) {
  325. conditions.add(condition);
  326. }
  327. void addUpdateColumnDeclaration(UpdateColumn declaration) {
  328. updateColumnDeclarations.add(declaration);
  329. }
  330. void appendWhere(SQLStatement stat) {
  331. if (!conditions.isEmpty()) {
  332. stat.appendSQL(" WHERE ");
  333. for (Token token : conditions) {
  334. token.appendSQL(stat, this);
  335. stat.appendSQL(" ");
  336. }
  337. }
  338. }
  339. void appendFromWhere(SQLStatement stat) {
  340. stat.appendSQL(" FROM ");
  341. from.appendSQL(stat);
  342. for (SelectTable<T> join : joins) {
  343. join.appendSQLAsJoin(stat, this);
  344. }
  345. appendWhere(stat);
  346. if (groupByExpressions != null) {
  347. stat.appendSQL(" GROUP BY ");
  348. int i = 0;
  349. for (Object obj : groupByExpressions) {
  350. if (i++ > 0) {
  351. stat.appendSQL(", ");
  352. }
  353. appendSQL(stat, obj);
  354. stat.appendSQL(" ");
  355. }
  356. }
  357. if (!orderByList.isEmpty()) {
  358. stat.appendSQL(" ORDER BY ");
  359. int i = 0;
  360. for (OrderExpression<T> o : orderByList) {
  361. if (i++ > 0) {
  362. stat.appendSQL(", ");
  363. }
  364. o.appendSQL(stat);
  365. stat.appendSQL(" ");
  366. }
  367. }
  368. if (limit > 0) {
  369. db.getDialect().appendLimit(stat, limit);
  370. }
  371. if (offset > 0) {
  372. db.getDialect().appendOffset(stat, offset);
  373. }
  374. StatementLogger.select(stat.getSQL());
  375. }
  376. /**
  377. * Join another table.
  378. *
  379. * @param alias
  380. * an alias for the table to join
  381. * @return the joined query
  382. */
  383. @SuppressWarnings({ "unchecked", "rawtypes" })
  384. public <U> QueryJoin innerJoin(U alias) {
  385. TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
  386. SelectTable<T> join = new SelectTable(db, this, alias, false);
  387. def.initSelectObject(join, alias, aliasMap);
  388. joins.add(join);
  389. return new QueryJoin(this, join);
  390. }
  391. Db getDb() {
  392. return db;
  393. }
  394. boolean isJoin() {
  395. return !joins.isEmpty();
  396. }
  397. SelectColumn<T> getSelectColumn(Object obj) {
  398. return aliasMap.get(obj);
  399. }
  400. void addOrderBy(OrderExpression<T> expr) {
  401. orderByList.add(expr);
  402. }
  403. }