001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.amber.query;
031:
032: import com.caucho.amber.AmberQuery;
033: import com.caucho.amber.expr.ArgExpr;
034: import com.caucho.amber.manager.AmberConnection;
035: import com.caucho.amber.type.*;
036: import com.caucho.jdbc.JdbcMetaData;
037:
038: import java.lang.reflect.InvocationTargetException;
039: import java.lang.reflect.Method;
040: import java.sql.PreparedStatement;
041: import java.sql.ResultSet;
042: import java.sql.ResultSetMetaData;
043: import java.sql.SQLException;
044: import java.util.ArrayList;
045: import java.util.List;
046: import java.util.Map;
047:
048: /**
049: * Represents the application's view of the query.
050: */
051: public class UserQuery implements AmberQuery {
052: private AmberConnection _aConn;
053: private AbstractQuery _query;
054: private ResultSetImpl _rs;
055:
056: private QueryCacheKey _cacheKey;
057:
058: private Type[] _argTypes;
059: private Object[] _argValues;
060: private int _argLength = 0;
061:
062: private int _firstResult = 0;
063: private int _maxResults = -1;
064:
065: private long _cacheMaxAge;
066:
067: private boolean _copyOnLoad = true;
068: private boolean _loadOnQuery;
069:
070: public UserQuery(AbstractQuery query) {
071: _query = query;
072:
073: ArgExpr[] argList = query.getArgList();
074:
075: _argTypes = new Type[argList.length];
076: _argValues = new Object[argList.length];
077:
078: _argLength = argList.length;
079:
080: if (query instanceof SelectQuery) {
081: SelectQuery select = (SelectQuery) query;
082:
083: if (select.getOffset() >= 0)
084: _firstResult = select.getOffset();
085:
086: if (select.getLimit() >= 0)
087: _maxResults = select.getLimit();
088: }
089: }
090:
091: /**
092: * Returns the query string.
093: */
094: public String getQueryString() {
095: return _query.getQueryString();
096: }
097:
098: public void init(AmberConnection aConn) {
099: setSession(aConn);
100: }
101:
102: public void setSession(AmberConnection aConn) {
103: _aConn = aConn;
104: }
105:
106: public AmberConnection getSession() {
107: return _aConn;
108: }
109:
110: public AmberConnection getConnection() {
111: return _aConn;
112: }
113:
114: /**
115: * Sets true for load-on-query.
116: */
117: public void setLoadOnQuery(boolean loadOnQuery) {
118: _loadOnQuery = loadOnQuery;
119: }
120:
121: /**
122: * Returns the compiled query.
123: */
124: public AbstractQuery getQuery() {
125: return _query;
126: }
127:
128: /**
129: * Returns the arg type array.
130: */
131: Type[] getArgTypes() {
132: return _argTypes;
133: }
134:
135: /**
136: * Returns the arg values
137: */
138: Object[] getArgValues() {
139: return _argValues;
140: }
141:
142: /**
143: * Returns the arg length
144: */
145: int getArgLength() {
146: return _argLength;
147: }
148:
149: /**
150: * Sets the argument with a string
151: */
152: public void setString(int index, String v) {
153: _argTypes[index - 1] = StringType.create();
154: _argValues[index - 1] = v;
155: _argLength = index;
156: }
157:
158: /**
159: * Sets the argument with a byte
160: */
161: public void setByte(int index, byte v) {
162: _argTypes[index - 1] = ByteType.create();
163: _argValues[index - 1] = new Integer(v);
164: _argLength = index;
165: }
166:
167: /**
168: * Sets the argument with a short
169: */
170: public void setShort(int index, short v) {
171: _argTypes[index - 1] = ShortType.create();
172: _argValues[index - 1] = new Integer(v);
173: _argLength = index;
174: }
175:
176: /**
177: * Sets the argument with an int
178: */
179: public void setInt(int index, int v) {
180: _argTypes[index - 1] = IntegerType.create();
181: _argValues[index - 1] = new Integer(v);
182: _argLength = index;
183: }
184:
185: /**
186: * Sets the argument with a string
187: */
188: public void setLong(int index, long v) {
189: _argTypes[index - 1] = LongType.create();
190: _argValues[index - 1] = new Long(v);
191: _argLength = index;
192: }
193:
194: /**
195: * Sets the argument with a double
196: */
197: public void setDouble(int index, double v) {
198: _argTypes[index - 1] = DoubleType.create();
199: _argValues[index - 1] = new Double(v);
200: _argLength = index;
201: }
202:
203: /**
204: * Sets the argument with a double
205: */
206: public void setFloat(int index, float v) {
207: _argTypes[index - 1] = FloatType.create();
208: _argValues[index - 1] = new Float(v);
209: _argLength = index;
210: }
211:
212: /**
213: * Sets the argument with a timestamp
214: */
215: public void setTimestamp(int index, java.sql.Timestamp v) {
216: _argTypes[index - 1] = SqlTimestampType.create();
217: _argValues[index - 1] = v;
218: _argLength = index;
219: }
220:
221: /**
222: * Sets the argument with a date
223: */
224: public void setDate(int index, java.sql.Date v) {
225: _argTypes[index - 1] = SqlDateType.create();
226: _argValues[index - 1] = v;
227: _argLength = index;
228: }
229:
230: /**
231: * Sets the argument with an object.
232: */
233: public void setObject(int index, Object v) {
234: _argTypes[index - 1] = ObjectType.create();
235: _argValues[index - 1] = v;
236: _argLength = index;
237: }
238:
239: /**
240: * Sets the argument with an object and its Amber type.
241: */
242: public void setObject(int index, Object v, Type type) {
243: _argTypes[index - 1] = type;
244: _argValues[index - 1] = v;
245: _argLength = index;
246: }
247:
248: /**
249: * Sets the argument with a null
250: */
251: public void setNull(int index, int v) {
252: _argTypes[index - 1] = StringType.create();
253: _argValues[index - 1] = null;
254: _argLength = index;
255: }
256:
257: /**
258: * Sets the first result.
259: */
260: public void setFirstResult(int index) {
261: _firstResult = index;
262: }
263:
264: /**
265: * Sets the maximum number of results.
266: */
267: public void setMaxResults(int index) {
268: _maxResults = index;
269: }
270:
271: /**
272: * Returns the max results.
273: */
274: public int getMaxResults() {
275: return _maxResults;
276: }
277:
278: /**
279: * Executes the query returning a result set.
280: */
281: public ResultSet executeQuery() throws SQLException {
282: _aConn.flushNoChecks();
283:
284: if (_rs == null)
285: _rs = new ResultSetImpl();
286:
287: SelectQuery query = (SelectQuery) _query;
288:
289: _rs.setQuery(query);
290: _rs.setSession(_aConn);
291: _rs.setFirstResult(_firstResult);
292: _rs.setMaxResults(_maxResults);
293:
294: int chunkSize = _aConn.getCacheChunkSize();
295: boolean isCacheable;
296:
297: if (chunkSize <= _firstResult)
298: isCacheable = false;
299: else if (_aConn.isActiveTransaction()
300: && !query.isTableReadOnly())
301: isCacheable = false;
302: else if (!query.isCacheable())
303: isCacheable = false;
304: else
305: isCacheable = true;
306:
307: ResultSetCacheChunk cacheChunk = null;
308: ResultSetMetaData metaData = null;
309:
310: if (isCacheable) {
311: int row = 0;
312:
313: cacheChunk = _aConn.getQueryCacheChunk(query.getSQL(),
314: _argValues, row);
315: metaData = _aConn.getQueryMetaData();
316:
317: _rs.setCacheChunk(cacheChunk, metaData);
318: _rs.setUserQuery(this );
319: }
320:
321: if (cacheChunk == null) {
322: ResultSet rs;
323:
324: rs = executeQuery(0, _maxResults);
325:
326: metaData = rs.getMetaData();
327:
328: _rs.setResultSet(rs, metaData);
329:
330: if (isCacheable) {
331: cacheChunk = new ResultSetCacheChunk();
332: cacheChunk.setQuery(query);
333:
334: _rs.fillCacheChunk(cacheChunk);
335:
336: _rs.setCacheChunk(cacheChunk, metaData);
337:
338: _aConn.putQueryCacheChunk(query.getSQL(), _argValues,
339: 0, cacheChunk, metaData);
340: }
341: }
342:
343: _rs.init();
344:
345: return _rs;
346: }
347:
348: /**
349: * Executes the query.
350: */
351: ResultSet executeQuery(int row, int maxResults) throws SQLException {
352: String sql = _query.getSQL();
353:
354: if (maxResults > 0) {
355: JdbcMetaData metaData = _aConn.getAmberManager()
356: .getMetaData();
357:
358: // XXX: should limit meta-data as well?
359: sql = metaData.limit(sql, maxResults);
360: }
361:
362: PreparedStatement pstmt = _aConn.prepareStatement(sql);
363: ArgExpr[] args = _query.getArgList();
364:
365: if (args.length > 0)
366: pstmt.clearParameters();
367:
368: for (int i = 0; i < args.length; i++) {
369: args[i].setParameter(pstmt, i + 1, _argTypes, _argValues);
370: }
371:
372: ResultSet rs = pstmt.executeQuery();
373:
374: for (int i = 0; i < row && rs.next(); i++) {
375: }
376:
377: return rs;
378: }
379:
380: /**
381: * Executes the query returning a result set.
382: */
383: public int executeUpdate() throws SQLException {
384: _aConn.flushNoChecks();
385: // XXX: sync
386:
387: String sql = _query.getSQL();
388:
389: PreparedStatement pstmt = _aConn.prepareStatement(sql);
390: ArgExpr[] args = _query.getArgList();
391:
392: if (args.length > 0)
393: pstmt.clearParameters();
394:
395: for (int i = 0; i < args.length; i++) {
396: args[i].setParameter(pstmt, i + 1, _argTypes, _argValues);
397: }
398:
399: _query.prepare(this , _aConn);
400:
401: int count = pstmt.executeUpdate();
402:
403: if (count != 0)
404: _query.complete(this , _aConn);
405:
406: return count;
407: }
408:
409: /**
410: * Sets the cache max age for the query.
411: */
412: public void setCacheMaxAge(long ms) {
413: _cacheMaxAge = ms;
414: }
415:
416: /**
417: * Executes the query, returning a list.
418: */
419: public List<Object> list() throws SQLException {
420: ArrayList<Object> list = new ArrayList<Object>();
421:
422: list(list);
423:
424: return list;
425: }
426:
427: /**
428: * Executes the query returning the single result.
429: */
430: public Object getSingleResult() throws SQLException {
431: SelectQuery query = (SelectQuery) _query;
432: ResultSet rs = null;
433:
434: _aConn.pushDepth();
435: try {
436: rs = executeQuery();
437: if (rs.next())
438: return rs.getObject(1);
439:
440: return null;
441: } catch (SQLException e) {
442: throw e;
443: } finally {
444: _aConn.popDepth();
445:
446: if (rs != null)
447: rs.close();
448: }
449: }
450:
451: /**
452: * Executes the query, filling the list.
453: */
454: public void list(List<Object> list) throws SQLException {
455: SelectQuery query = (SelectQuery) _query;
456: ResultSet rs = null;
457:
458: _aConn.pushDepth();
459: try {
460: rs = executeQuery();
461:
462: int tupleCount = query.getResultCount();
463:
464: while (rs.next()) {
465: if (tupleCount == 1) {
466: Object value = rs.getObject(1);
467:
468: list.add(value);
469: } else {
470: Object[] values = new Object[tupleCount];
471:
472: for (int i = 0; i < tupleCount; i++) {
473: values[i] = rs.getObject(i + 1);
474: }
475:
476: list.add(values);
477: }
478: }
479: } catch (SQLException e) {
480: throw e;
481: } finally {
482: _aConn.popDepth();
483:
484: if (rs != null)
485: rs.close();
486: }
487: }
488:
489: /**
490: * Executes the query, filling the map.
491: */
492: public void list(Map<Object, Object> map, Method methodGetMapKey)
493: throws SQLException, IllegalAccessException,
494: InvocationTargetException {
495: SelectQuery query = (SelectQuery) _query;
496:
497: ResultSet rs = null;
498:
499: _aConn.pushDepth();
500: try {
501: rs = executeQuery();
502:
503: int tupleCount = query.getResultCount();
504:
505: while (rs.next()) {
506: if (tupleCount == 1) {
507: Object value = rs.getObject(1);
508:
509: com.caucho.amber.entity.Entity entity;
510: entity = (com.caucho.amber.entity.Entity) value;
511:
512: Object mapKey;
513: if (methodGetMapKey == null)
514: mapKey = entity.__caucho_getPrimaryKey();
515: else
516: mapKey = methodGetMapKey.invoke(entity, null);
517:
518: map.put(mapKey, value);
519: } else {
520: Object[] values = new Object[tupleCount];
521:
522: for (int i = 0; i < tupleCount; i++) {
523: values[i] = rs.getObject(i + 1);
524: }
525:
526: // XXX: for now, assume 1st column is the key.
527: Object mapKey = values[0];
528: map.put(mapKey, values);
529: }
530: }
531: } catch (SQLException e) {
532: throw e;
533: } finally {
534: _aConn.popDepth();
535:
536: if (rs != null)
537: rs.close();
538: }
539: }
540:
541: public String toString() {
542: return "UserQuery[" + _query.getQueryString() + "]";
543: }
544: }
|