KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > dba > JdbcAdapter


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56
57 package org.objectstyle.cayenne.dba;
58
59 import java.net.URL JavaDoc;
60 import java.sql.Connection JavaDoc;
61 import java.sql.PreparedStatement JavaDoc;
62 import java.sql.SQLException JavaDoc;
63 import java.util.Collection JavaDoc;
64 import java.util.Iterator JavaDoc;
65
66 import org.objectstyle.cayenne.CayenneRuntimeException;
67 import org.objectstyle.cayenne.access.DataNode;
68 import org.objectstyle.cayenne.access.OperationObserver;
69 import org.objectstyle.cayenne.access.QueryTranslator;
70 import org.objectstyle.cayenne.access.trans.DeleteTranslator;
71 import org.objectstyle.cayenne.access.trans.ProcedureTranslator;
72 import org.objectstyle.cayenne.access.trans.QualifierTranslator;
73 import org.objectstyle.cayenne.access.trans.QueryAssembler;
74 import org.objectstyle.cayenne.access.trans.SelectTranslator;
75 import org.objectstyle.cayenne.access.trans.UpdateTranslator;
76 import org.objectstyle.cayenne.access.types.BooleanType;
77 import org.objectstyle.cayenne.access.types.ByteArrayType;
78 import org.objectstyle.cayenne.access.types.CharType;
79 import org.objectstyle.cayenne.access.types.ExtendedType;
80 import org.objectstyle.cayenne.access.types.ExtendedTypeMap;
81 import org.objectstyle.cayenne.access.types.UtilDateType;
82 import org.objectstyle.cayenne.map.DbAttribute;
83 import org.objectstyle.cayenne.map.DbEntity;
84 import org.objectstyle.cayenne.map.DbJoin;
85 import org.objectstyle.cayenne.map.DbRelationship;
86 import org.objectstyle.cayenne.map.DerivedDbEntity;
87 import org.objectstyle.cayenne.query.BatchQuery;
88 import org.objectstyle.cayenne.query.DeleteQuery;
89 import org.objectstyle.cayenne.query.ProcedureQuery;
90 import org.objectstyle.cayenne.query.Query;
91 import org.objectstyle.cayenne.query.SQLAction;
92 import org.objectstyle.cayenne.query.SelectQuery;
93 import org.objectstyle.cayenne.query.UpdateQuery;
94 import org.objectstyle.cayenne.util.ResourceLocator;
95 import org.objectstyle.cayenne.util.Util;
96
97 /**
98  * A generic DbAdapter implementation. Can be used as a default adapter or as a superclass
99  * of a concrete adapter implementation.
100  *
101  * @author Andrei Adamchik
102  */

