KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > amber > query > SelectQuery


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.amber.query;
31
32 import com.caucho.amber.entity.AmberEntityHome;
33 import com.caucho.amber.expr.AmberExpr;
34 import com.caucho.amber.expr.AndExpr;
35 import com.caucho.amber.expr.JoinExpr;
36 import com.caucho.amber.expr.KeyColumnExpr;
37 import com.caucho.amber.expr.LoadEntityExpr;
38 import com.caucho.amber.expr.ManyToOneJoinExpr;
39 import com.caucho.amber.type.EntityType;
40 import com.caucho.amber.type.Type;
41 import com.caucho.util.CharBuffer;
42
43 import java.sql.SQLException JavaDoc;
44 import java.util.ArrayList JavaDoc;
45 import java.util.Map JavaDoc;
46
47
48 /**
49  * Represents an Amber select query
50  */

51 public class SelectQuery extends AbstractQuery {
52   private AbstractQuery _parentQuery;
53
54   private boolean _isDistinct;
55
56   private ArrayList JavaDoc<AmberExpr> _resultList;
57   private AmberExpr _where;
58   private AmberExpr _having;
59
60   private ArrayList JavaDoc<AmberExpr> _orderList;
61   private ArrayList JavaDoc<Boolean JavaDoc> _ascList;
62
63   private ArrayList JavaDoc<AmberExpr> _groupList;
64
65   private Map JavaDoc<AmberExpr, String JavaDoc> _joinFetchMap;
66
67   private String JavaDoc _sql;
68
69   // SELECT NEW
70
private Class JavaDoc _constructorClass;
71
72   private boolean _isTableReadOnly = false;
73   private long _cacheTimeout = -1;
74
75   private boolean _hasFrom = true;
76
77   SelectQuery(String JavaDoc query)
78   {
79     super(query);
80   }
81
82   /**
83    * Gets the (join) fetch map.
84    */

85   Map JavaDoc<AmberExpr, String JavaDoc> getJoinFetchMap()
86   {
87     return _joinFetchMap;
88   }
89
90   /**
91    * Sets the constructor class for SELECT NEW.
92    */

93   void setConstructorClass(Class JavaDoc cl)
94   {
95     _constructorClass = cl;
96   }
97
98   /**
99    * Gets the constructor class for SELECT NEW.
100    */

101   public Class JavaDoc getConstructorClass()
102   {
103     return _constructorClass;
104   }
105
106   /**
107    * Sets whether the query has a FROM clause or not.
108    */

109   void setHasFrom(boolean hasFrom)
110   {
111     // The spec. is not clear about the FROM clause for
112
// Current_Date/Time/Timestamp functions.
113

114     _hasFrom = hasFrom;
115   }
116
117   /**
118    * Sets the parent query.
119    */

120   void setParentQuery(AbstractQuery parent)
121   {
122     _parentQuery = parent;
123   }
124
125   /**
126    * Gets the parent query.
127    */

128   public AbstractQuery getParentQuery()
129   {
130     return _parentQuery;
131   }
132
133   /**
134    * Sets true if distinct.
135    */

136   void setDistinct(boolean isDistinct)
137   {
138     _isDistinct = isDistinct;
139   }
140
141   /**
142    * Sets the result list.
143    */

144   void setResultList(ArrayList JavaDoc<AmberExpr> resultList)
145   {
146     _resultList = resultList;
147   }
148
149   /**
150    * Returns the result list.
151    */

152   public ArrayList JavaDoc<AmberExpr> getResultList()
153   {
154     return _resultList;
155   }
156
157   /**
158    * Returns the result type.
159    */

160   int getResultCount()
161   {
162     return _resultList.size();
163   }
164
165   /**
166    * Returns the result type.
167    */

168   Type getResultType(int index)
169   {
170     AmberExpr expr = _resultList.get(index);
171
172     return expr.getType();
173   }
174
175   /**
176    * Sets the having expression
177    */

178   void setHaving(AmberExpr expr)
179   {
180     _having = expr;
181   }
182
183   /**
184    * Sets the where expression
185    */

186   void setWhere(AmberExpr expr)
187   {
188     _where = expr;
189   }
190
191   /**
192    * Sets the group by list.
193    */

194   void setGroupList(ArrayList JavaDoc<AmberExpr> groupList)
195   {
196     _groupList = groupList;
197   }
198
199   /**
200    * Sets the (join) fetch map.
201    */

202   void setJoinFetchMap(Map JavaDoc<AmberExpr, String JavaDoc> joinFetchMap)
203   {
204     _joinFetchMap = joinFetchMap;
205   }
206
207   /**
208    * Sets the order by list.
209    */

210   void setOrderList(ArrayList JavaDoc<AmberExpr> orderList,
211                     ArrayList JavaDoc<Boolean JavaDoc> ascList)
212   {
213     _orderList = orderList;
214     _ascList = ascList;
215   }
216
217   /**
218    * Returns the id load sql
219    */

220   public String JavaDoc getSQL()
221   {
222     return _sql;
223   }
224
225   /**
226    * Returns the expire time.
227    */

228   public long getCacheMaxAge()
229   {
230     return _cacheTimeout;
231   }
232
233   /**
234    * Returns true for cacheable queries.
235    */

236   public boolean isCacheable()
237   {
238     return 100L <= _cacheTimeout;
239   }
240
241   /**
242    * Are the tables read-only
243    */

244   public boolean isTableReadOnly()
245   {
246     return _isTableReadOnly;
247   }
248
249   /**
250    * initializes the query.
251    */

252   void init()
253     throws SQLException JavaDoc
254   {
255     if (_where instanceof AndExpr) {
256       AndExpr and = (AndExpr) _where;
257
258       ArrayList JavaDoc<AmberExpr> components = and.getComponents();
259
260       for (int i = components.size() - 1; i >= 0; i--) {
261         AmberExpr component = components.get(i);
262
263         if (component instanceof JoinExpr) {
264           JoinExpr link = (JoinExpr) component;
265
266           if (link.bindToFromItem()) {
267             components.remove(i);
268           }
269         }
270       }
271
272       _where = and.getSingle();
273     }
274
275     if (_having instanceof AndExpr) {
276       AndExpr and = (AndExpr) _having;
277
278       ArrayList JavaDoc<AmberExpr> components = and.getComponents();
279
280       for (int i = components.size() - 1; i >= 0; i--) {
281         AmberExpr component = components.get(i);
282
283         if (component instanceof JoinExpr) {
284           JoinExpr link = (JoinExpr) component;
285
286           if (link.bindToFromItem()) {
287             components.remove(i);
288           }
289         }
290       }
291
292       _having = and.getSingle();
293     }
294
295     // Rolls up unused from items from the left to the right.
296
// It's not necessary to roll up the rightmost items because
297
// they're only created if they're actually needed
298
for (int i = 0; i < _fromList.size(); i++) {
299       FromItem item = _fromList.get(i);
300
301       JoinExpr join = item.getJoinExpr();
302
303       if (join == null)
304         continue;
305
306       // jpa/1178
307
if (getParentQuery() != null)
308         break;
309
310       FromItem joinParent = join.getJoinParent();
311       FromItem joinTarget = join.getJoinTarget();
312
313       boolean isTarget = item == joinTarget;
314
315       if (joinParent == null) {
316       }
317       else if (joinParent.getJoinExpr() == null
318                && joinParent == joinTarget
319                && ! usesFromData(joinParent)) {
320         _fromList.remove(joinParent);
321
322         replaceJoin(join);
323
324         // XXX:
325
item.setJoinExpr(null);
326         //item.setOuterJoin(false);
327
i = -1;
328
329         AmberExpr joinWhere = join.getWhere();
330
331         if (joinWhere != null)
332           _where = AndExpr.create(_where, joinWhere);
333       }
334       else if (item == joinTarget
335                && ! isJoinParent(item)
336                && ! usesFromData(item)) {
337
338         boolean isManyToMany = false;
339
340         // jpa/1144
341
if (join instanceof ManyToOneJoinExpr) {
342           ManyToOneJoinExpr manyToOneJoinExpr;
343           manyToOneJoinExpr = (ManyToOneJoinExpr) join;
344           isManyToMany = manyToOneJoinExpr.isManyToMany();
345         }
346
347         if (item.isOuterJoin() || exists(joinTarget) || isManyToMany) {
348           // Optimization for common children query:
349
// SELECT o FROM TestBean o WHERE o.parent.id=?
350
// jpa/0h1k
351
// jpa/114g as negative exists test
352

353           _fromList.remove(item);
354
355           replaceJoin(join);
356
357           i = -1;
358
359           AmberExpr joinWhere = join.getWhere();
360
361           if (joinWhere != null)
362             _where = AndExpr.create(_where, joinWhere);
363         }
364       }
365     }
366
367     for (int i = 0; i < _fromList.size(); i++) {
368       FromItem item = _fromList.get(i);
369
370       if (item.isInnerJoin())
371         continue;
372
373       if (item.getJoinExpr() == null)
374         continue;
375
376       boolean isFromInner = isFromInnerJoin(item);
377
378       item.setOuterJoin(! isFromInner);
379     }
380
381     _cacheTimeout = Long.MAX_VALUE / 2;
382     _isTableReadOnly = true;
383     for (FromItem item : _fromList) {
384       EntityType type = item.getTableType();
385
386       if (type != null) {
387         long timeout = type.getCacheTimeout();
388
389         if (timeout < _cacheTimeout)
390           _cacheTimeout = timeout;
391
392         if (! type.isReadOnly())
393           _isTableReadOnly = false;
394       }
395       else {
396         // XXX: kills the cache?
397
_isTableReadOnly = false;
398       }
399     }
400
401     _sql = generateLoadSQL();
402   }
403
404   boolean isJoinParent(FromItem item)
405   {
406     for (int i = 0; i < _fromList.size(); i++) {
407       FromItem subItem = _fromList.get(i);
408
409       if (subItem.getJoinExpr() != null &&
410           subItem.getJoinExpr().getJoinParent() == item) {
411         return true;
412       }
413     }
414
415     return false;
416   }
417
418   boolean isFromInnerJoin(FromItem item)
419   {
420     return usesFrom(item, AmberExpr.IS_INNER_JOIN);
421   }
422
423   boolean usesFromData(FromItem item)
424   {
425     return usesFrom(item, AmberExpr.USES_DATA);
426   }
427
428   /**
429    * Returns true if the item must have at least one entry in the database.
430    */

431   public boolean exists(FromItem item)
432   {
433     // jpa/0h1b vs jpa/114g
434
if (_where != null && _where.exists(item))
435       return true;
436
437     if (_orderList != null) {
438       for (AmberExpr orderBy : _orderList) {
439         // jpa/1110
440
if (orderBy instanceof KeyColumnExpr
441             && orderBy.usesFrom(item, AmberExpr.IS_INNER_JOIN, false))
442           return true;
443       }
444     }
445
446     if (_groupList != null) {
447       for (AmberExpr groupBy : _groupList) {
448         if (groupBy instanceof KeyColumnExpr
449             && groupBy.usesFrom(item, AmberExpr.IS_INNER_JOIN, false))
450           return true;
451       }
452     }
453
454     if (_having != null && _having.exists(item))
455       return true;
456
457     return false;
458   }
459
460   /**
461    * Returns true if the from item is used by the query.
462    */

463   public boolean usesFrom(FromItem item, int type)
464   {
465     for (int j = 0; j < _resultList.size(); j++) {
466       AmberExpr result = _resultList.get(j);
467
468       if (result.usesFrom(item, type)) {
469         return true;
470       }
471     }
472
473     if (_where != null && _where.usesFrom(item, type))
474       return true;
475
476     if (_orderList != null) {
477       for (int j = 0; j < _orderList.size(); j++) {
478         AmberExpr order = _orderList.get(j);
479
480         if (order.usesFrom(item, type)) {
481           return true;
482         }
483       }
484     }
485
486     // jpa/1123
487
if (_groupList != null) {
488       for (int j = 0; j < _groupList.size(); j++) {
489         AmberExpr group = _groupList.get(j);
490
491         // jpa/1123 if (group.usesFrom(item, type)) {
492
if (group.usesFrom(item, AmberExpr.IS_INNER_JOIN)) {
493           return true;
494         }
495       }
496
497       if (_having != null && _having.usesFrom(item, type))
498         return true;
499     }
500
501     return false;
502   }
503
504   void replaceJoin(JoinExpr join)
505   {
506     for (int i = 0; i < _resultList.size(); i++) {
507       AmberExpr result = _resultList.get(i);
508
509       _resultList.set(i, result.replaceJoin(join));
510     }
511
512     if (_where != null) {
513       _where = _where.replaceJoin(join);
514     }
515
516     if (_orderList != null) {
517       for (int i = 0; i < _orderList.size(); i++) {
518         AmberExpr order = _orderList.get(i);
519
520         _orderList.set(i, order.replaceJoin(join));
521       }
522     }
523   }
524
525   public String JavaDoc generateLoadSQL()
526   {
527     return generateLoadSQL(true);
528   }
529
530   /**
531    * Generates the load SQL.
532    *
533    * @param fullSelect true if the load entity expressions
534    * should be fully loaded for all entity
535    * fields. Otherwise, only the entity id
536    * will be loaded: select o.id from ...
537    * It is implemented to optimize the SQL
538    * and allow for databases that only
539    * support single columns in subqueries.
540    * Derby is an example. An additional
541    * condition to generate only the o.id
542    * is the absence of group by. If there
543    * is a group by the full select will
544    * always be generated.
545    *
546    * See also com.caucho.amber.expr.ExistsExpr
547    *
548    * @return the load SQL.
549    */

550   public String JavaDoc generateLoadSQL(boolean fullSelect)
551   {
552     CharBuffer cb = CharBuffer.allocate();
553
554     cb.append("select ");
555
556     if (_isDistinct)
557       cb.append(" distinct ");
558
559     for (int i = 0; i < _resultList.size(); i++) {
560       if (i != 0)
561         cb.append(", ");
562
563       AmberExpr expr = _resultList.get(i);
564
565       if ((_groupList == null) && (expr instanceof LoadEntityExpr))
566         ((LoadEntityExpr) expr).generateSelect(cb, fullSelect);
567       else
568         expr.generateSelect(cb);
569     }
570
571     if (_hasFrom)
572       cb.append(" from ");
573
574     // jpa/114f: reorder from list for left outer join
575
for (int i = 1; i < _fromList.size(); i++) {
576       FromItem item = _fromList.get(i);
577
578       if (item.isOuterJoin()) {
579         JoinExpr join = item.getJoinExpr();
580
581         if (join == null)
582           continue;
583
584         FromItem parent = join.getJoinParent();
585
586         int index = _fromList.indexOf(parent);
587
588         if (index < 0)
589           continue;
590
591         _fromList.remove(i);
592
593         if (index < i)
594           index++;
595
596         _fromList.add(index, item);
597       }
598     }
599
600     boolean hasJoinExpr = false;
601     boolean isFirst = true;
602     for (int i = 0; i < _fromList.size(); i++) {
603       FromItem item = _fromList.get(i);
604
605       // jpa/1178
606
if (getParentQuery() != null) {
607         ArrayList JavaDoc<FromItem> fromList = getParentQuery().getFromList();
608         if (fromList != null) {
609           if (fromList.contains(item)) {
610             hasJoinExpr = true;
611             continue;
612           }
613         }
614       }
615
616       if (isFirst) {
617         isFirst = false;
618       }
619       else {
620         if (item.isOuterJoin())
621           cb.append(" left outer join ");
622         else {
623           cb.append(", ");
624
625           if (item.getJoinExpr() != null)
626             hasJoinExpr = true;
627         }
628       }
629
630       cb.append(item.getTable().getName());
631       cb.append(" ");
632       cb.append(item.getName());
633
634       if (item.getJoinExpr() != null && item.isOuterJoin()) {
635         cb.append(" on ");
636         item.getJoinExpr().generateJoin(cb);
637       }
638     }
639
640     if (hasJoinExpr || _where != null) {
641       boolean hasExpr = false;
642
643       cb.append(" where ");
644
645       for (int i = 0; i < _fromList.size(); i++) {
646         FromItem item = _fromList.get(i);
647         AmberExpr expr = item.getJoinExpr();
648
649         if (expr != null && ! item.isOuterJoin()) {
650           if (hasExpr)
651             cb.append(" and ");
652           hasExpr = true;
653
654           expr.generateJoin(cb);
655         }
656       }
657
658       if (_where != null) {
659         if (hasExpr)
660           cb.append(" and ");
661         hasExpr = true;
662
663         _where.generateWhere(cb);
664       }
665     }
666
667     if (_groupList != null) {
668       cb.append(" group by ");
669
670       for (int i = 0; i < _groupList.size(); i++) {
671         if (i != 0)
672           cb.append(", ");
673
674         _groupList.get(i).generateSelect(cb);
675       }
676     }
677
678     if (_having != null) {
679       boolean hasExpr = false;
680
681       cb.append(" having ");
682
683       /*
684       for (int i = 0; i < _fromList.size(); i++) {
685         FromItem item = _fromList.get(i);
686         AmberExpr expr = item.getJoinExpr();
687
688         if (expr != null && ! item.isOuterJoin()) {
689           if (hasExpr)
690             cb.append(" and ");
691           hasExpr = true;
692
693           expr.generateJoin(cb);
694         }
695       }
696       */

697
698       if (_having != null) {
699         if (hasExpr)
700           cb.append(" and ");
701         hasExpr = true;
702
703         _having.generateHaving(cb);
704       }
705     }
706
707     if (_orderList != null) {
708       cb.append(" order by ");
709
710       for (int i = 0; i < _orderList.size(); i++) {
711         if (i != 0)
712           cb.append(", ");
713
714         _orderList.get(i).generateSelect(cb);
715
716         if (Boolean.FALSE.equals(_ascList.get(i)))
717           cb.append(" desc");
718       }
719     }
720
721     return cb.toString();
722   }
723
724   /**
725    * Generates update
726    */

727   void registerUpdates(CachedQuery query)
728   {
729     for (int i = 0; i < _fromList.size(); i++) {
730       FromItem item = _fromList.get(i);
731
732       AmberEntityHome home = item.getEntityHome();
733
734       CacheUpdate update = new TableCacheUpdate(query);
735
736       home.addUpdate(update);
737     }
738   }
739
740   /**
741    * Returns true if modifying the given table modifies a cached query.
742    */

743   public boolean invalidateTable(String JavaDoc table)
744   {
745     for (int i = _fromList.size() - 1; i >= 0; i--) {
746       FromItem from = _fromList.get(i);
747
748       if (table.equals(from.getTable().getName()))
749         return true;
750     }
751
752     return false;
753   }
754
755   /**
756    * Debug view.
757    */

758   public String JavaDoc toString()
759   {
760     return "SelectQuery[" + getQueryString() + "]";
761   }
762 }
763
Popular Tags