KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > accesslayer > sql > SqlGeneratorDefaultImpl


1 package org.apache.ojb.broker.accesslayer.sql;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.util.Collection JavaDoc;
19 import java.util.Enumeration JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.apache.commons.collections.map.ReferenceIdentityMap;
23 import org.apache.ojb.broker.metadata.ClassDescriptor;
24 import org.apache.ojb.broker.metadata.ProcedureDescriptor;
25 import org.apache.ojb.broker.platforms.Platform;
26 import org.apache.ojb.broker.query.BetweenCriteria;
27 import org.apache.ojb.broker.query.Criteria;
28 import org.apache.ojb.broker.query.ExistsCriteria;
29 import org.apache.ojb.broker.query.FieldCriteria;
30 import org.apache.ojb.broker.query.InCriteria;
31 import org.apache.ojb.broker.query.NullCriteria;
32 import org.apache.ojb.broker.query.Query;
33 import org.apache.ojb.broker.query.SelectionCriteria;
34 import org.apache.ojb.broker.query.SqlCriteria;
35 import org.apache.ojb.broker.util.logging.Logger;
36 import org.apache.ojb.broker.util.logging.LoggerFactory;
37
38 /**
39  * This Class is responsible for building sql statements
40  * Objects fields and their repective values are accessed by Java reflection
41  *
42  * @author <a HREF="mailto:thma@apache.org">Thomas Mahler<a>
43  * @author <a HREF="mailto:rgallagh@bellsouth.net">Ron Gallagher</a>
44  * @author <a HREF="mailto:rburt3@mchsi.com">Randall Burt</a>
45  * @version $Id: SqlGeneratorDefaultImpl.java,v 1.23.2.5 2005/12/21 22:23:44 tomdz Exp $
46  */