103 public class JdbcAdapter implements DbAdapter {
104
105     protected PkGenerator pkGenerator;
106     protected TypesHandler typesHandler;
107     protected ExtendedTypeMap extendedTypes;
108     protected boolean supportsBatchUpdates;
109     protected boolean supportsFkConstraints;
110     protected boolean supportsUniqueConstraints;
111     protected boolean supportsGeneratedKeys;
112
113     /**
114      * Creates new JdbcAdapter with a set of default parameters.
115      */

116     public JdbcAdapter() {
117         // init defaults
118
this.setSupportsBatchUpdates(false);
119         this.setSupportsUniqueConstraints(true);
120         this.setSupportsFkConstraints(true);
121
122         this.pkGenerator = this.createPkGenerator();
123         this.typesHandler = TypesHandler.getHandler(findAdapterResource("/types.xml"));
124         this.extendedTypes = new ExtendedTypeMap();
125         this.configureExtendedTypes(extendedTypes);
126     }
127
128     /**
129      * Returns default separator - a semicolon.
130      *
131      * @since 1.0.4
132      */

133     public String JavaDoc getBatchTerminator() {
134         return ";";
135     }
136
137     /**
138      * Locates and returns a named adapter resource. A resource can be an XML file, etc.
139      * <p>
140      * This implementation is based on the premise that each adapter is located in its own
141      * Java package and all resources are in the same package as well. Resource lookup is
142      * recursive, so that if DbAdapter is a subclass of another adapter, parent adapter
143      * package is searched as a failover.
144      * </p>
145      *
146      * @since 1.1
147      */

148     public URL JavaDoc findAdapterResource(String JavaDoc name) {
149         Class JavaDoc adapterClass = this.getClass();
150
151         while (adapterClass != null && JdbcAdapter.class.isAssignableFrom(adapterClass)) {
152
153             String JavaDoc path = Util.getPackagePath(adapterClass.getName()) + name;
154             URL JavaDoc url = ResourceLocator.findURLInClasspath(path);
155             if (url != null) {
156                 return url;
157             }
158
159             adapterClass = adapterClass.getSuperclass();
160         }
161
162         return null;
163     }
164
165     /**
166      * Installs appropriate ExtendedTypes as converters for passing values between JDBC
167      * and Java layers. Called from default constructor.
168      */

169     protected void configureExtendedTypes(ExtendedTypeMap map) {
170         // use BooleanType to ensure that returned booleans are an enum of Boolean.TRUE
171
// and Boolean.FALSE
172
map.registerType(new BooleanType());
173
174         // Create a default CHAR handler with some generic settings.
175
// Subclasses may need to install their own CharType or reconfigure
176
// this one to work better with the target database.
177
map.registerType(new CharType(false, true));
178
179         // enable java.util.Dates as "persistent" values
180
map.registerType(new UtilDateType());
181
182         // enable "small" BLOBs
183
map.registerType(new ByteArrayType(false, true));
184     }
185
186     /**
187      * Creates and returns a primary key generator. This factory method should be
188      * overriden by JdbcAdapter subclasses to provide custom implementations of
189      * PKGenerator.
190      */

191     protected PkGenerator createPkGenerator() {
192         return new JdbcPkGenerator();
193     }
194
195     /**
196      * Returns primary key generator associated with this DbAdapter.
197      */

198     public PkGenerator getPkGenerator() {
199         return pkGenerator;
200     }
201
202     /**
203      * Sets new primary key generator.
204      *
205      * @since 1.1
206      */

207     public void setPkGenerator(PkGenerator pkGenerator) {
208         this.pkGenerator = pkGenerator;
209     }
210
211     /**
212      * @deprecated since 1.2 this method is unneeded as customizations are done via custom
213      * SQLActions.
214      */

215     public QueryTranslator getQueryTranslator(Query query) throws Exception JavaDoc {
216         Class JavaDoc queryClass = queryTranslatorClass(query);
217
218         try {
219             QueryTranslator t = (QueryTranslator) queryClass.newInstance();
220             t.setQuery(query);
221             t.setAdapter(this);
222             return t;
223         }
224         catch (Exception JavaDoc ex) {
225             throw new CayenneRuntimeException("Can't load translator class: "
226                     + queryClass);
227         }
228     }
229
230     /**
231      * Returns a class of the query translator that should be used to translate the query
232      * <code>q</code> to SQL. Exists mainly for the benefit of subclasses that can
233      * override this method providing their own translator.
234      *
235      * @deprecated since 1.2 this method is unneeded as customizations are done via custom
236      * SQLActions.
237      */

238     protected Class JavaDoc queryTranslatorClass(Query q) {
239         if (q == null) {
240             throw new NullPointerException JavaDoc("Null query.");
241         }
242         else if (q instanceof SelectQuery) {
243             return SelectTranslator.class;
244         }
245         else if (q instanceof UpdateQuery) {
246             return UpdateTranslator.class;
247         }
248         else if (q instanceof org.objectstyle.cayenne.query.InsertQuery) {
249             return org.objectstyle.cayenne.access.trans.InsertTranslator.class;
250         }
251         else if (q instanceof DeleteQuery) {
252             return DeleteTranslator.class;
253         }
254         else if (q instanceof ProcedureQuery) {
255             return ProcedureTranslator.class;
256         }
257         else {
258             throw new CayenneRuntimeException("Unrecognized query class..."
259                     + q.getClass().getName());
260         }
261     }
262
263     /**
264      * Returns true.
265      */

266     public boolean supportsFkConstraints() {
267         return supportsFkConstraints;
268     }
269
270     /**
271      * @since 1.1
272      */

273     public void setSupportsFkConstraints(boolean flag) {
274         this.supportsFkConstraints = flag;
275     }
276
277     /**
278      * Returns true.
279      *
280      * @since 1.1
281      */

282     public boolean supportsUniqueConstraints() {
283         return supportsUniqueConstraints;
284     }
285
286     /**
287      * @since 1.1
288      */

289     public void setSupportsUniqueConstraints(boolean flag) {
290         this.supportsUniqueConstraints = flag;
291     }
292
293     /**
294      * Returns a SQL string to drop a table corresponding to <code>ent</code> DbEntity.
295      */

296     public String JavaDoc dropTable(DbEntity ent) {
297         return "DROP TABLE " + ent.getFullyQualifiedName();
298     }
299
300     /**
301      * Returns a SQL string that can be used to create database table corresponding to
302      * <code>ent</code> parameter.
303      */

304     public String JavaDoc createTable(DbEntity ent) {
305         // later we may support view creation
306
// for derived DbEntities
307
if (ent instanceof DerivedDbEntity) {
308             throw new CayenneRuntimeException("Can't create table for derived DbEntity '"
309                     + ent.getName()
310                     + "'.");
311         }
312
313         StringBuffer JavaDoc sqlBuffer = new StringBuffer JavaDoc();
314         sqlBuffer
315                 .append("CREATE TABLE ")
316                 .append(ent.getFullyQualifiedName())
317                 .append(" (");
318
319         // columns
320
Iterator JavaDoc it = ent.getAttributes().iterator();
321         boolean first = true;
322         while (it.hasNext()) {
323             if (first) {
324                 first = false;
325             }
326             else {
327                 sqlBuffer.append(", ");
328             }
329
330             DbAttribute column = (DbAttribute) it.next();
331
332             // attribute may not be fully valid, do a simple check
333
if (column.getType() == TypesMapping.NOT_DEFINED) {
334                 throw new CayenneRuntimeException("Undefined type for attribute '"
335                         + ent.getFullyQualifiedName()
336                         + "."
337                         + column.getName()
338                         + "'.");
339             }
340
341             createTableAppendColumn(sqlBuffer, column);
342
343         }
344
345         // primary key clause
346
Iterator JavaDoc pkit = ent.getPrimaryKey().iterator();
347         if (pkit.hasNext()) {
348             if (first)
349                 first = false;
350             else
351                 sqlBuffer.append(", ");
352
353             sqlBuffer.append("PRIMARY KEY (");
354             boolean firstPk = true;
355             while (pkit.hasNext()) {
356                 if (firstPk)
357                     firstPk = false;
358                 else
359                     sqlBuffer.append(", ");
360
361                 DbAttribute at = (DbAttribute) pkit.next();
362                 sqlBuffer.append(at.getName());
363             }
364             sqlBuffer.append(')');
365         }
366         sqlBuffer.append(')');
367
368         return sqlBuffer.toString();
369     }
370
371     /**
372      * Appends SQL for column creation to CREATE TABLE buffer.
373      *
374      * @since 1.2
375      */

376     protected void createTableAppendColumn(StringBuffer JavaDoc sqlBuffer, DbAttribute column) {
377         String JavaDoc[] types = externalTypesForJdbcType(column.getType());
378         if (types == null || types.length == 0) {
379             String JavaDoc entityName = column.getEntity() != null ? ((DbEntity) column
380                     .getEntity()).getFullyQualifiedName() : "<null>";
381             throw new CayenneRuntimeException("Undefined type for attribute '"
382                     + entityName
383                     + "."
384                     + column.getName()
385                     + "': "
386                     + column.getType());
387         }
388
389         String JavaDoc type = types[0];
390         sqlBuffer.append(column.getName()).append(' ').append(type);
391
392         // append size and precision (if applicable)
393
if (TypesMapping.supportsLength(column.getType())) {
394             int len = column.getMaxLength();
395             int prec = TypesMapping.isDecimal(column.getType())
396                     ? column.getPrecision()
397                     : -1;
398
399             // sanity check
400
if (prec > len) {
401                 prec = -1;
402             }
403
404             if (len > 0) {
405                 sqlBuffer.append('(').append(len);
406
407                 if (prec >= 0) {
408                     sqlBuffer.append(", ").append(prec);
409                 }
410
411                 sqlBuffer.append(')');
412             }
413         }
414
415         sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL");
416     }
417
418     /**
419      * Returns a DDL string to create a unique constraint over a set of columns.
420      *
421      * @since 1.1
422      */

423     public String JavaDoc createUniqueConstraint(DbEntity source, Collection JavaDoc columns) {
424         if (columns == null || columns.isEmpty()) {
425             throw new CayenneRuntimeException(
426                     "Can't create UNIQUE constraint - no columns specified.");
427         }
428
429         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
430
431         buf
432                 .append("ALTER TABLE ")
433                 .append(source.getFullyQualifiedName())
434                 .append(" ADD UNIQUE (");
435
436         Iterator JavaDoc it = columns.iterator();
437         DbAttribute first = (DbAttribute) it.next();
438         buf.append(first.getName());
439
440         while (it.hasNext()) {
441             DbAttribute next = (DbAttribute) it.next();
442             buf.append(", ");
443             buf.append(next.getName());
444         }
445
446         buf.append(")");
447
448         return buf.toString();
449     }
450
451     /**
452      * Returns a SQL string that can be used to create a foreign key constraint for the
453      * relationship.
454      */

455     public String JavaDoc createFkConstraint(DbRelationship rel) {
456         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
457         StringBuffer JavaDoc refBuf = new StringBuffer JavaDoc();
458
459         buf.append("ALTER TABLE ").append(((DbEntity) rel.getSourceEntity())
460                 .getFullyQualifiedName()).append(" ADD FOREIGN KEY (");
461
462         Iterator JavaDoc jit = rel.getJoins().iterator();
463         boolean first = true;
464         while (jit.hasNext()) {
465             DbJoin join = (DbJoin) jit.next();
466             if (!first) {
467                 buf.append(", ");
468                 refBuf.append(", ");
469             }
470             else
471                 first = false;
472
473             buf.append(join.getSourceName());
474             refBuf.append(join.getTargetName());
475         }
476
477         buf
478                 .append(") REFERENCES ")
479                 .append(((DbEntity) rel.getTargetEntity()).getFullyQualifiedName())
480                 .append(" (")
481                 .append(refBuf.toString())
482                 .append(')');
483         return buf.toString();
484     }
485
486     public String JavaDoc[] externalTypesForJdbcType(int type) {
487         return typesHandler.externalTypesForJdbcType(type);
488     }
489
490     public ExtendedTypeMap getExtendedTypes() {
491         return extendedTypes;
492     }
493
494     public DbAttribute buildAttribute(
495             String JavaDoc name,
496             String JavaDoc typeName,
497             int type,
498             int size,
499             int precision,
500             boolean allowNulls) {
501
502         DbAttribute attr = new DbAttribute();
503         attr.setName(name);
504         attr.setType(type);
505         attr.setMandatory(!allowNulls);
506
507         if (size >= 0) {
508             attr.setMaxLength(size);
509         }
510
511         if (precision >= 0) {
512             attr.setPrecision(precision);
513         }
514
515         return attr;
516     }
517
518     public String JavaDoc tableTypeForTable() {
519         return "TABLE";
520     }
521
522     public String JavaDoc tableTypeForView() {
523         return "VIEW";
524     }
525
526     /**
527      * Creates and returns a default implementation of a qualifier translator.
528      */

529     public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
530         return new QualifierTranslator(queryAssembler);
531     }
532
533     /**
534      * Creates an instance of DataNode class.
535      *
536      * @deprecated since 1.2 this method is not used as node behavior customization is
537      * done via SQLActionVisitor.
538      */

