KickJava   Java API By Example, From Geeks To Geeks.

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


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

19
20
21 package org.apache.cayenne.dba;
22
23 import java.net.URL JavaDoc;
24 import java.sql.PreparedStatement JavaDoc;
25 import java.sql.SQLException JavaDoc;
26 import java.util.Calendar JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.GregorianCalendar JavaDoc;
29 import java.util.Iterator JavaDoc;
30
31 import org.apache.cayenne.CayenneRuntimeException;
32 import org.apache.cayenne.access.DataNode;
33 import org.apache.cayenne.access.trans.QualifierTranslator;
34 import org.apache.cayenne.access.trans.QueryAssembler;
35 import org.apache.cayenne.access.types.BigIntegerType;
36 import org.apache.cayenne.access.types.BooleanType;
37 import org.apache.cayenne.access.types.ByteArrayType;
38 import org.apache.cayenne.access.types.CalendarType;
39 import org.apache.cayenne.access.types.CharType;
40 import org.apache.cayenne.access.types.ExtendedType;
41 import org.apache.cayenne.access.types.ExtendedTypeMap;
42 import org.apache.cayenne.access.types.UtilDateType;
43 import org.apache.cayenne.map.DbAttribute;
44 import org.apache.cayenne.map.DbEntity;
45 import org.apache.cayenne.map.DbJoin;
46 import org.apache.cayenne.map.DbRelationship;
47 import org.apache.cayenne.map.DerivedDbEntity;
48 import org.apache.cayenne.query.Query;
49 import org.apache.cayenne.query.SQLAction;
50 import org.apache.cayenne.util.ResourceLocator;
51 import org.apache.cayenne.util.Util;
52
53 /**
54  * A generic DbAdapter implementation. Can be used as a default adapter or as a superclass
55  * of a concrete adapter implementation.
56  *
57  * @author Andrus Adamchik
58  */

