KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > cmp > jdbc > JDBCAbstractQueryCommand


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb.plugins.cmp.jdbc;
23
24 import java.lang.reflect.Method JavaDoc;
25 import java.sql.Connection JavaDoc;
26 import java.sql.PreparedStatement JavaDoc;
27 import java.sql.ResultSet JavaDoc;
28 import java.sql.SQLException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
33 import java.util.Collections JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.AbstractCollection JavaDoc;
36 import java.util.NoSuchElementException JavaDoc;
37 import java.util.Set JavaDoc;
38 import javax.ejb.FinderException JavaDoc;
39 import javax.ejb.EJBException JavaDoc;
40 import javax.transaction.Synchronization JavaDoc;
41
42 import org.jboss.deployment.DeploymentException;
43 import org.jboss.ejb.EntityEnterpriseContext;
44 import org.jboss.ejb.GenericEntityObjectFactory;
45 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
46 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
47 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge;
48 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
49 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData;
50 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCLeftJoinMetaData;
51 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData;
52 import org.jboss.ejb.plugins.cmp.ejbql.SelectFunction;
53 import org.jboss.logging.Logger;
54
55 /**
56  * Abstract superclass of finder commands that return collections.
57  * Provides the handleResult() implementation that these all need.
58  *
59  * @author <a HREF="mailto:dain@daingroup.com">Dain Sundstrom</a>
60  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard �berg</a>
61  * @author <a HREF="mailto:marc.fleury@telkel.com">Marc Fleury</a>
62  * @author <a HREF="mailto:shevlandj@kpi.com.au">Joe Shevland</a>
63  * @author <a HREF="mailto:justin@j-m-f.demon.co.uk">Justin Forder</a>
64  * @author <a HREF="mailto:alex@jboss.org">Alex Loubyansky</a>
65  * @version $Revision: 43506 $
66  */