47 public class SqlGeneratorDefaultImpl implements SqlGenerator
48 {
49     private Logger logger = LoggerFactory.getLogger(SqlGeneratorDefaultImpl.class);
50     private Platform m_platform;
51     /*
52     arminw:
53     TODO: In ClassDescriptor we need support for "field change event" listener if we allow
54     to change metadata at runtime.
55     Further on we have to deal with weak references to allow GC of outdated Metadata classes
56     (key=cld of map have to be a weak reference and the metadata used in the SqlStatement classes too,
57     because inner class SqlForClass indirectly refer the key=cld in some cases and SqlStatement
58     implementation classes have references to metadata classes too).
59
60     Field changes are not reflected in this implementation!
61     */

62     /** Cache for {@link SqlForClass} instances, keyed per class descriptor. */
63     private Map JavaDoc sqlForClass = new ReferenceIdentityMap(ReferenceIdentityMap.WEAK, ReferenceIdentityMap.HARD);
64
65     public SqlGeneratorDefaultImpl(Platform platform)
66     {
67         this.m_platform = platform;
68     }
69
70     /**
71      * generate a prepared DELETE-Statement for the Class
72      * described by cld.
73      * @param cld the ClassDescriptor
74      */

75     public SqlStatement getPreparedDeleteStatement(ClassDescriptor cld)
76     {
77         SqlForClass sfc = getSqlForClass(cld);
78         SqlStatement sql = sfc.getDeleteSql();
79         if(sql == null)
80         {
81             ProcedureDescriptor pd = cld.getDeleteProcedure();
82
83             if(pd == null)
84             {
85                 sql = new SqlDeleteByPkStatement(cld, logger);
86             }
87             else
88             {
89                 sql = new SqlProcedureStatement(pd, logger);
90             }
91             // set the sql string
92
sfc.setDeleteSql(sql);
93
94             if(logger.isDebugEnabled())
95             {
96                 logger.debug("SQL:" + sql.getStatement());
97             }
98         }
99         return sql;
100     }
101
102     /**
103      * generate a prepared INSERT-Statement for the Class
104      * described by cld.
105      *
106      * @param cld the ClassDescriptor
107      */

108     public SqlStatement getPreparedInsertStatement(ClassDescriptor cld)
109     {
110         SqlStatement sql;
111         SqlForClass sfc = getSqlForClass(cld);
112         sql = sfc.getInsertSql();
113         if(sql == null)
114         {
115             ProcedureDescriptor pd = cld.getInsertProcedure();
116
117             if(pd == null)
118             {
119                 sql = new SqlInsertStatement(cld, logger);
120             }
121             else
122             {
123                 sql = new SqlProcedureStatement(pd, logger);
124             }
125             // set the sql string
126
sfc.setInsertSql(sql);
127
128             if(logger.isDebugEnabled())
129             {
130                 logger.debug("SQL:" + sql.getStatement());
131             }
132         }
133         return sql;
134     }
135
136     /**
137      * generate a prepared SELECT-Statement for the Class
138      * described by cld
139      * @param cld the ClassDescriptor
140      */

141     public SelectStatement getPreparedSelectByPkStatement(ClassDescriptor cld)
142     {
143         SelectStatement sql;
144         SqlForClass sfc = getSqlForClass(cld);
145         sql = sfc.getSelectByPKSql();
146         if(sql == null)
147         {
148             sql = new SqlSelectByPkStatement(m_platform, cld, logger);
149
150             // set the sql string
151
sfc.setSelectByPKSql(sql);
152
153             if(logger.isDebugEnabled())
154             {
155                 logger.debug("SQL:" + sql.getStatement());
156             }
157         }
158         return sql;
159     }
160
161     public SqlStatement getPreparedExistsStatement(ClassDescriptor cld)
162     {
163         SqlStatement sql;
164         SqlForClass sfc = getSqlForClass(cld);
165         sql = sfc.getSelectExists();
166         if(sql == null)
167         {
168             // TODO: Should we support a procedure call for this too??
169
sql = new SqlExistStatement(cld, logger);
170             // set the sql string
171
sfc.setSelectExists(sql);
172             if(logger.isDebugEnabled())
173             {
174                 logger.debug("SQL:" + sql.getStatement());
175             }
176         }
177         return sql;
178     }
179
180     /**
181      * generate a select-Statement according to query
182      *
183      * @param query the Query
184      * @param cld the ClassDescriptor
185      */

186     public SelectStatement getPreparedSelectStatement(Query query, ClassDescriptor cld)
187     {
188         SelectStatement sql = new SqlSelectStatement(m_platform, cld, query, logger);
189         if (logger.isDebugEnabled())
190         {
191             logger.debug("SQL:" + sql.getStatement());
192         }
193         return sql;
194     }
195
196     /**
197      * generate a prepared UPDATE-Statement for the Class
198      * described by cld
199      * @param cld the ClassDescriptor
200      */

201     public SqlStatement getPreparedUpdateStatement(ClassDescriptor cld)
202     {
203         SqlForClass sfc = getSqlForClass(cld);
204         SqlStatement result = sfc.getUpdateSql();
205         if(result == null)
206         {
207             ProcedureDescriptor pd = cld.getUpdateProcedure();
208
209             if(pd == null)
210             {
211                 result = new SqlUpdateStatement(cld, logger);
212             }
213             else
214             {
215                 result = new SqlProcedureStatement(pd, logger);
216             }
217             // set the sql string
218
sfc.setUpdateSql(result);
219
220             if(logger.isDebugEnabled())
221             {
222                 logger.debug("SQL:" + result.getStatement());
223             }
224         }
225         return result;
226     }
227
228     /**
229      * generate an INSERT-Statement for M:N indirection table
230      *
231      * @param table
232      * @param pkColumns1
233      * @param pkColumns2
234      */

235     public String JavaDoc getInsertMNStatement(String JavaDoc table, String JavaDoc[] pkColumns1, String JavaDoc[] pkColumns2)
236     {
237         SqlStatement sql;
238         String JavaDoc result;
239
240         String JavaDoc[] cols = new String JavaDoc[pkColumns1.length + pkColumns2.length];
241         System.arraycopy(pkColumns1, 0, cols, 0, pkColumns1.length);
242         System.arraycopy(pkColumns2, 0, cols, pkColumns1.length, pkColumns2.length);
243
244         sql = new SqlInsertMNStatement(table, cols, logger);
245         result = sql.getStatement();
246
247         if (logger.isDebugEnabled())
248         {
249             logger.debug("SQL:" + result);
250         }
251         return result;
252     }
253
254     /**
255      * generate a SELECT-Statement for M:N indirection table
256      *
257      * @param table the indirection table
258      * @param selectColumns selected columns
259      * @param columns for where
260      */

261     public String JavaDoc getSelectMNStatement(String JavaDoc table, String JavaDoc[] selectColumns, String JavaDoc[] columns)
262     {
263         SqlStatement sql;
264         String JavaDoc result;
265
266         sql = new SqlSelectMNStatement(table, selectColumns, columns, logger);
267         result = sql.getStatement();
268
269         if (logger.isDebugEnabled())
270         {
271             logger.debug("SQL:" + result);
272         }
273         return result;
274     }
275
276     /**
277      * generate a DELETE-Statement for M:N indirection table
278      *
279      * @param table
280      * @param pkColumns1
281      * @param pkColumns2
282      */

283     public String JavaDoc getDeleteMNStatement(String JavaDoc table, String JavaDoc[] pkColumns1, String JavaDoc[] pkColumns2)
284     {
285         SqlStatement sql;
286         String JavaDoc result;
287         String JavaDoc[] cols;
288
289         if (pkColumns2 == null)
290         {
291             cols = pkColumns1;
292         }
293         else
294         {
295             cols = new String JavaDoc[pkColumns1.length + pkColumns2.length];
296             System.arraycopy(pkColumns1, 0, cols, 0, pkColumns1.length);
297             System.arraycopy(pkColumns2, 0, cols, pkColumns1.length, pkColumns2.length);
298         }
299
300         sql = new SqlDeleteMNStatement(table, cols, logger);
301         result = sql.getStatement();
302
303         if (logger.isDebugEnabled())
304         {
305             logger.debug("SQL:" + result);
306         }
307         return result;
308     }
309
310     /**
311      * generate a select-Statement according to query
312      * @param query the Query
313      * @param cld the ClassDescriptor
314      */

315     public SelectStatement getSelectStatementDep(Query query, ClassDescriptor cld)
316     {
317         // TODO: Why do we need this method?
318
return getPreparedSelectStatement(query, cld);
319     }
320
321     /**
322      * @param crit Selection criteria
323      *
324      * 26/06/99 Change statement to a StringBuffer for efficiency
325      */

326     public String JavaDoc asSQLStatement(Criteria crit, ClassDescriptor cld)
327     {
328         Enumeration JavaDoc e = crit.getElements();
329         StringBuffer JavaDoc statement = new StringBuffer JavaDoc();
330         while (e.hasMoreElements())
331         {
332             Object JavaDoc o = e.nextElement();
333             if (o instanceof Criteria)
334             {
335                 String JavaDoc addAtStart;
336                 String JavaDoc addAtEnd;
337                 Criteria pc = (Criteria) o;
338                 // need to add parenthesises?
339
if (pc.isEmbraced())
340                 {
341                     addAtStart = " (";
342                     addAtEnd = ") ";
343                 }
344                 else
345                 {
346                     addAtStart = "";
347                     addAtEnd = "";
348                 }
349
350                 switch (pc.getType())
351                 {
352                     case (Criteria.OR) :
353                         {
354                             statement.append(" OR ").append(addAtStart);
355                             statement.append(asSQLStatement(pc, cld));
356                             statement.append(addAtEnd);
357                             break;
358                         }
359                     case (Criteria.AND) :
360                         {
361                             statement.insert(0, "( ");
362                             statement.append(") ");
363                             statement.append(" AND ").append(addAtStart);
364                             statement.append(asSQLStatement(pc, cld));
365                             statement.append(addAtEnd);
366                             break;
367                         }
368                 }
369             }
370             else
371             {
372                 SelectionCriteria c = (SelectionCriteria) o;
373                 if (statement.length() == 0)
374                 {
375                     statement.append(asSQLClause(c, cld));
376                 }
377                 else
378                 {
379                     statement.insert(0, "(");
380                     statement.append(") ");
381                     statement.append(" AND ");
382                     statement.append(asSQLClause(c, cld));
383                 }
384             }
385         } // while
386
if (statement.length() == 0)
387         {
388             return null;
389         }
390         return statement.toString();
391     }
392
393     /**
394      * Answer the SQL-Clause for a SelectionCriteria
395      *
396      * @param c SelectionCriteria
397      * @param cld ClassDescriptor
398      */

399     protected String JavaDoc asSQLClause(SelectionCriteria c, ClassDescriptor cld)
400     {
401         if (c instanceof FieldCriteria)
402             return toSQLClause((FieldCriteria) c, cld);
403
404         if (c instanceof NullCriteria)
405             return toSQLClause((NullCriteria) c);
406
407         if (c instanceof BetweenCriteria)
408             return toSQLClause((BetweenCriteria) c, cld);
409
410         if (c instanceof InCriteria)
411             return toSQLClause((InCriteria) c);
412
413         if (c instanceof SqlCriteria)
414             return toSQLClause((SqlCriteria) c);
415
416         if (c instanceof ExistsCriteria)
417             return toSQLClause((ExistsCriteria) c, cld);
418
419         return toSQLClause(c, cld);
420     }
421
422     private String JavaDoc toSqlClause(Object JavaDoc attributeOrQuery, ClassDescriptor cld)
423     {
424         String JavaDoc result;
425
426         if (attributeOrQuery instanceof Query)
427         {
428             Query q = (Query) attributeOrQuery;
429             result = getPreparedSelectStatement(q, cld.getRepository().getDescriptorFor(q.getSearchClass()))
430                     .getStatement();
431         }
432         else
433         {
434            result = (String JavaDoc)attributeOrQuery;
435         }
436
437         return result;
438     }
439
440     /**
441      * Answer the SQL-Clause for a NullCriteria
442      *
443      * @param c NullCriteria
444      */

445     private String JavaDoc toSQLClause(NullCriteria c)
446     {
447         String JavaDoc colName = (String JavaDoc)c.getAttribute();
448         return colName + c.getClause();
449     }
450
451     /**
452      * Answer the SQL-Clause for a FieldCriteria
453      *
454      * @param c FieldCriteria
455      * @param cld ClassDescriptor
456      */

457     private String JavaDoc toSQLClause(FieldCriteria c, ClassDescriptor cld)
458     {
459         String JavaDoc colName = toSqlClause(c.getAttribute(), cld);
460         return colName + c.getClause() + c.getValue();
461     }
462
463     /**
464      * Answer the SQL-Clause for a BetweenCriteria
465      *
466      * @param c BetweenCriteria
467      * @param cld ClassDescriptor
468      */

469     private String JavaDoc toSQLClause(BetweenCriteria c, ClassDescriptor cld)
470     {
471         String JavaDoc colName = toSqlClause(c.getAttribute(), cld);
472         return colName + c.getClause() + " ? AND ? ";
473     }
474
475     /**
476      * Answer the SQL-Clause for an InCriteria
477      *
478      * @param c SelectionCriteria
479      */

480     private String JavaDoc toSQLClause(InCriteria c)
481     {
482         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
483         Collection JavaDoc values = (Collection JavaDoc) c.getValue();
484         int size = values.size();
485
486         buf.append(c.getAttribute());
487         buf.append(c.getClause());
488         buf.append("(");
489         for (int i = 0; i < size - 1; i++)
490         {
491             buf.append("?,");
492         }
493         buf.append("?)");
494         return buf.toString();
495     }
496
497     /**
498      * Answer the SQL-Clause for a SelectionCriteria
499      *
500      * @param c SelectionCriteria
501      * @param cld ClassDescriptor
502      */

503     private String JavaDoc toSQLClause(SelectionCriteria c, ClassDescriptor cld)
504     {
505         String JavaDoc colName = toSqlClause(c.getAttribute(), cld);
506         return colName + c.getClause() + " ? ";
507     }
508
509     /**
510      * Answer the SQL-Clause for a SqlCriteria
511      *
512      * @param c SqlCriteria
513      */

514     private String JavaDoc toSQLClause(SqlCriteria c)
515     {
516         return c.getClause();
517     }
518
519     /**
520      * Answer the SQL-Clause for an ExistsCriteria
521      *
522      * @param c ExistsCriteria
523      * @param cld ClassDescriptor
524      */

525     private String JavaDoc toSQLClause(ExistsCriteria c, ClassDescriptor cld)
526     {
527         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
528         Query subQuery = (Query) c.getValue();
529
530         buf.append(c.getClause());
531         buf.append(" (");
532
533         // If it's a proper call
534
if (cld != null)
535         {
536             buf.append(
537                 getPreparedSelectStatement(
538                     subQuery,
539                     cld.getRepository().getDescriptorFor(subQuery.getSearchClass())));
540
541             // Otherwise it's most likely a call to toString()
542
}
543         else
544         {
545             buf.append(subQuery);
546         }
547
548         buf.append(")");
549         return buf.toString();
550     }
551
552     /**
553      * generate a prepared DELETE-Statement according to query
554      * @param query the Query
555      * @param cld the ClassDescriptor
556      */

557     public SqlStatement getPreparedDeleteStatement(Query query, ClassDescriptor cld)
558     {
559         return new SqlDeleteByQuery(m_platform, cld, query, logger);
560     }
561
562     /* (non-Javadoc)
563      * @see org.apache.ojb.broker.accesslayer.sql.SqlGenerator#getPlatform()
564      */

565     public Platform getPlatform()
566     {
567         return m_platform;
568     }
569
570     /**
571      * Returns the {@link SqlForClass} instance for
572      * the given class descriptor.
573      *
574      * @param cld The class descriptor.
575      * @return The {@link SqlForClass}.
576      */

577     protected SqlForClass getSqlForClass(ClassDescriptor cld)
578     {
579         SqlForClass result = (SqlForClass) sqlForClass.get(cld);
580         if(result == null)
581         {
582             result = newInstanceSqlForClass();
583             sqlForClass.put(cld, result);
584         }
585         return result;
586     }
587
588     /**
589      * User who want to extend this implementation can override this method to use
590      * their own (extended) version of
591      * {@link org.apache.ojb.broker.accesslayer.sql.SqlGeneratorDefaultImpl.SqlForClass}.
592      *
593      * @return A new instance.
594      */

595     protected SqlForClass newInstanceSqlForClass()
596     {
597         return new SqlForClass();
598     }
599
600     //===================================================================
601
// inner class
602
//===================================================================
603

604     /**
605      * This class serves as a cache for sql-Statements
606      * used for persistence operations.
607      */

608     public static class SqlForClass
609     {
610         private SqlStatement deleteSql;
611         private SqlStatement insertSql;
612         private SqlStatement updateSql;
613         private SelectStatement selectByPKSql;
614         private SqlStatement selectExists;
615
616         public SqlStatement getDeleteSql()
617         {
618             return deleteSql;
619         }
620
621         public void setDeleteSql(SqlStatement deleteSql)
622         {
623             this.deleteSql = deleteSql;
624         }
625
626         public SqlStatement getInsertSql()
627         {
628             return insertSql;
629         }
630
631         public void setInsertSql(SqlStatement insertSql)
632         {
633             this.insertSql = insertSql;
634         }
635
636         public SqlStatement getUpdateSql()
637         {
638             return updateSql;
639         }
640
641         public void setUpdateSql(SqlStatement updateSql)
642         {
643             this.updateSql = updateSql;
644         }
645
646         public SelectStatement getSelectByPKSql()
647         {
648             return selectByPKSql;
649         }
650
651         public void setSelectByPKSql(SelectStatement selectByPKSql)
652         {
653             this.selectByPKSql = selectByPKSql;
654         }
655
656         public SqlStatement getSelectExists()
657         {
658             return selectExists;
659         }
660
661         public void setSelectExists(SqlStatement selectExists)
662         {
663             this.selectExists = selectExists;
664         }
665     }
666
667 }
668
Popular Tags