539     public DataNode createDataNode(String JavaDoc name) {
540         DataNode node = new DataNode(name);
541         node.setAdapter(this);
542         return node;
543     }
544
545     /**
546      * Uses JdbcActionBuilder to create the right action.
547      *
548      * @since 1.2
549      */

550     public SQLAction getAction(Query query, DataNode node) {
551         return query.createSQLAction(new JdbcActionBuilder(this, node.getEntityResolver()));
552     }
553
554     public void bindParameter(
555             PreparedStatement JavaDoc statement,
556             Object JavaDoc object,
557             int pos,
558             int sqlType,
559             int precision) throws SQLException JavaDoc, Exception JavaDoc {
560
561         if (object == null) {
562             statement.setNull(pos, sqlType);
563         }
564         else {
565             ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(object
566                     .getClass());
567             typeProcessor.setJdbcObject(statement, object, pos, sqlType, precision);
568         }
569     }
570
571     public boolean supportsBatchUpdates() {
572         return this.supportsBatchUpdates;
573     }
574
575     public void setSupportsBatchUpdates(boolean flag) {
576         this.supportsBatchUpdates = flag;
577     }
578
579     /**
580      * @since 1.2
581      */

582     public boolean supportsGeneratedKeys() {
583         return supportsGeneratedKeys;
584     }
585
586     /**
587      * @since 1.2
588      */

589     public void setSupportsGeneratedKeys(boolean flag) {
590         this.supportsGeneratedKeys = flag;
591     }
592
593     /**
594      * Always returns <code>true</code>, letting DataNode to handle the query.
595      *
596      * @deprecated Since 1.2 this method is obsolete and is ignored across Cayenne.
597      */

598     public boolean shouldRunBatchQuery(
599             DataNode node,
600             Connection JavaDoc con,
601             BatchQuery query,
602             OperationObserver delegate) throws SQLException JavaDoc, Exception JavaDoc {
603         return true;
604     }
605 }
Popular Tags