KickJava   Java API By Example, From Geeks To Geeks.

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


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.sql.Connection JavaDoc;
25 import java.sql.PreparedStatement JavaDoc;
26 import java.sql.ResultSet JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Collections JavaDoc;
34 import javax.ejb.EJBException JavaDoc;
35
36 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
37 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
38 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge;
39 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge;
40 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData;
41 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
42 import org.jboss.logging.Logger;
43 import org.jboss.deployment.DeploymentException;
44
45 /**
46  * Loads relations for a particular entity from a relation table.
47  *
48  * @author <a HREF="mailto:dain@daingroup.com">Dain Sundstrom</a>
49  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
50  * @version $Revision: 37459 $
51  */

52 public final class JDBCLoadRelationCommand
53 {
54    private final JDBCStoreManager manager;
55    private final JDBCEntityBridge entity;
56    private final Logger log;
57
58    public JDBCLoadRelationCommand(JDBCStoreManager manager)
59    {
60       this.manager = manager;
61       this.entity = (JDBCEntityBridge) manager.getEntityBridge();
62
63       // Create the Log
64
log = Logger.getLogger(
65          this.getClass().getName() +
66          "." +
67          manager.getMetaData().getName());
68    }
69
70    public Collection JavaDoc execute(JDBCCMRFieldBridge cmrField, Object JavaDoc pk)
71    {
72       JDBCCMRFieldBridge relatedCMRField = (JDBCCMRFieldBridge) cmrField.getRelatedCMRField();
73
74       // get the read ahead cahces
75
ReadAheadCache readAheadCache = manager.getReadAheadCache();
76       ReadAheadCache relatedReadAheadCache = cmrField.getRelatedManager().getReadAheadCache();
77
78       // get the finder results associated with this context, if it exists
79
ReadAheadCache.EntityReadAheadInfo info = readAheadCache.getEntityReadAheadInfo(pk);
80       List JavaDoc loadKeys = info.getLoadKeys();
81
82       Connection JavaDoc con = null;
83       PreparedStatement JavaDoc ps = null;
84       ResultSet JavaDoc rs = null;
85       try
86       {
87          // generate the sql
88
boolean[] preloadMask = getPreloadMask(cmrField);
89          String JavaDoc sql = getSQL(cmrField, preloadMask, loadKeys.size());
90
91          // create the statement
92
if(log.isDebugEnabled())
93             log.debug("load relation SQL: " + sql);
94
95          // get the connection
96
con = cmrField.getDataSource().getConnection();
97          ps = con.prepareStatement(sql.toString());
98
99          // Set the fetch size of the statement
100
if(entity.getFetchSize() > 0)
101          {
102             ps.setFetchSize(entity.getFetchSize());
103          }
104
105          // get the load fields
106
JDBCCMPFieldBridge[] myKeyFields = getMyKeyFields(cmrField);
107          JDBCCMPFieldBridge[] relatedKeyFields = getRelatedKeyFields(cmrField);
108
109          // set the parameters
110
int paramIndex = 1;
111          for(int i = 0; i < loadKeys.size(); i++)
112          {
113             Object JavaDoc key = loadKeys.get(i);
114             for(int j = 0; j < myKeyFields.length; ++j)
115                paramIndex = myKeyFields[j].setPrimaryKeyParameters(ps, paramIndex, key);
116          }
117
118          // execute statement
119
rs = ps.executeQuery();
120
121          // initialize the results map
122
Map JavaDoc resultsMap = new HashMap JavaDoc(loadKeys.size());
123          for(int i = 0; i < loadKeys.size(); ++i)
124          {
125             resultsMap.put(loadKeys.get(i), new ArrayList JavaDoc());
126          }
127
128          // load the results
129
Object JavaDoc[] ref = new Object JavaDoc[1];
130          while(rs.next())
131          {
132             // reset the column index for this row
133
int index = 1;
134
135             // ref must be reset to null before each load
136
ref[0] = null;
137
138             // if we are loading more then one entity, load the pk from the row
139
Object JavaDoc loadedPk = pk;
140             if(loadKeys.size() > 1)
141             {
142                // load the pk
143
for(int i = 0; i < myKeyFields.length; ++i)
144                {
145                   index = myKeyFields[i].loadPrimaryKeyResults(rs, index, ref);
146                   if(ref[0] == null)
147                   {
148                      break;
149                   }
150                }
151                loadedPk = ref[0];
152             }
153
154             // load the fk
155
ref[0] = null;
156             for(int i = 0; i < relatedKeyFields.length; ++i)
157             {
158                index = relatedKeyFields[i].loadPrimaryKeyResults(rs, index, ref);
159                if(ref[0] == null)
160                {
161                   break;
162                }
163             }
164             Object JavaDoc loadedFk = ref[0];
165
166             if(loadedFk != null)
167             {
168                // add this value to the list for loadedPk
169
List JavaDoc results = (List JavaDoc)resultsMap.get(loadedPk);
170                results.add(loadedFk);
171
172                // if the related cmr field is single valued we can pre-load
173
// the reverse relationship
174
if(relatedCMRField.isSingleValued())
175                {
176                   relatedReadAheadCache.addPreloadData(
177                      loadedFk,
178                      relatedCMRField,
179                      Collections.singletonList(loadedPk));
180                }
181
182                // read the preload fields
183
if(preloadMask != null)
184                {
185                   JDBCFieldBridge[] relatedFields = cmrField.getRelatedJDBCEntity().getTableFields();
186                   for(int i = 0; i < relatedFields.length; ++i)
187                   {
188                      if(preloadMask[i])
189                      {
190                         JDBCFieldBridge field = relatedFields[i];
191                         ref[0] = null;
192
193                         // read the value and store it in the readahead cache
194
index = field.loadArgumentResults(rs, index, ref);
195                         relatedReadAheadCache.addPreloadData(loadedFk, field, ref[0]);
196                      }
197                   }
198                }
199             }
200          }
201
202          // set all of the preloaded values
203
JDBCReadAheadMetaData readAhead = relatedCMRField.getReadAhead();
204          for(Iterator JavaDoc iter = resultsMap.keySet().iterator(); iter.hasNext();)
205          {
206             Object JavaDoc key = iter.next();
207
208             // get the results for this key
209
List JavaDoc results = (List JavaDoc)resultsMap.get(key);
210
211             // store the results list for readahead on-load
212
relatedReadAheadCache.addFinderResults(results, readAhead);
213
214             // store the preloaded relationship (unless this is the realts we
215
// are actually after)
216
if(!key.equals(pk))
217             {
218                readAheadCache.addPreloadData(key, cmrField, results);
219             }
220          }
221
222          // success, return the results
223
return (List JavaDoc)resultsMap.get(pk);
224       }
225       catch(EJBException JavaDoc e)
226       {
227          throw e;
228       }
229       catch(Exception JavaDoc e)
230       {
231          throw new EJBException JavaDoc("Load relation failed", e);
232       }
233       finally
234       {
235          JDBCUtil.safeClose(rs);
236          JDBCUtil.safeClose(ps);
237          JDBCUtil.safeClose(con);
238       }
239    }
240
241    private String JavaDoc getSQL(JDBCCMRFieldBridge cmrField, boolean[] preloadMask, int keyCount) throws DeploymentException
242    {
243       JDBCCMPFieldBridge[] myKeyFields = getMyKeyFields(cmrField);
244       JDBCCMPFieldBridge[] relatedKeyFields = getRelatedKeyFields(cmrField);
245       String JavaDoc relationTable = getQualifiedRelationTable(cmrField);
246       JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
247       String JavaDoc relatedTable = relatedEntity.getQualifiedTableName();
248
249       // do we need to join the relation table and the related table
250
boolean join = ((preloadMask != null) || cmrField.allFkFieldsMappedToPkFields())
251          && (relatedKeyFields != relatedEntity.getPrimaryKeyFields());
252
253       // aliases for the tables, only required if we are joining the tables
254
String JavaDoc relationTableAlias;
255       String JavaDoc relatedTableAlias;
256       if(join)
257       {
258          relationTableAlias = getRelationTable(cmrField);
259          relatedTableAlias = (
260             relatedTable.equals(relationTable) ? getRelationTable(cmrField) + '_' + cmrField.getFieldName() : relatedEntity.getTableName()
261          );
262       }
263       else
264       {
265          relationTableAlias = "";
266          relatedTableAlias = "";
267       }
268
269       JDBCFunctionMappingMetaData selectTemplate = getSelectTemplate(cmrField);
270       return selectTemplate == null ?
271          getPlainSQL(
272             keyCount,
273             myKeyFields,
274             relationTableAlias,
275             relatedKeyFields,
276             preloadMask,
277             cmrField,
278             relatedTableAlias,
279             relationTable,
280             join,
281             relatedTable)
282          :
283          getSQLByTemplate(
284             keyCount,
285             myKeyFields,
286             relationTableAlias,
287             relatedKeyFields,
288             preloadMask,
289             cmrField,
290             relatedTableAlias,
291             relationTable,
292             join,
293             relatedTable,
294             selectTemplate);
295    }
296
297    private JDBCCMPFieldBridge[] getMyKeyFields(JDBCCMRFieldBridge cmrField)
298    {
299       if(cmrField.getRelationMetaData().isTableMappingStyle())
300       {
301          // relation table
302
return (JDBCCMPFieldBridge[]) cmrField.getTableKeyFields();
303       }
304       else if(cmrField.getRelatedCMRField().hasForeignKey())
305       {
306          // related has foreign key
307
return (JDBCCMPFieldBridge[]) cmrField.getRelatedCMRField().getForeignKeyFields();
308       }
309       else
310       {
311          // i have foreign key
312
return (JDBCCMPFieldBridge[])entity.getPrimaryKeyFields();
313       }
314    }
315
316    private static JDBCCMPFieldBridge[] getRelatedKeyFields(JDBCCMRFieldBridge cmrField)
317    {
318       if(cmrField.getRelationMetaData().isTableMappingStyle())
319       {
320          // relation table
321
return (JDBCCMPFieldBridge[]) cmrField.getRelatedCMRField().getTableKeyFields();
322       }
323       else if(cmrField.getRelatedCMRField().hasForeignKey())
324       {
325          // related has foreign key
326
return (JDBCCMPFieldBridge[])cmrField.getRelatedJDBCEntity().getPrimaryKeyFields();
327       }
328       else
329       {
330          // i have foreign key
331
return (JDBCCMPFieldBridge[]) cmrField.getForeignKeyFields();
332       }
333    }
334
335    private static boolean[] getPreloadMask(JDBCCMRFieldBridge cmrField)
336    {
337       boolean[] preloadMask = null;
338       if(cmrField.getReadAhead().isOnFind())
339       {
340          JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity();
341          String JavaDoc eagerLoadGroup = cmrField.getReadAhead().getEagerLoadGroup();
342          preloadMask = relatedEntity.getLoadGroupMask(eagerLoadGroup);
343       }
344       return preloadMask;
345    }
346
347    private String JavaDoc getQualifiedRelationTable(JDBCCMRFieldBridge cmrField)
348    {
349       if(cmrField.getRelationMetaData().isTableMappingStyle())
350       {
351          // relation table
352
return cmrField.getQualifiedTableName();
353       }
354       else if(cmrField.getRelatedCMRField().hasForeignKey())
355       {
356          // related has foreign key
357
return cmrField.getRelatedJDBCEntity().getQualifiedTableName();
358       }
359       else
360       {
361          // i have foreign key
362
return entity.getQualifiedTableName();
363       }
364    }
365
366    private String JavaDoc getRelationTable(JDBCCMRFieldBridge cmrField)
367    {
368       if(cmrField.getRelationMetaData().isTableMappingStyle())
369       {
370          // relation table
371
return cmrField.getTableName();
372       }
373       else if(cmrField.getRelatedCMRField().hasForeignKey())
374       {
375          // related has foreign key
376
return cmrField.getRelatedJDBCEntity().getTableName();
377       }
378       else
379       {
380          // i have foreign key
381
return entity.getTableName();
382       }
383    }
384
385    private JDBCFunctionMappingMetaData getSelectTemplate(JDBCCMRFieldBridge cmrField) throws DeploymentException
386    {
387
388       JDBCFunctionMappingMetaData selectTemplate = null;
389       if(cmrField.getRelationMetaData().isTableMappingStyle())
390       {
391          // relation table
392
if(cmrField.getRelationMetaData().hasRowLocking())
393          {
394             selectTemplate =
395                cmrField.getRelationMetaData().getTypeMapping().getRowLockingTemplate();
396             if(selectTemplate == null)
397             {
398                throw new IllegalStateException JavaDoc(
399                   "row-locking is not allowed for this type of datastore");
400             }
401          }
402       }
403       else if(cmrField.getRelatedCMRField().hasForeignKey())
404       {
405          // related has foreign key
406
if(cmrField.getRelatedJDBCEntity().getMetaData().hasRowLocking())
407          {
408             selectTemplate =
409                cmrField.getRelatedJDBCEntity().getMetaData().getTypeMapping().getRowLockingTemplate();
410             if(selectTemplate == null)
411             {
412                throw new IllegalStateException JavaDoc(
413                   "row-locking is not allowed for this type of datastore");
414             }
415          }
416       }
417       else
418       {
419          // i have foreign key
420
if(entity.getMetaData().hasRowLocking())
421          {
422             selectTemplate = entity.getMetaData().getTypeMapping().getRowLockingTemplate();
423             if(selectTemplate == null)
424             {
425                throw new IllegalStateException JavaDoc(
426                   "row-locking is not allowed for this type of datastore");
427             }
428          }
429       }
430       return selectTemplate;
431    }
432
433    private static String JavaDoc getPlainSQL(int keyCount,
434                                      JDBCCMPFieldBridge[] myKeyFields,
435                                      String JavaDoc relationTableAlias,
436                                      JDBCCMPFieldBridge[] relatedKeyFields,
437                                      boolean[] preloadMask,
438                                      JDBCCMRFieldBridge cmrField,
439                                      String JavaDoc relatedTableAlias,
440                                      String JavaDoc relationTable,
441                                      boolean join,
442                                      String JavaDoc relatedTable)
443    {
444       //
445
// column names clause
446
//
447
StringBuffer JavaDoc sql = new StringBuffer JavaDoc(400);
448       sql.append(SQLUtil.SELECT);
449
450       if(keyCount > 1)
451       {
452          SQLUtil.getColumnNamesClause(myKeyFields, relationTableAlias, sql)
453             .append(SQLUtil.COMMA);
454       }
455       SQLUtil.getColumnNamesClause(relatedKeyFields, relationTableAlias, sql);
456
457       if(preloadMask != null)
458       {
459          SQLUtil.appendColumnNamesClause(
460             cmrField.getRelatedJDBCEntity().getTableFields(),
461             preloadMask,
462             relatedTableAlias,
463             sql);
464       }
465
466       //
467
// from clause
468
//
469
sql.append(SQLUtil.FROM).append(relationTable);
470       if(join)
471       {
472          sql.append(' ')
473             .append(relationTableAlias)
474             .append(SQLUtil.COMMA)
475             .append(relatedTable)
476             .append(' ')
477             .append(relatedTableAlias);
478       }
479
480       //
481
// where clause
482
//
483
sql.append(SQLUtil.WHERE);
484       // add the join
485
if(join)
486       {
487          // join the tables
488
sql.append('(');
489          SQLUtil.getJoinClause(
490             relatedKeyFields,
491             relationTableAlias,
492             cmrField.getRelatedJDBCEntity().getPrimaryKeyFields(),
493             relatedTableAlias,
494             sql)
495             .append(')')
496             .append(SQLUtil.AND)
497             .append('(');
498       }
499
500       // add the keys
501
String JavaDoc pkWhere = SQLUtil.getWhereClause(myKeyFields, relationTableAlias, new StringBuffer JavaDoc(50)).toString();
502       for(int i = 0; i < keyCount; i++)
503       {
504          if(i > 0)
505             sql.append(SQLUtil.OR);
506          sql.append('(').append(pkWhere).append(')');
507       }
508
509       if(join)
510          sql.append(')');
511
512       return sql.toString();
513    }
514
515    private static String JavaDoc getSQLByTemplate(int keyCount,
516                                           JDBCCMPFieldBridge[] myKeyFields,
517                                           String JavaDoc relationTableAlias,
518                                           JDBCCMPFieldBridge[] relatedKeyFields,
519                                           boolean[] preloadMask,
520                                           JDBCCMRFieldBridge cmrField,
521                                           String JavaDoc relatedTableAlias,
522                                           String JavaDoc relationTable,
523                                           boolean join,
524                                           String JavaDoc relatedTable,
525                                           JDBCFunctionMappingMetaData selectTemplate)
526    {
527       //
528
// column names clause
529
//
530
StringBuffer JavaDoc columnNamesClause = new StringBuffer JavaDoc(100);
531       if(keyCount > 1)
532       {
533          SQLUtil.getColumnNamesClause(myKeyFields, relationTableAlias, columnNamesClause)
534             .append(SQLUtil.COMMA);
535       }
536       SQLUtil.getColumnNamesClause(relatedKeyFields, relationTableAlias, columnNamesClause);
537       if(preloadMask != null)
538       {
539          SQLUtil.appendColumnNamesClause(
540             cmrField.getRelatedJDBCEntity().getTableFields(),
541             preloadMask,
542             relatedTableAlias,
543             columnNamesClause);
544       }
545
546       //
547
// from clause
548
//
549
StringBuffer JavaDoc fromClause = new StringBuffer JavaDoc(100);
550       fromClause.append(relationTable);
551       if(join)
552       {
553          fromClause.append(' ')
554             .append(relationTableAlias)
555             .append(SQLUtil.COMMA)
556             .append(relatedTable)
557             .append(' ')
558             .append(relatedTableAlias);
559       }
560
561       //
562
// where clause
563
//
564
StringBuffer JavaDoc whereClause = new StringBuffer JavaDoc(150);
565       // add the join
566
if(join)
567       {
568          // join the tables
569
whereClause.append('(');
570          SQLUtil.getJoinClause(
571             relatedKeyFields,
572             relationTableAlias,
573             cmrField.getRelatedJDBCEntity().getPrimaryKeyFields(),
574             relatedTableAlias,
575             whereClause)
576             .append(')')
577             .append(SQLUtil.AND)
578             .append('(');
579       }
580
581       // add the keys
582
String JavaDoc pkWhere = SQLUtil.getWhereClause(myKeyFields, relationTableAlias, new StringBuffer JavaDoc(50)).toString();
583       for(int i = 0; i < keyCount; i++)
584       {
585          if(i > 0)
586          {
587             whereClause.append(SQLUtil.OR);
588          }
589          whereClause.append('(').append(pkWhere).append(')');
590       }
591
592       if(join)
593       {
594          whereClause.append(')');
595       }
596
597       //
598
// assemble pieces into final statement
599
//
600
String JavaDoc[] args = new String JavaDoc[]{
601          columnNamesClause.toString(),
602          fromClause.toString(),
603          whereClause.toString(),
604          null // order by
605
};
606       return selectTemplate.getFunctionSql(args, new StringBuffer JavaDoc(500)).toString();
607    }
608 }
609
Popular Tags