67 public abstract class JDBCAbstractQueryCommand implements JDBCQueryCommand
68 {
69    private JDBCQueryMetaData queryMetaData;
70    protected Logger log;
71
72    private JDBCStoreManager selectManager;
73    private JDBCEntityBridge selectEntity;
74    private JDBCCMPFieldBridge selectField;
75    private SelectFunction selectFunction;
76    private boolean[] eagerLoadMask;
77    private String JavaDoc eagerLoadGroup;
78    private String JavaDoc sql;
79    private int offsetParam;
80    private int offsetValue;
81    private int limitParam;
82    private int limitValue;
83    private List JavaDoc parameters = new ArrayList JavaDoc(0);
84    private List JavaDoc onFindCMRList = Collections.EMPTY_LIST;
85    private QueryCollectionFactory collectionFactory;
86
87    public JDBCAbstractQueryCommand(JDBCStoreManager manager, JDBCQueryMetaData q)
88       throws DeploymentException
89    {
90       this.log = Logger.getLogger(this.getClass().getName() +
91          "." +
92          manager.getMetaData().getName() +
93          "#" +
94          q.getMethod().getName());
95
96       queryMetaData = q;
97       collectionFactory = q.isLazyResultSetLoading() ?
98          new LazyCollectionFactory() :
99          (QueryCollectionFactory) new EagerCollectionFactory();
100
101 // setDefaultOffset(q.getOffsetParam());
102
// setDefaultLimit(q.getLimitParam());
103
setSelectEntity((JDBCEntityBridge) manager.getEntityBridge());
104    }
105
106    public void setOffsetValue(int offsetValue)
107    {
108       this.offsetValue = offsetValue;
109    }
110
111    public void setLimitValue(int limitValue)
112    {
113       this.limitValue = limitValue;
114    }
115
116    public void setOffsetParam(int offsetParam)
117    {
118       this.offsetParam = offsetParam;
119    }
120
121    public void setLimitParam(int limitParam)
122    {
123       this.limitParam = limitParam;
124    }
125
126    public void setOnFindCMRList(List JavaDoc onFindCMRList)
127    {
128       this.onFindCMRList = onFindCMRList;
129    }
130
131    public JDBCStoreManager getSelectManager()
132    {
133       return selectManager;
134    }
135
136    public Collection JavaDoc execute(Method JavaDoc finderMethod,
137                              Object JavaDoc[] args,
138                              EntityEnterpriseContext ctx,
139                              GenericEntityObjectFactory factory)
140       throws FinderException JavaDoc
141    {
142       int offset = toInt(args, offsetParam, offsetValue);
143       int limit = toInt(args, limitParam, limitValue);
144       return execute(sql,
145          args,
146          offset,
147          limit,
148          selectEntity,
149          selectField,
150          selectFunction,
151          selectManager,
152          eagerLoadMask,
153          parameters,
154          onFindCMRList,
155          queryMetaData,
156          factory,
157          log);
158    }
159
160    protected static int toInt(Object JavaDoc[] params, int paramNumber, int defaultValue)
161    {
162       if(paramNumber == 0)
163       {
164          return defaultValue;
165       }
166       Integer JavaDoc arg = (Integer JavaDoc) params[paramNumber - 1];
167       return arg.intValue();
168    }
169
170    protected Collection JavaDoc execute(String JavaDoc sql,
171                                 Object JavaDoc[] args,
172                                 int offset,
173                                 int limit,
174                                 JDBCEntityBridge selectEntity,
175                                 JDBCCMPFieldBridge selectField,
176                                 SelectFunction selectFunction,
177                                 JDBCStoreManager selectManager,
178                                 boolean[] eagerLoadMask,
179                                 List JavaDoc parameters,
180                                 List JavaDoc onFindCMRList,
181                                 JDBCQueryMetaData queryMetaData,
182                                 GenericEntityObjectFactory factory,
183                                 Logger log)
184       throws FinderException JavaDoc
185    {
186       int count = offset;
187       Connection JavaDoc con = null;
188       PreparedStatement JavaDoc ps = null;
189       ResultSet JavaDoc rs = null;
190       final JDBCEntityBridge entityBridge = (JDBCEntityBridge)selectManager.getEntityBridge();
191       boolean throwRuntimeExceptions = entityBridge.getMetaData().getThrowRuntimeExceptions();
192
193       // if metadata is true, the getconnection is done inside
194
// its own try catch block to throw a runtime exception (EJBException)
195
if (throwRuntimeExceptions)
196       {
197           try
198           {
199               con = entityBridge.getDataSource().getConnection();
200           }
201           catch (SQLException JavaDoc sqle)
202           {
203               javax.ejb.EJBException JavaDoc ejbe = new javax.ejb.EJBException JavaDoc("Could not get a connection; " + sqle);
204               ejbe.initCause(sqle);
205               throw ejbe;
206           }
207       }
208
209
210       try
211       {
212          // create the statement
213
if(log.isDebugEnabled())
214          {
215             log.debug("Executing SQL: " + sql);
216             if(limit != 0 || offset != 0)
217             {
218                log.debug("Query offset=" + offset + ", limit=" + limit);
219             }
220          }
221
222          // if metadata is false, the getconnection is done inside this try catch block
223
if ( ! throwRuntimeExceptions)
224          {
225              con = entityBridge.getDataSource().getConnection();
226          }
227          ps = con.prepareStatement(sql);
228
229          // Set the fetch size of the statement
230
if(entityBridge.getFetchSize() > 0)
231          {
232             ps.setFetchSize(entityBridge.getFetchSize());
233          }
234
235          // set the parameters
236
for(int i = 0; i < parameters.size(); i++)
237          {
238             QueryParameter parameter = (QueryParameter) parameters.get(i);
239             parameter.set(log, ps, i + 1, args);
240          }
241
242          // execute statement
243
rs = ps.executeQuery();
244
245          // skip 'offset' results
246
while(count > 0 && rs.next())
247          {
248             count--;
249          }
250
251          count = limit;
252       }
253       catch(Exception JavaDoc e)
254       {
255          JDBCUtil.safeClose(rs);
256          JDBCUtil.safeClose(ps);
257          JDBCUtil.safeClose(con);
258
259          log.error("Find failed", e);
260          FinderException JavaDoc fe = new FinderException JavaDoc("Find failed: " + e);
261          fe.initCause(e);
262          throw fe;
263       }
264
265       return collectionFactory.createCollection(con,
266          ps,
267          rs,
268          limit,
269          count,
270          selectEntity,
271          selectField,
272          selectFunction,
273          selectManager,
274          onFindCMRList,
275          eagerLoadMask,
276          factory);
277    }
278
279    protected Logger getLog()
280    {
281       return log;
282    }
283
284    protected void setSQL(String JavaDoc sql)
285    {
286       this.sql = sql;
287       if(log.isDebugEnabled())
288       {
289          log.debug("SQL: " + sql);
290       }
291    }
292
293    protected void setParameterList(List JavaDoc p)
294    {
295       for(int i = 0; i < p.size(); i++)
296       {
297          if(!(p.get(i) instanceof QueryParameter))
298          {
299             throw new IllegalArgumentException JavaDoc("Element " +
300                i +
301                " of list " +
302                "is not an instance of QueryParameter, but " +
303                p.get(i).getClass().getName());
304          }
305       }
306       parameters = new ArrayList JavaDoc(p);
307    }
308
309    protected JDBCEntityBridge getSelectEntity()
310    {
311       return selectEntity;
312    }
313
314    protected void setSelectEntity(JDBCEntityBridge selectEntity)
315       throws DeploymentException
316    {
317       if(queryMetaData.getMethod().getName().startsWith("find") &&
318          this.selectEntity != null && this.selectEntity != selectEntity)
319       {
320          throw new DeploymentException("Finder " + queryMetaData.getMethod().getName() +
321             " defined on " + this.selectEntity.getEntityName() +
322             " should return only instances of " + this.selectEntity.getEntityName() +
323             " but the query results in instances of " + selectEntity.getEntityName());
324       }
325
326       this.selectField = null;
327       this.selectFunction = null;
328       this.selectEntity = selectEntity;
329       this.selectManager = (JDBCStoreManager) selectEntity.getManager();
330    }
331
332    protected JDBCCMPFieldBridge getSelectField()
333    {
334       return selectField;
335    }
336
337    protected void setSelectField(JDBCCMPFieldBridge selectField)
338    {
339       this.selectEntity = null;
340       this.selectFunction = null;
341       this.selectField = selectField;
342       this.selectManager = (JDBCStoreManager) selectField.getManager();
343    }
344
345    protected void setSelectFunction(SelectFunction func, JDBCStoreManager manager)
346    {
347       this.selectEntity = null;
348       this.selectField = null;
349       this.selectFunction = func;
350       this.selectManager = manager;
351    }
352
353    protected void setEagerLoadGroup(String JavaDoc eagerLoadGroup)
354    {
355       this.eagerLoadGroup = eagerLoadGroup;
356       this.eagerLoadMask = selectEntity.getLoadGroupMask(eagerLoadGroup);
357    }
358
359    protected String JavaDoc getEagerLoadGroup()
360    {
361       return eagerLoadGroup;
362    }
363
364    protected boolean[] getEagerLoadMask()
365    {
366       return this.eagerLoadMask;
367    }
368
369    /**
370     * Replaces the parameters in the specifiec sql with question marks, and
371     * initializes the parameter setting code. Parameters are encoded in curly
372     * brackets use a zero based index.
373     *
374     * @param sql the sql statement that is parsed for parameters
375     * @return the original sql statement with the parameters replaced with a
376     * question mark
377     * @throws DeploymentException if a error occures while parsing the sql
378     */

379    protected String JavaDoc parseParameters(String JavaDoc sql) throws DeploymentException
380    {
381       StringBuffer JavaDoc sqlBuf = new StringBuffer JavaDoc();
382       ArrayList JavaDoc params = new ArrayList JavaDoc();
383
384       // Replace placeholders {0} with ?
385
if(sql != null)
386       {
387          sql = sql.trim();
388
389          StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(sql, "{}", true);
390          while(tokens.hasMoreTokens())
391          {
392             String JavaDoc token = tokens.nextToken();
393             if(token.equals("{"))
394             {
395                token = tokens.nextToken();
396                if(Character.isDigit(token.charAt(0)))
397                {
398                   QueryParameter parameter = new QueryParameter(selectManager, queryMetaData.getMethod(), token);
399
400                   // of if we are here we can assume that we have
401
// a parameter and not a function
402
sqlBuf.append("?");
403                   params.add(parameter);
404
405                   if(!tokens.nextToken().equals("}"))
406                   {
407                      throw new DeploymentException("Invalid parameter - missing closing '}' : " + sql);
408                   }
409                }
410                else
411                {
412                   // ok we don't have a parameter, we have a function
413
// push the tokens on the buffer and continue
414
sqlBuf.append("{").append(token);
415                }
416             }
417             else
418             {
419                // not parameter... just append it
420
sqlBuf.append(token);
421             }
422          }
423       }
424
425       parameters = params;
426
427       return sqlBuf.toString();
428    }
429
430    // Static
431

432    public static List JavaDoc getLeftJoinCMRNodes(JDBCEntityBridge entity, String JavaDoc path, Iterator JavaDoc leftJoinIter, Set JavaDoc declaredPaths)
433       throws DeploymentException
434    {
435       List JavaDoc leftJoinCMRNodes;
436
437       if(leftJoinIter.hasNext())
438       {
439          leftJoinCMRNodes = new ArrayList JavaDoc();
440          while(leftJoinIter.hasNext())
441          {
442             JDBCLeftJoinMetaData leftJoin = (JDBCLeftJoinMetaData) leftJoinIter.next();
443             JDBCCMRFieldBridge cmrField = entity.getCMRFieldByName(leftJoin.getCmrField());
444             if(cmrField == null)
445             {
446                throw new DeploymentException("cmr-field in left-join was not found: cmr-field=" +
447                   leftJoin.getCmrField() + ", entity=" + entity.getEntityName());
448             }
449
450             List JavaDoc subNodes;
451             JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
452             String JavaDoc childPath = path + '.' + cmrField.getFieldName();
453             if(declaredPaths != null)
454             {
455                declaredPaths.add(childPath);
456             }
457
458             subNodes = getLeftJoinCMRNodes(relatedEntity, childPath, leftJoin.getLeftJoins(), declaredPaths);
459
460             boolean[] mask = relatedEntity.getLoadGroupMask(leftJoin.getEagerLoadGroup());
461             LeftJoinCMRNode node = new LeftJoinCMRNode(childPath, cmrField, mask, subNodes);
462             leftJoinCMRNodes.add(node);
463          }
464       }
465       else
466       {
467          leftJoinCMRNodes = Collections.EMPTY_LIST;
468       }
469
470       return leftJoinCMRNodes;
471    }
472
473    public static final void leftJoinCMRNodes(String JavaDoc alias,
474                                              List JavaDoc onFindCMRNodes,
475                                              AliasManager aliasManager,
476                                              StringBuffer JavaDoc sb)
477    {
478       for(int i = 0; i < onFindCMRNodes.size(); ++i)
479       {
480          LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(i);
481          JDBCCMRFieldBridge cmrField = node.cmrField;
482          JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
483          String JavaDoc relatedAlias = aliasManager.getAlias(node.path);
484
485          JDBCRelationMetaData relation = cmrField.getMetaData().getRelationMetaData();
486          if(relation.isTableMappingStyle())
487          {
488             String JavaDoc relTableAlias = aliasManager.getRelationTableAlias(node.path);
489             sb.append(" LEFT OUTER JOIN ")
490                .append(cmrField.getQualifiedTableName())
491                .append(' ')
492                .append(relTableAlias)
493                .append(" ON ");
494             SQLUtil.getRelationTableJoinClause(cmrField, alias, relTableAlias, sb);
495
496             sb.append(" LEFT OUTER JOIN ")
497                .append(relatedEntity.getQualifiedTableName())
498                .append(' ')
499                .append(relatedAlias)
500                .append(" ON ");
501             SQLUtil.getRelationTableJoinClause(cmrField.getRelatedCMRField(), relatedAlias, relTableAlias, sb);
502          }
503          else
504          {
505             // foreign key mapping style
506
sb.append(" LEFT OUTER JOIN ")
507                .append(relatedEntity.getQualifiedTableName())
508                .append(' ')
509                .append(relatedAlias)
510                .append(" ON ");
511             SQLUtil.getJoinClause(cmrField,
512                alias,
513                relatedAlias,
514                sb);
515          }
516
517          List JavaDoc subNodes = node.onFindCMRNodes;
518          if(!subNodes.isEmpty())
519          {
520             leftJoinCMRNodes(relatedAlias, subNodes, aliasManager, sb);
521          }
522       }
523    }
524
525    public static final void appendLeftJoinCMRColumnNames(List JavaDoc onFindCMRNodes,
526                                                          AliasManager aliasManager,
527                                                          StringBuffer JavaDoc sb)
528    {
529       for(int i = 0; i < onFindCMRNodes.size(); ++i)
530       {
531          LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(i);
532          JDBCCMRFieldBridge cmrField = node.cmrField;
533          JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
534          String JavaDoc childAlias = aliasManager.getAlias(node.path);
535
536          // primary key fields
537
SQLUtil.appendColumnNamesClause(relatedEntity.getPrimaryKeyFields(),
538             childAlias,
539             sb);
540
541          // eager load group
542
if(node.eagerLoadMask != null)
543          {
544             SQLUtil.appendColumnNamesClause(relatedEntity.getTableFields(),
545                node.eagerLoadMask,
546                childAlias,
547                sb);
548          }
549
550          List JavaDoc subNodes = node.onFindCMRNodes;
551          if(!subNodes.isEmpty())
552          {
553             appendLeftJoinCMRColumnNames(subNodes, aliasManager, sb);
554          }
555       }
556    }
557
558    private static int loadOnFindCMRFields(Object JavaDoc pk, List JavaDoc onFindCMRNodes, ResultSet JavaDoc rs, int index, Logger log)
559    {
560       Object JavaDoc[] ref = new Object JavaDoc[1];
561       for(int nodeInd = 0; nodeInd < onFindCMRNodes.size(); ++nodeInd)
562       {
563          LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(nodeInd);
564          JDBCCMRFieldBridge cmrField = node.cmrField;
565          ReadAheadCache myCache = cmrField.getJDBCStoreManager().getReadAheadCache();
566          JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
567          ReadAheadCache relatedCache = cmrField.getRelatedManager().getReadAheadCache();
568
569          // load related id
570
ref[0] = null;
571          index = relatedEntity.loadPrimaryKeyResults(rs, index, ref);
572          Object JavaDoc relatedId = ref[0];
573          boolean cacheRelatedData = relatedId != null;
574
575          if(pk != null)
576          {
577             if(cmrField.getMetaData().getRelatedRole().isMultiplicityOne())
578             {
579                // cacheRelatedData the value
580
myCache.addPreloadData(pk,
581                   cmrField,
582                   relatedId == null ? Collections.EMPTY_LIST : Collections.singletonList(relatedId));
583             }
584             else
585             {
586                Collection JavaDoc cachedValue = myCache.getCachedCMRValue(pk, cmrField);
587                if(cachedValue == null)
588                {
589                   cachedValue = new ArrayList JavaDoc();
590                   myCache.addPreloadData(pk, cmrField, cachedValue);
591                }
592
593                if(relatedId != null)
594                {
595                   if(cachedValue.contains(relatedId))
596                   {
597                      cacheRelatedData = false;
598                   }
599                   else
600                   {
601                      cachedValue.add(relatedId);
602                   }
603                }
604             }
605          }
606
607          // load eager load group
608
if(node.eagerLoadMask != null)
609          {
610             JDBCFieldBridge[] tableFields = relatedEntity.getTableFields();
611             for(int fieldInd = 0; fieldInd < tableFields.length; ++fieldInd)
612             {
613                if(node.eagerLoadMask[fieldInd])
614                {
615                   JDBCFieldBridge field = tableFields[fieldInd];
616                   ref[0] = null;
617                   index = field.loadArgumentResults(rs, index, ref);
618
619                   if(cacheRelatedData)
620                   {
621                      if(log.isTraceEnabled())
622                      {
623                         log.trace("Caching " +
624                            relatedEntity.getEntityName() +
625                            '[' +
626                            relatedId +
627                            "]." +
628                            field.getFieldName() + "=" + ref[0]);
629                      }
630                      relatedCache.addPreloadData(relatedId, field, ref[0]);
631                   }
632                }
633             }
634          }
635
636          List JavaDoc subNodes = node.onFindCMRNodes;
637          if(!subNodes.isEmpty())
638          {
639             index = loadOnFindCMRFields(relatedId, subNodes, rs, index, log);
640          }
641       }
642
643       return index;
644    }
645
646    public static final class LeftJoinCMRNode
647    {
648       public final String JavaDoc path;
649       public final JDBCCMRFieldBridge cmrField;
650       public final boolean[] eagerLoadMask;
651       public final List JavaDoc onFindCMRNodes;
652
653       public LeftJoinCMRNode(String JavaDoc path, JDBCCMRFieldBridge cmrField, boolean[] eagerLoadMask, List JavaDoc onFindCMRNodes)
654       {
655          this.path = path;
656          this.cmrField = cmrField;
657          this.eagerLoadMask = eagerLoadMask;
658          this.onFindCMRNodes = onFindCMRNodes;
659       }
660
661       public boolean equals(Object JavaDoc o)
662       {
663          boolean result;
664          if(o == this)
665          {
666             result = true;
667          }
668          else if(o instanceof LeftJoinCMRNode)
669          {
670             LeftJoinCMRNode other = (LeftJoinCMRNode) o;
671             result = cmrField == other.cmrField;
672          }
673          else
674          {
675             result = false;
676          }
677          return result;
678       }
679
680       public int hashCode()
681       {
682          return cmrField == null ? Integer.MIN_VALUE : cmrField.hashCode();
683       }
684
685       public String JavaDoc toString()
686       {
687          return '[' + cmrField.getFieldName() + ": " + onFindCMRNodes + ']';
688       }
689    }
690
691
692    interface QueryCollectionFactory
693    {
694       Collection JavaDoc createCollection(Connection JavaDoc con,
695                                   PreparedStatement JavaDoc ps,
696                                   ResultSet JavaDoc rs,
697                                   int limit, int count,
698                                   JDBCEntityBridge selectEntity,
699                                   JDBCCMPFieldBridge selectField,
700                                   SelectFunction selectFunction,
701                                   JDBCStoreManager selectManager,
702                                   List JavaDoc onFindCMRList,
703                                   boolean[] eagerLoadMask,
704                                   GenericEntityObjectFactory factory)
705          throws FinderException JavaDoc;
706    }
707
708    class EagerCollectionFactory
709       implements QueryCollectionFactory
710    {
711       public Collection JavaDoc createCollection(Connection JavaDoc con,
712                                          PreparedStatement JavaDoc ps,
713                                          ResultSet JavaDoc rs,
714                                          int limit, int count,
715                                          JDBCEntityBridge selectEntity,
716                                          JDBCCMPFieldBridge selectField,
717                                          SelectFunction selectFunction,
718                                          JDBCStoreManager selectManager,
719                                          List JavaDoc onFindCMRList,
720                                          boolean[] eagerLoadMask,
721                                          GenericEntityObjectFactory factory)
722          throws FinderException JavaDoc
723       {
724          try
725          {
726             List JavaDoc results = new ArrayList JavaDoc();
727
728             if(selectEntity != null)
729             {
730                ReadAheadCache selectReadAheadCache = selectManager.getReadAheadCache();
731                List JavaDoc ids = new ArrayList JavaDoc();
732
733                boolean loadOnFindCmr = !onFindCMRList.isEmpty();
734                Object JavaDoc[] ref = new Object JavaDoc[1];
735                Object JavaDoc prevPk = null;
736
737                while((limit == 0 || count-- > 0) && rs.next())
738                {
739                   int index = 1;
740
741                   // get the pk
742
index = selectEntity.loadPrimaryKeyResults(rs, index, ref);
743                   Object JavaDoc pk = ref[0];
744
745                   boolean addPk = (loadOnFindCmr ? !pk.equals(prevPk) : true);
746                   if(addPk)
747                   {
748                      ids.add(pk);
749                      results.add(factory.getEntityEJBObject(pk));
750                      prevPk = pk;
751                   }
752
753                   // read the preload fields
754
if(eagerLoadMask != null)
755                   {
756                      JDBCFieldBridge[] tableFields = selectEntity.getTableFields();
757                      for(int i = 0; i < eagerLoadMask.length; i++)
758                      {
759                         if(eagerLoadMask[i])
760                         {
761                            JDBCFieldBridge field = tableFields[i];
762                            ref[0] = null;
763
764                            // read the value and store it in the readahead cache
765
index = field.loadArgumentResults(rs, index, ref);
766
767                            if(addPk)
768                            {
769                               selectReadAheadCache.addPreloadData(pk, field, ref[0]);
770                            }
771                         }
772                      }
773
774                      if(!onFindCMRList.isEmpty())
775                      {
776                         index = loadOnFindCMRFields(pk, onFindCMRList, rs, index, log);
777                      }
778                   }
779                }
780
781                // add the results list to the cache
782
selectReadAheadCache.addFinderResults(ids, queryMetaData.getReadAhead());
783             }
784             else if(selectField != null)
785             {
786                // load the field
787
Object JavaDoc[] valueRef = new Object JavaDoc[1];
788                while((limit == 0 || count-- > 0) && rs.next())
789                {
790                   valueRef[0] = null;
791                   selectField.loadArgumentResults(rs, 1, valueRef);
792                   results.add(valueRef[0]);
793                }
794             }
795             else
796             {
797                while(rs.next())
798                {
799                   results.add(selectFunction.readResult(rs));
800                }
801             }
802
803             if(log.isDebugEnabled() && limit != 0 && count == 0)
804             {
805                log.debug("Query result was limited to " + limit + " row(s)");
806             }
807
808             return results;
809          }
810          catch(Exception JavaDoc e)
811          {
812             log.error("Find failed", e);
813             throw new FinderException JavaDoc("Find failed: " + e);
814          }
815          finally
816          {
817             JDBCUtil.safeClose(rs);
818             JDBCUtil.safeClose(ps);
819             JDBCUtil.safeClose(con);
820          }
821       }
822
823    }
824
825    class LazyCollectionFactory
826       implements QueryCollectionFactory
827    {
828       public Collection JavaDoc createCollection(Connection JavaDoc con,
829                                          PreparedStatement JavaDoc ps,
830                                          ResultSet JavaDoc rs,
831                                          int limit, int count,
832                                          JDBCEntityBridge selectEntity,
833                                          JDBCCMPFieldBridge selectField,
834                                          SelectFunction selectFunction,
835                                          JDBCStoreManager selectManager,
836                                          List JavaDoc onFindCMRList,
837                                          boolean[] eagerLoadMask,
838                                          GenericEntityObjectFactory factory)
839          throws FinderException JavaDoc
840       {
841          return new LazyCollection(con,
842             ps,
843             rs,
844             limit,
845             count,
846             selectEntity,
847             selectField,
848             selectFunction,
849             selectManager,
850             eagerLoadMask,
851             factory);
852       }
853
854       private class LazyCollection extends AbstractCollection JavaDoc
855       {
856          private final Connection JavaDoc con;
857          private final PreparedStatement JavaDoc ps;
858          private final ResultSet JavaDoc rs;
859          private final int limit;
860          private int count;
861          private final JDBCEntityBridge selectEntity;
862          private final JDBCCMPFieldBridge selectField;
863          private final SelectFunction selectFunction;
864          private final JDBCStoreManager selectManager;
865          private final boolean[] eagerLoadMask;
866          private final GenericEntityObjectFactory factory;
867
868          private Object JavaDoc prevPk;
869          private Object JavaDoc curPk;
870          private Object JavaDoc currentResult;
871
872          Object JavaDoc[] ref = new Object JavaDoc[1];
873
874          boolean loadOnFindCmr;
875
876          private List JavaDoc results = null;
877          private Iterator JavaDoc firstIterator;
878          private int size;
879          private boolean resourcesClosed;
880
881          public LazyCollection(final Connection JavaDoc con,
882                                final PreparedStatement JavaDoc ps,
883                                final ResultSet JavaDoc rs,
884                                int limit,
885                                int count,
886                                JDBCEntityBridge selectEntity,
887                                JDBCCMPFieldBridge selectField,
888                                SelectFunction selectFunction,
889                                JDBCStoreManager selectManager,
890                                boolean[] eagerLoadMask,
891                                GenericEntityObjectFactory factory)
892          {
893             this.con = con;
894             this.ps = ps;
895             this.rs = rs;
896             this.limit = limit;
897             this.count = count;
898             this.selectEntity = selectEntity;
899             this.selectField = selectField;
900             this.selectFunction = selectFunction;
901             this.selectManager = selectManager;
902             this.eagerLoadMask = eagerLoadMask;
903             this.factory = factory;
904             loadOnFindCmr = !onFindCMRList.isEmpty();
905
906             firstIterator = getFirstIterator();
907             if(firstIterator.hasNext())
908             {
909                try
910                {
911                   size = rs.getInt(1);
912                }
913                catch(SQLException JavaDoc e)
914                {
915                   throw new EJBException JavaDoc("Failed to read ResultSet.", e);
916                }
917
918                if(limit > 0 && size > limit)
919                {
920                   size = limit;
921                }
922             }
923
924             if(size < 1)
925             {
926                firstIterator = null;
927                results = new ArrayList JavaDoc(0);
928                closeResources();
929             }
930             else
931             {
932                results = new ArrayList JavaDoc(size);
933                try
934                {
935                   selectManager.getContainer().getTransactionManager().getTransaction().registerSynchronization(new Synchronization JavaDoc()
936                   {
937                      public void beforeCompletion()
938                      {
939                         closeResources();
940                      }
941
942                      public void afterCompletion(int status)
943                      {
944                         closeResources();
945                      }
946                   });
947                }
948                catch(Exception JavaDoc e)
949                {
950                   throw new EJBException JavaDoc("Failed to obtain current transaction", e);
951                }
952             }
953          }
954
955          private void closeResources()
956          {
957             if(!resourcesClosed)
958             {
959                JDBCUtil.safeClose(rs);
960                JDBCUtil.safeClose(ps);
961                JDBCUtil.safeClose(con);
962                resourcesClosed = true;
963             }
964          }
965
966          public Iterator JavaDoc iterator()
967          {
968             return firstIterator != null ? firstIterator : results.iterator();
969          }
970
971          public int size()
972          {
973             return firstIterator != null ? size : results.size();
974          }
975
976          public boolean add(Object JavaDoc o)
977          {
978             if(firstIterator == null)
979             {
980                return results.add(o);
981             }
982             throw new IllegalStateException JavaDoc("Can't modify collection while the first iterator is not exhausted.");
983          }
984
985          public boolean remove(Object JavaDoc o)
986          {
987             if(firstIterator == null)
988             {
989                return results.remove(o);
990             }
991             throw new IllegalStateException JavaDoc("Can't modify collection while the first iterator is not exhausted.");
992          }
993
994          private boolean hasNextResult()
995          {
996             try
997             {
998                boolean has = (limit == 0 || count-- > 0) && rs.next();
999                if(!has)
1000               {
1001                  if(log.isTraceEnabled())
1002                  {
1003                     log.trace("first iterator exhausted!");
1004                  }
1005                  firstIterator = null;
1006                  closeResources();
1007               }
1008               return has;
1009            }
1010            catch(Exception JavaDoc e)
1011            {
1012               log.error("Failed to read ResultSet.", e);
1013               throw new EJBException JavaDoc("Failed to read ResultSet: " + e.getMessage());
1014            }
1015         }
1016
1017         private Object JavaDoc readNext()
1018         {
1019            try
1020            {
1021               if(selectEntity != null)
1022               {
1023                  ReadAheadCache selectReadAheadCache = selectManager.getReadAheadCache();
1024
1025                  // first one is size
1026
int index = 2;
1027
1028                  // get the pk
1029
index = selectEntity.loadPrimaryKeyResults(rs, index, ref);
1030                  curPk = ref[0];
1031
1032                  boolean addPk = (loadOnFindCmr ? !curPk.equals(prevPk) : true);
1033                  if(addPk)
1034                  {
1035                     prevPk = curPk;
1036                     currentResult = factory.getEntityEJBObject(curPk);
1037                  }
1038
1039                  // read the preload fields
1040
if(eagerLoadMask != null)
1041                  {
1042                     JDBCFieldBridge[] tableFields = selectEntity.getTableFields();
1043                     for(int i = 0; i < eagerLoadMask.length; i++)
1044                     {
1045                        if(eagerLoadMask[i])
1046                        {
1047                           JDBCFieldBridge field = tableFields[i];
1048                           ref[0] = null;
1049
1050                           // read the value and store it in the readahead cache
1051
index = field.loadArgumentResults(rs, index, ref);
1052
1053                           if(addPk)
1054                           {
1055                              selectReadAheadCache.addPreloadData(curPk, field, ref[0]);
1056                           }
1057                        }
1058                     }
1059
1060                     if(!onFindCMRList.isEmpty())
1061                     {
1062                        index = loadOnFindCMRFields(curPk, onFindCMRList, rs, index, log);
1063                     }
1064                  }
1065               }
1066               else if(selectField != null)
1067               {
1068                  // load the field
1069
selectField.loadArgumentResults(rs, 2, ref);
1070                  currentResult = ref[0];
1071               }
1072               else
1073               {
1074                  currentResult = selectFunction.readResult(rs);
1075               }
1076
1077               if(log.isTraceEnabled() && limit != 0 && count == 0)
1078               {
1079                  log.trace("Query result was limited to " + limit + " row(s)");
1080               }
1081
1082               return currentResult;
1083            }
1084            catch(Exception JavaDoc e)
1085            {
1086               log.error("Failed to read ResultSet", e);
1087               throw new EJBException JavaDoc("Failed to read ResultSet: " + e.getMessage());
1088            }
1089         }
1090
1091         private Iterator JavaDoc getFirstIterator()
1092         {
1093            return new Iterator JavaDoc()
1094            {
1095               private boolean hasNext;
1096               private Object JavaDoc cursor;
1097
1098               public boolean hasNext()
1099               {
1100                  return hasNext ? hasNext : (hasNext = hasNextResult());
1101               }
1102
1103               public Object JavaDoc next()
1104               {
1105                  if(!hasNext())
1106                  {
1107                     throw new NoSuchElementException JavaDoc();
1108                  }
1109                  hasNext = false;
1110
1111                  cursor = readNext();
1112                  results.add(cursor);
1113
1114                  return cursor;
1115               }
1116
1117               public void remove()
1118               {
1119                  --size;
1120                  results.remove(cursor);
1121               }
1122            };
1123         }
1124      }
1125   }
1126}
1127
Popular Tags