Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/objectstyle/cayenne/dba/JdbcAdapter.java


1   /* ====================================================================
2    *
3    * The ObjectStyle Group Software License, Version 1.0
4    *
5    * Copyright (c) 2002-2003 The ObjectStyle Group
6    * and individual authors of the software.  All rights reserved.
7    *
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   *
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer.
14   *
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in
17   *    the documentation and/or other materials provided with the
18   *    distribution.
19   *
20   * 3. The end-user documentation included with the redistribution, if
21   *    any, must include the following acknowlegement:
22   *       "This product includes software developed by the
23   *        ObjectStyle Group (http://objectstyle.org/)."
24   *    Alternately, this acknowlegement may appear in the software itself,
25   *    if and wherever such third-party acknowlegements normally appear.
26   *
27   * 4. The names "ObjectStyle Group" and "Cayenne"
28   *    must not be used to endorse or promote products derived
29   *    from this software without prior written permission. For written
30   *    permission, please contact andrus@objectstyle.org.
31   *
32   * 5. Products derived from this software may not be called "ObjectStyle"
33   *    nor may "ObjectStyle" appear in their names without prior written
34   *    permission of the ObjectStyle Group.
35   *
36   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39   * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47   * SUCH DAMAGE.
48   * ====================================================================
49   *
50   * This software consists of voluntary contributions made by many
51   * individuals on behalf of the ObjectStyle Group.  For more
52   * information on the ObjectStyle Group, please see
53   * <http://objectstyle.org/>.
54   *
55   */
56  
57  package org.objectstyle.cayenne.dba;
58  
59  import java.sql.Connection;
60  import java.sql.PreparedStatement;
61  import java.sql.SQLException;
62  import java.util.Iterator;
63  
64  import org.objectstyle.cayenne.CayenneRuntimeException;
65  import org.objectstyle.cayenne.access.DataNode;
66  import org.objectstyle.cayenne.access.OperationObserver;
67  import org.objectstyle.cayenne.access.QueryTranslator;
68  import org.objectstyle.cayenne.access.trans.DeleteTranslator;
69  import org.objectstyle.cayenne.access.trans.FlattenedRelationshipDeleteTranslator;
70  import org.objectstyle.cayenne.access.trans.FlattenedRelationshipInsertTranslator;
71  import org.objectstyle.cayenne.access.trans.InsertTranslator;
72  import org.objectstyle.cayenne.access.trans.ProcedureTranslator;
73  import org.objectstyle.cayenne.access.trans.QualifierTranslator;
74  import org.objectstyle.cayenne.access.trans.QueryAssembler;
75  import org.objectstyle.cayenne.access.trans.SelectTranslator;
76  import org.objectstyle.cayenne.access.trans.SqlModifyTranslator;
77  import org.objectstyle.cayenne.access.trans.SqlSelectTranslator;
78  import org.objectstyle.cayenne.access.trans.UpdateTranslator;
79  import org.objectstyle.cayenne.access.types.ByteArrayType;
80  import org.objectstyle.cayenne.access.types.CharType;
81  import org.objectstyle.cayenne.access.types.ExtendedType;
82  import org.objectstyle.cayenne.access.types.ExtendedTypeMap;
83  import org.objectstyle.cayenne.access.types.UtilDateType;
84  import org.objectstyle.cayenne.map.DbAttribute;
85  import org.objectstyle.cayenne.map.DbAttributePair;
86  import org.objectstyle.cayenne.map.DbEntity;
87  import org.objectstyle.cayenne.map.DbRelationship;
88  import org.objectstyle.cayenne.map.DerivedDbEntity;
89  import org.objectstyle.cayenne.query.BatchQuery;
90  import org.objectstyle.cayenne.query.DeleteQuery;
91  import org.objectstyle.cayenne.query.FlattenedRelationshipDeleteQuery;
92  import org.objectstyle.cayenne.query.FlattenedRelationshipInsertQuery;
93  import org.objectstyle.cayenne.query.InsertQuery;
94  import org.objectstyle.cayenne.query.ProcedureQuery;
95  import org.objectstyle.cayenne.query.Query;
96  import org.objectstyle.cayenne.query.SelectQuery;
97  import org.objectstyle.cayenne.query.SqlModifyQuery;
98  import org.objectstyle.cayenne.query.SqlSelectQuery;
99  import org.objectstyle.cayenne.query.UpdateQuery;
100 
101 /**
102  * A generic DbAdapter implementation.
103  * Can be used as a default adapter or as
104  * a superclass of a concrete adapter implementation.
105  *
106  * @author Andrei Adamchik
107  */
108 public class JdbcAdapter implements DbAdapter {
109     protected PkGenerator pkGenerator;
110     protected TypesHandler typesHandler;
111     protected ExtendedTypeMap extendedTypes;
112     protected boolean supportsBatchUpdates;
113 
114     public JdbcAdapter() {
115         // create Pk generator
116         this.pkGenerator = this.createPkGenerator();
117         this.typesHandler = TypesHandler.getHandler(this.getClass());
118         this.extendedTypes = new ExtendedTypeMap();
119         this.configureExtendedTypes(extendedTypes);
120         this.setSupportsBatchUpdates(false);
121     }
122 
123     /**
124      * Installs appropriate ExtendedTypes as converters for passing values
125      * between JDBC and Java layers. Called from default constructor.
126      */
127     protected void configureExtendedTypes(ExtendedTypeMap map) {
128         // Create a default CHAR handler with some generic settings.
129         // Subclasses may need to install their own CharType or reconfigure
130         // this one to work better with the target database.
131         map.registerType(new CharType(false, true));
132 
133         // enable java.util.Dates as "persistent" values
134         map.registerType(new UtilDateType());
135 
136         // enable "small" BLOBs
137         map.registerType(new ByteArrayType(false, true));
138     }
139 
140     /**
141      * Creates and returns a primary key generator. This factory
142      * method should be overriden by JdbcAdapter subclasses to
143      * provide custom implementations of PKGenerator.
144      */
145     protected PkGenerator createPkGenerator() {
146         return new JdbcPkGenerator();
147     }
148 
149     /** Returns primary key generator associated with this DbAdapter. */
150     public PkGenerator getPkGenerator() {
151         return pkGenerator;
152     }
153 
154     public QueryTranslator getQueryTranslator(Query query) throws Exception {
155         Class queryClass = queryTranslatorClass(query);
156 
157         try {
158             QueryTranslator t = (QueryTranslator) queryClass.newInstance();
159             t.setQuery(query);
160             t.setAdapter(this);
161             return t;
162         }
163         catch (Exception ex) {
164             throw new CayenneRuntimeException(
165                 "Can't load translator class: " + queryClass);
166         }
167     }
168 
169     /**
170      * Returns a class of the query translator that
171      * should be used to translate the query <code>q</code>
172      * to SQL. Exists mainly for the benefit of subclasses
173      * that can override this method providing their own translator.
174      */
175     protected Class queryTranslatorClass(Query q) {
176         if (q == null) {
177             throw new NullPointerException("Null query.");
178         }
179         else if (q instanceof SelectQuery) {
180             return SelectTranslator.class;
181         }
182         else if (q instanceof UpdateQuery) {
183             return UpdateTranslator.class;
184         }
185         else if (q instanceof FlattenedRelationshipInsertQuery) {
186             return FlattenedRelationshipInsertTranslator.class;
187         }
188         else if (q instanceof InsertQuery) {
189             return InsertTranslator.class;
190         }
191         else if (q instanceof FlattenedRelationshipDeleteQuery) {
192             return FlattenedRelationshipDeleteTranslator.class;
193         }
194         else if (q instanceof DeleteQuery) {
195             return DeleteTranslator.class;
196         }
197         else if (q instanceof SqlSelectQuery) {
198             return SqlSelectTranslator.class;
199         }
200         else if (q instanceof SqlModifyQuery) {
201             return SqlModifyTranslator.class;
202         }
203         else if (q instanceof ProcedureQuery) {
204             return ProcedureTranslator.class;
205         }
206         else {
207             throw new CayenneRuntimeException(
208                 "Unrecognized query class..." + q.getClass().getName());
209         }
210     }
211 
212     /** Returns true. */
213     public boolean supportsFkConstraints() {
214         return true;
215     }
216 
217     /**
218      * Returns a SQL string to drop a table corresponding
219      * to <code>ent</code> DbEntity.
220      */
221     public String dropTable(DbEntity ent) {
222         return "DROP TABLE " + ent.getFullyQualifiedName();
223     }
224 
225     /**
226      * Returns a SQL string that can be used to create database table
227      * corresponding to <code>ent</code> parameter.
228      */
229     public String createTable(DbEntity ent) {
230         // later we may support view creation
231         // for derived DbEntities
232         if (ent instanceof DerivedDbEntity) {
233             throw new CayenneRuntimeException(
234                 "Can't create table for derived DbEntity '" + ent.getName() + "'.");
235         }
236 
237         StringBuffer buf = new StringBuffer();
238         buf.append("CREATE TABLE ").append(ent.getFullyQualifiedName()).append(" (");
239 
240         // columns
241         Iterator it = ent.getAttributes().iterator();
242         boolean first = true;
243         while (it.hasNext()) {
244             if (first) {
245                 first = false;
246             }
247             else {
248                 buf.append(", ");
249             }
250 
251             DbAttribute at = (DbAttribute) it.next();
252 
253             // attribute may not be fully valid, do a simple check
254             if (at.getType() == TypesMapping.NOT_DEFINED) {
255                 throw new CayenneRuntimeException(
256                     "Undefined type for attribute '"
257                         + ent.getFullyQualifiedName()
258                         + "."
259                         + at.getName()
260                         + "'.");
261             }
262 
263             String[] types = externalTypesForJdbcType(at.getType());
264             if (types == null || types.length == 0) {
265                 throw new CayenneRuntimeException(
266                     "Undefined type for attribute '"
267                         + ent.getFullyQualifiedName()
268                         + "."
269                         + at.getName()
270                         + "': "
271                         + at.getType());
272             }
273 
274             String type = types[0];
275             buf.append(at.getName()).append(' ').append(type);
276 
277             // append size and precision (if applicable)
278             if (TypesMapping.supportsLength(at.getType())) {
279                 int len = at.getMaxLength();
280                 int prec = TypesMapping.isDecimal(at.getType()) ? at.getPrecision() : -1;
281 
282                 // sanity check
283                 if (prec > len) {
284                     prec = -1;
285                 }
286 
287                 if (len > 0) {
288                     buf.append('(').append(len);
289 
290                     if (prec >= 0) {
291                         buf.append(", ").append(prec);
292                     }
293 
294                     buf.append(')');
295                 }
296             }
297 
298             if (at.isMandatory()) {
299                 buf.append(" NOT NULL");
300             }
301             else {
302         buf.append(" NULL");
303             }
304         }
305 
306         // primary key clause
307         Iterator pkit = ent.getPrimaryKey().iterator();
308         if (pkit.hasNext()) {
309             if (first)
310                 first = false;
311             else
312                 buf.append(", ");
313 
314             buf.append("PRIMARY KEY (");
315             boolean firstPk = true;
316             while (pkit.hasNext()) {
317                 if (firstPk)
318                     firstPk = false;
319                 else
320                     buf.append(", ");
321 
322                 DbAttribute at = (DbAttribute) pkit.next();
323                 buf.append(at.getName());
324             }
325             buf.append(')');
326         }
327         buf.append(')');
328         return buf.toString();
329     }
330 
331     /**
332      * Returns a SQL string that can be used to create
333      * a foreign key constraint for the relationship.
334      */
335     public String createFkConstraint(DbRelationship rel) {
336         StringBuffer buf = new StringBuffer();
337         StringBuffer refBuf = new StringBuffer();
338 
339         buf
340             .append("ALTER TABLE ")
341             .append(((DbEntity) rel.getSourceEntity()).getFullyQualifiedName())
342             .append(" ADD FOREIGN KEY (");
343 
344         Iterator jit = rel.getJoins().iterator();
345         boolean first = true;
346         while (jit.hasNext()) {
347             DbAttributePair join = (DbAttributePair) jit.next();
348             if (!first) {
349                 buf.append(", ");
350                 refBuf.append(", ");
351             }
352             else
353                 first = false;
354 
355             buf.append(join.getSource().getName());
356             refBuf.append(join.getTarget().getName());
357         }
358 
359         buf
360             .append(") REFERENCES ")
361             .append(((DbEntity) rel.getTargetEntity()).getFullyQualifiedName())
362             .append(" (")
363             .append(refBuf.toString())
364             .append(')');
365         return buf.toString();
366     }
367 
368     public String[] externalTypesForJdbcType(int type) {
369         return typesHandler.externalTypesForJdbcType(type);
370     }
371 
372     public ExtendedTypeMap getExtendedTypes() {
373         return extendedTypes;
374     }
375 
376     public DbAttribute buildAttribute(
377         String name,
378         String typeName,
379         int type,
380         int size,
381         int precision,
382         boolean allowNulls) {
383 
384         DbAttribute attr = new DbAttribute();
385         attr.setName(name);
386         attr.setType(type);
387         attr.setMandatory(!allowNulls);
388 
389         if (size >= 0) {
390             attr.setMaxLength(size);
391         }
392 
393         if (precision >= 0) {
394             attr.setPrecision(precision);
395         }
396 
397         return attr;
398     }
399 
400     public String tableTypeForTable() {
401         return "TABLE";
402     }
403 
404     public String tableTypeForView() {
405         return "VIEW";
406     }
407 
408     /**
409      * Creates and returns a default implementation of a qualifier translator.
410      */
411     public QualifierTranslator getQualifierTranslator(QueryAssembler queryAssembler) {
412         return new QualifierTranslator(queryAssembler);
413     }
414 
415     /**
416      * Creates an instance of DataNode class.
417      */
418     public DataNode createDataNode(String name) {
419         DataNode node = new DataNode(name);
420         node.setAdapter(this);
421         return node;
422     }
423 
424     public void bindParameter(
425         PreparedStatement statement,
426         Object object,
427         int pos,
428         int sqlType,
429         int precision)
430         throws SQLException, Exception {
431 
432         if (object == null) {
433             statement.setNull(pos, sqlType);
434         }
435         else {
436             ExtendedType typeProcessor =
437                 getExtendedTypes().getRegisteredType(object.getClass());
438             typeProcessor.setJdbcObject(statement, object, pos, sqlType, precision);
439         }
440     }
441 
442     public boolean supportsBatchUpdates() {
443         return this.supportsBatchUpdates;
444     }
445 
446     public void setSupportsBatchUpdates(boolean flag) {
447         this.supportsBatchUpdates = flag;
448     }
449 
450     /**
451      * Always returns <code>true</code>, letting DataNode
452      * to handle the query.
453      */
454     public boolean shouldRunBatchQuery(
455         DataNode node,
456         Connection con,
457         BatchQuery query,
458         OperationObserver delegate)
459         throws SQLException, Exception {
460         return true;
461     }
462 }