59 public class JdbcAdapter implements DbAdapter {
60
61     protected PkGenerator pkGenerator;
62     protected TypesHandler typesHandler;
63     protected ExtendedTypeMap extendedTypes;
64     protected boolean supportsBatchUpdates;
65     protected boolean supportsFkConstraints;
66     protected boolean supportsUniqueConstraints;
67     protected boolean supportsGeneratedKeys;
68
69     /**
70      * Creates new JdbcAdapter with a set of default parameters.
71      */

72     public JdbcAdapter() {
73         // init defaults
74
this.setSupportsBatchUpdates(false);
75         this.setSupportsUniqueConstraints(true);
76         this.setSupportsFkConstraints(true);
77
78         this.pkGenerator = this.createPkGenerator();
79         this.typesHandler = TypesHandler.getHandler(findAdapterResource("/types.xml"));
80         this.extendedTypes = new ExtendedTypeMap();
81         this.configureExtendedTypes(extendedTypes);
82     }
83
84     /**
85      * Returns default separator - a semicolon.
86      *
87      * @since 1.0.4
88      */

89     public String JavaDoc getBatchTerminator() {
90         return ";";
91     }
92
93     /**
94      * Locates and returns a named adapter resource. A resource can be an XML file, etc.
95      * <p>
96      * This implementation is based on the premise that each adapter is located in its own
97      * Java package and all resources are in the same package as well. Resource lookup is
98      * recursive, so that if DbAdapter is a subclass of another adapter, parent adapter
99      * package is searched as a failover.
100      * </p>
101      *
102      * @since 1.1
103      */

104     public URL JavaDoc findAdapterResource(String JavaDoc name) {
105         Class JavaDoc adapterClass = this.getClass();
106
107         while (adapterClass != null && JdbcAdapter.class.isAssignableFrom(adapterClass)) {
108
109             String JavaDoc path = Util.getPackagePath(adapterClass.getName()) + name;
110             URL JavaDoc url = ResourceLocator.findURLInClasspath(path);
111             if (url != null) {
112                 return url;
113             }
114
115             adapterClass = adapterClass.getSuperclass();
116         }
117
118         return null;
119     }
120
121     /**
122      * Installs appropriate ExtendedTypes as converters for passing values between JDBC
123      * and Java layers. Called from default constructor.
124      */

125     protected void configureExtendedTypes(ExtendedTypeMap map) {
126         // use BooleanType to ensure that returned booleans are an enum of Boolean.TRUE
127
// and Boolean.FALSE
128
map.registerType(new BooleanType());
129
130         // Create a default CHAR handler with some generic settings.
131
// Subclasses may need to install their own CharType or reconfigure
132
// this one to work better with the target database.
133
map.registerType(new CharType(false, true));
134
135         // enable java.util.Dates as "persistent" values
136
map.registerType(new UtilDateType());
137
138         // enable "small" BLOBs
139
map.registerType(new ByteArrayType(false, true));
140         
141         // enable Calendar
142
// TODO: andrus 9/1/2006 - maybe use ExtendedTypeFactory to handle all calendar
143
// subclasses at once
144
map.registerType(new CalendarType(GregorianCalendar JavaDoc.class));
145         map.registerType(new CalendarType(Calendar JavaDoc.class));
146         
147         map.registerType(new BigIntegerType());
148     }
149
150     /**
151      * Creates and returns a primary key generator. This factory method should be
152      * overriden by JdbcAdapter subclasses to provide custom implementations of
153      * PKGenerator.
154      */

155     protected PkGenerator createPkGenerator() {
156         return new JdbcPkGenerator();
157     }
158
159     /**
160      * Returns primary key generator associated with this DbAdapter.
161      */

162     public PkGenerator getPkGenerator() {
163         return pkGenerator;
164     }
165
166     /**
167      * Sets new primary key generator.
168      *
169      * @since 1.1
170      */

171     public void setPkGenerator(PkGenerator pkGenerator) {
172         this.pkGenerator = pkGenerator;
173     }
174
175     /**
176      * Returns true.
177      */

178     public boolean supportsFkConstraints() {
179         return supportsFkConstraints;
180     }
181
182     /**
183      * @since 1.1
184      */

185     public void setSupportsFkConstraints(boolean flag) {
186         this.supportsFkConstraints = flag;
187     }
188
189     /**
190      * Returns true.
191      *
192      * @since 1.1
193      */

194     public boolean supportsUniqueConstraints() {
195         return supportsUniqueConstraints;
196     }
197
198     /**
199      * @since 1.1
200      */

201     public void setSupportsUniqueConstraints(boolean flag) {
202         this.supportsUniqueConstraints = flag;
203     }
204
205     /**
206      * Returns a SQL string to drop a table corresponding to <code>ent</code> DbEntity.
207      */

208     public String JavaDoc dropTable(DbEntity ent) {
209         return "DROP TABLE " + ent.getFullyQualifiedName();
210     }
211
212     /**
213      * Returns a SQL string that can be used to create database table corresponding to
214      * <code>ent</code> parameter.
215      */

216     public String JavaDoc createTable(DbEntity entity) {
217         if (entity instanceof DerivedDbEntity) {
218             throw new CayenneRuntimeException("Can't create table for derived DbEntity '"
219                     + entity.getName()
220                     + "'.");
221         }
222
223         StringBuffer JavaDoc sqlBuffer = new StringBuffer JavaDoc();
224         sqlBuffer.append("CREATE TABLE ").append(entity.getFullyQualifiedName()).append(
225                 " (");
226
227         // columns
228
Iterator JavaDoc it = entity.getAttributes().iterator();
229         if (it.hasNext()) {
230             boolean first = true;
231             while (it.hasNext()) {
232                 if (first) {
233                     first = false;
234                 }
235                 else {
236                     sqlBuffer.append(", ");
237                 }
238
239                 DbAttribute column = (DbAttribute) it.next();
240
241                 // attribute may not be fully valid, do a simple check
242
if (column.getType() == TypesMapping.NOT_DEFINED) {
243                     throw new CayenneRuntimeException("Undefined type for attribute '"
244                             + entity.getFullyQualifiedName()
245                             + "."
246                             + column.getName()
247                             + "'.");
248                 }
249
250                 createTableAppendColumn(sqlBuffer, column);
251             }
252
253             
254             createTableAppendPKClause(sqlBuffer, entity);
255         }
256
257         sqlBuffer.append(')');
258         return sqlBuffer.toString();
259     }
260
261     /**
262      * @since 1.2
263      */

264     protected void createTableAppendPKClause(StringBuffer JavaDoc sqlBuffer, DbEntity entity) {
265         Iterator JavaDoc pkit = entity.getPrimaryKey().iterator();
266         if (pkit.hasNext()) {
267             sqlBuffer.append(", PRIMARY KEY (");
268             boolean firstPk = true;
269             while (pkit.hasNext()) {
270                 if (firstPk)
271                     firstPk = false;
272                 else
273                     sqlBuffer.append(", ");
274
275                 DbAttribute at = (DbAttribute) pkit.next();
276                 sqlBuffer.append(at.getName());
277             }
278             sqlBuffer.append(')');
279         }
280     }
281
282     /**
283      * Appends SQL for column creation to CREATE TABLE buffer.
284      *
285      * @since 1.2
286      */

287     protected void createTableAppendColumn(StringBuffer JavaDoc sqlBuffer, DbAttribute column) {
288         String JavaDoc[] types = externalTypesForJdbcType(column.getType());
289         if (types == null || types.length == 0) {
290             String JavaDoc entityName = column.getEntity() != null ? ((DbEntity) column
291                     .getEntity()).getFullyQualifiedName() : "<null>";
292             throw new CayenneRuntimeException("Undefined type for attribute '"
293                     + entityName
294                     + "."
295                     + column.getName()
296                     + "': "
297                     + column.getType());
298         }
299
300         String JavaDoc type = types[0];
301         sqlBuffer.append(column.getName()).append(' ').append(type);
302
303         // append size and precision (if applicable)
304
if (TypesMapping.supportsLength(column.getType())) {
305             int len = column.getMaxLength();
306             int scale = TypesMapping.isDecimal(column.getType())
307                     ? column.getScale()
308                     : -1;
309
310             // sanity check
311
if (scale > len) {
312                 scale = -1;
313             }
314
315             if (len > 0) {
316                 sqlBuffer.append('(').append(len);
317
318                 if (scale >= 0) {
319                     sqlBuffer.append(", ").append(scale);
320                 }
321
322                 sqlBuffer.append(')');
323             }
324         }
325
326         sqlBuffer.append(column.isMandatory() ? " NOT NULL" : " NULL");
327     }
328
329     /**
330      * Returns a DDL string to create a unique constraint over a set of columns.
331      *
332      * @since 1.1
333      */

334     public String JavaDoc createUniqueConstraint(DbEntity source, Collection JavaDoc columns) {
335         if (columns == null || columns.isEmpty()) {
336             throw new CayenneRuntimeException(
337                     "Can't create UNIQUE constraint - no columns specified.");
338         }
339
340         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
341
342         buf.append("ALTER TABLE ").append(source.getFullyQualifiedName()).append(
343                 " ADD UNIQUE (");
344
345         Iterator JavaDoc it = columns.iterator();
346         DbAttribute first = (DbAttribute) it.next();
347         buf.append(first.getName());
348
349         while (it.hasNext()) {
350             DbAttribute next = (DbAttribute) it.next();
351             buf.append(", ");
352             buf.append(next.getName());
353         }
354
355         buf.append(")");
356
357         return buf.toString();
358     }
359
360     /**
361      * Returns a SQL string that can be used to create a foreign key constraint for the
362      * relationship.
363      */

364     public String JavaDoc createFkConstraint(DbRelationship rel) {
365         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
366         StringBuffer JavaDoc refBuf = new StringBuffer JavaDoc();
367
368         buf.append("ALTER TABLE ").append(
369                 ((DbEntity) rel.getSourceEntity()).getFullyQualifiedName()).append(
370                 " ADD FOREIGN KEY (");
371
372         Iterator JavaDoc jit = rel.getJoins().iterator();
373         boolean first = true;
374         while (jit.hasNext()) {
375             DbJoin join = (DbJoin) jit.next();
376             if (!first) {
377                 buf.append(", ");
378                 refBuf.append(", ");
379             }
380             else
381                 first = false;
382
383             buf.append(join.getSourceName());
384             refBuf.append(join.getTargetName());
385         }
386
387         buf
388                 .append(") REFERENCES ")
389                 .append(((DbEntity) rel.getTargetEntity()).getFullyQualifiedName())
390                 .append(" (")
391                 .append(refBuf.toString())
392                 .append(')');
393         return buf.toString();
394     }
395
396     public String JavaDoc[] externalTypesForJdbcType(int type) {
397         return typesHandler.externalTypesForJdbcType(type);
398     }
399
400     public ExtendedTypeMap getExtendedTypes() {
401         return extendedTypes;
402     }
403
404     public DbAttribute buildAttribute(
405             String JavaDoc name,
406             String JavaDoc typeName,
407             int type,
408             int size,
409             int scale,
410             boolean allowNulls) {
411
412         DbAttribute attr = new DbAttribute();
413         attr.setName(name);
414         attr.setType(type);
415         attr.setMandatory(!allowNulls);
416
417         if (size >= 0) {
418             attr.setMaxLength(size);
419         }
420
421         if (scale >= 0) {
422             attr.setScale(scale);
423         }
424
425         return attr;
426     }
427
428     public String JavaDoc tableTypeForTable() {
429         return "TABLE";
430     }
431
432     public String JavaDoc tableTypeForView() {
433         return "VIEW";
434     }
435
436     /**
437      * Creates and returns a default implementation of a qualifier translator.
438      */

439     public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
440         return new QualifierTranslator(queryAssembler);
441     }
442
443     /**
444      * Uses JdbcActionBuilder to create the right action.
445      *
446      * @since 1.2
447      */

448     public SQLAction getAction(Query query, DataNode node) {
449         return query
450                 .createSQLAction(new JdbcActionBuilder(this, node.getEntityResolver()));
451     }
452
453     public void bindParameter(
454             PreparedStatement JavaDoc statement,
455             Object JavaDoc object,
456             int pos,
457             int sqlType,
458             int scale) throws SQLException JavaDoc, Exception JavaDoc {
459
460         if (object == null) {
461             statement.setNull(pos, sqlType);
462         }
463         else {
464             ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(
465                     object.getClass());
466             typeProcessor.setJdbcObject(statement, object, pos, sqlType, scale);
467         }
468     }
469
470     public boolean supportsBatchUpdates() {
471         return this.supportsBatchUpdates;
472     }
473
474     public void setSupportsBatchUpdates(boolean flag) {
475         this.supportsBatchUpdates = flag;
476     }
477
478     /**
479      * @since 1.2
480      */

481     public boolean supportsGeneratedKeys() {
482         return supportsGeneratedKeys;
483     }
484
485     /**
486      * @since 1.2
487      */

488     public void setSupportsGeneratedKeys(boolean flag) {
489         this.supportsGeneratedKeys = flag;
490     }
491 }
492
Popular Tags