KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > metadata > JdbcClass


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc.metadata;
13
14 import com.versant.core.metadata.ClassMetaData;
15 import com.versant.core.metadata.FieldMetaData;
16 import com.versant.core.jdbc.JdbcKeyGenerator;
17 import com.versant.core.jdbc.sql.exp.*;
18 import com.versant.core.jdbc.sql.SqlDriver;
19 import com.versant.core.common.Debug;
20 import com.versant.core.util.CharBuf;
21
22 import java.io.Serializable JavaDoc;
23 import java.io.PrintStream JavaDoc;
24 import java.util.*;
25
26 /**
27  * Extra meta data for a class stored in JDBC. Some of the fields are
28  * also present in the normal ClassMetaData and are duplicated here for
29  * performance reasons.
30  */

31 public final class JdbcClass implements Serializable JavaDoc {
32
33     /**
34      * Do not detect concurrent updates to this class.
35      */

36     public static final int OPTIMISTIC_LOCKING_NONE = 1;
37     /**
38      * Detect concurrent updates to this class by using a version column.
39      */

40     public static final int OPTIMISTIC_LOCKING_VERSION = 2;
41     /**
42      * Detect concurrent updates to this class by using a timestamp column.
43      */

44     public static final int OPTIMISTIC_LOCKING_TIMESTAMP = 3;
45     /**
46      * Detect concurrent updates to this class by including the previous
47      * values of all changed fields in the where clause.
48      */

49     public static final int OPTIMISTIC_LOCKING_CHANGED = 4;
50
51     /**
52      * Subclass fields stored in the table of its immediate superclass.
53      */

54     public static final int INHERITANCE_FLAT = 1;
55     /**
56      * Subclass fields stored in its own table.
57      */

58     public static final int INHERITANCE_VERTICAL = 2;
59     /**
60      * Fields stored in subclass table.
61      */

62     public static final int INHERITANCE_HORIZONTAL = 3;
63
64     /**
65      * The normal meta data for our class.
66      */

67     public ClassMetaData cmd;
68     /**
69      * Our SqlDriver.
70      */

71     public final SqlDriver sqlDriver;
72     /**
73      * The inheritance strategy for this class. This will be INHERITANCE_FLAT
74      * if the class is not in a heirachy or is a base class.
75      *
76      * @see #INHERITANCE_FLAT
77      * @see #INHERITANCE_VERTICAL
78      */

79     public int inheritance;
80     /**
81      * Our table. This may be the table of the pcSuperClass if this is a
82      * subclass with fields stored in the base table.
83      */

84     public JdbcTable table;
85     /**
86      * The name of our table.
87      */

88     public String JavaDoc tableName;
89     /**
90      * All the tables for this class including superclass tables. The entry
91      * at 0 is the table for the topmost superclass. Also
92      * table == allTables[allTables.length - 1].
93      */

94     public JdbcTable[] allTables;
95     /**
96      * The fields in fieldNo order. Note that if a fieldNo is transactional
97      * then its entry here will be null.
98      */

99     public JdbcField[] fields;
100     /**
101      * The fields in State fieldNo order. Note that if a fieldNo is
102      * transactional then its entry here will be null. This includes all
103      * fields from superclasses.
104      */

105     public JdbcField[] stateFields;
106     /**
107      * mapping from column name to the jdbc fields
108      */

109     private transient Map colNamesToJdbcField;
110     /**
111      * The JDBC key generator for this class (null if no key generator is
112      * required).
113      */

114     public JdbcKeyGenerator jdbcKeyGenerator;
115     /**
116      * This becomes the default value for the useJoin field for references
117      * to this class.
118      *
119      * @see JdbcRefField#useJoin
120      */

121     public int useJoin;
122     /**
123      * Our classId for this class. This does not have to be the same as that
124      * in ClassMetaData. It only has to be unique within an inheritance
125      * heirachy. This will default to the classId of the class. If the
126      * classIdCol column is a number (INT etc) then this must be a Number.
127      * Otherwise it must be a String.
128      *
129      * @see ClassMetaData#classId
130      */

131     public Object JavaDoc jdbcClassId;
132     /**
133      * The column used to hold the classId value for each row. This is used
134      * to implement inheritance. It may be null if this class has no persistent
135      * subclasses or if all of the subclasses use vertical inheritance. This
136      * field is set to the same value for all the classes in a heirachy.
137      */

138     public JdbcColumn classIdCol;
139     /**
140      * Treat rows in the database that would be instances of us as instances
141      * of readAsClass instead. This is used to implement flat inheritance
142      * with no descriminator where rows are instances of the leaf class.
143      */

144     public ClassMetaData readAsClass;
145     /**
146      * How optimistic locking is done for this class (one of the
147      * OPTIMISTIC_LOCKING_xxx constants).
148      */

149     public int optimisticLocking;
150     /**
151      * The field used to store the row version or timestamp value for
152      * this table for optimistic locking. It may be null. This may be
153      * a fake field.
154      */

155     public JdbcSimpleField optimisticLockingField;
156     /**
157      * If this flag is set the the table for this class will not be created
158      * by the create schema task or the workbench.
159      */

160     public boolean doNotCreateTable;
161     /**
162      * Cache for SQL required to delete a main table row for this class.
163      */

164     public String JavaDoc deleteRowSql;
165     /**
166      * Cache for SQL required to lock the main table row for this class.
167      */

168     public String JavaDoc lockRowSql;
169     /**
170      * Do not use statement batching with this class.
171      */

172     public boolean noBatching;
173
174     private String JavaDoc lockRowColumnName; // column used for update locking
175

176     public JdbcClass(SqlDriver sqlDriver) {
177         this.sqlDriver = sqlDriver;
178     }
179
180     public String JavaDoc toString() {
181         return cmd.qname + " (" + table.name + ")";
182     }
183
184     /**
185      * Add all tables that belong to this class to the set.
186      */

187     public void getTables(HashSet tables) {
188         if (table != null) {
189             tables.add(table);
190         }
191         int n = 0;
192         if (fields != null) {
193             n = fields.length;
194         }
195         for (int i = 0; i < n; i++) {
196             JdbcField f = fields[i];
197             if (f != null) f.getTables(tables);
198         }
199     }
200
201     /**
202      * Build the stateFields array for this class.
203      */

204     public void buildStateFields() {
205         if (cmd.pcSuperMetaData == null) buildStateFieldsImp();
206     }
207
208     private void buildStateFieldsImp() {
209         if (cmd.pcSuperMetaData != null) {
210             JdbcField[] superStateFields = ((JdbcClass)cmd.pcSuperMetaData.storeClass).stateFields;
211             if (superStateFields != null) {
212                 int n = superStateFields.length;
213                 stateFields = new JdbcField[n + fields.length];
214                 System.arraycopy(superStateFields, 0, stateFields, 0, n);
215                 System.arraycopy(fields, 0, stateFields, n, fields.length);
216             }
217         } else {
218             stateFields = fields;
219         }
220         if (cmd.pcSubclasses != null) {
221             for (int i = cmd.pcSubclasses.length - 1; i >= 0; i--) {
222                 ((JdbcClass)cmd.pcSubclasses[i].storeClass).buildStateFieldsImp();
223             }
224         }
225     }
226
227     /**
228      * Set the key generator for this class and recursively all its subclasses.
229      */

230     public void setJdbcKeyGenerator(JdbcKeyGenerator jdbcKeyGenerator) {
231         this.jdbcKeyGenerator = jdbcKeyGenerator;
232         cmd.useKeyGen = true;
233         cmd.postInsertKeyGenerator = jdbcKeyGenerator.isPostInsertGenerator();
234         ClassMetaData[] pcSubclasses = cmd.pcSubclasses;
235         if (pcSubclasses == null) return;
236         for (int i = 0; i < pcSubclasses.length; i++) {
237             ((JdbcClass)pcSubclasses[i].storeClass).setJdbcKeyGenerator(jdbcKeyGenerator);
238         }
239     }
240
241     /**
242      * Copy our optimistic locking settings to all of our subclasses.
243      */

244     public void copyOptimisticLockingToSubs() {
245         ClassMetaData[] pcSubclasses = cmd.pcSubclasses;
246         if (pcSubclasses == null) return;
247         for (int i = 0; i < pcSubclasses.length; i++) {
248             JdbcClass sc = (JdbcClass)pcSubclasses[i].storeClass;
249             sc.optimisticLocking = optimisticLocking;
250             sc.cmd.changedOptimisticLocking =
251                     optimisticLocking == JdbcClass.OPTIMISTIC_LOCKING_CHANGED;
252             sc.optimisticLockingField = optimisticLockingField;
253             if (optimisticLockingField != null) {
254                 sc.cmd.optimisticLockingField = optimisticLockingField.fmd;
255             }
256             sc.copyOptimisticLockingToSubs();
257         }
258     }
259
260     /**
261      * Set the classIdField for this class and recursively all its subclasses.
262      */

263     public void setClassIdCol(JdbcColumn classIdCol) {
264         this.classIdCol = classIdCol;
265         ClassMetaData[] pcSubclasses = cmd.pcSubclasses;
266         if (pcSubclasses == null) return;
267         for (int i = 0; i < pcSubclasses.length; i++) {
268             ((JdbcClass)pcSubclasses[i].storeClass).setClassIdCol(classIdCol);
269         }
270     }
271
272     /**
273      * Find the class with the given JDBC classId. This might be ourselves or
274      * one of our subclasses. Returns null if not found.
275      */

276     public ClassMetaData findClass(Object JavaDoc jdbcClassId) {
277         if (this.jdbcClassId.equals(jdbcClassId)) return cmd;
278         ClassMetaData[] subs = cmd.pcSubclasses;
279         if (subs != null) {
280             for (int i = subs.length - 1; i >= 0; i--) {
281                 ClassMetaData ans = ((JdbcClass)subs[i].storeClass).findClass(jdbcClassId);
282                 if (ans != null) return ans;
283             }
284         }
285         return null;
286     }
287
288     /**
289      * Is this class stored in a different table to the base class in the
290      * heirachy?
291      */

292     public boolean isMultiTableHeirachy() {
293         return table != ((JdbcClass)cmd.pcHeirachy[0].storeClass).table;
294     }
295
296     public void dump() {
297         dump(Debug.OUT, "");
298     }
299
300     public void dump(PrintStream JavaDoc out, String JavaDoc indent) {
301         out.println(indent + this);
302         String JavaDoc is = indent + " ";
303         out.println(is + "cmd = " + cmd);
304         out.println(is + "jdbcKeyGenerator = " + jdbcKeyGenerator);
305         out.println(is + "useJoin = " + JdbcRefField.toUseJoinString(useJoin));
306         out.println(is + "classId = " + jdbcClassId);
307         out.println(is + "classIdCol = " + classIdCol);
308         out.println(is + "optimisticLocking = " +
309                 toOptimisticLockingString(optimisticLocking));
310         out.println(
311                 is + "optLockFieldStateFieldNo = " + optimisticLockingField);
312         out.println(is + "fields = " +
313                 (fields == null ? "null" : Integer.toString(fields.length)));
314         out.println(is + "stateFields = " +
315                 (stateFields == null ? "null" : Integer.toString(
316                         stateFields.length)));
317     }
318
319     public static String JavaDoc toOptimisticLockingString(int o) {
320         switch (o) {
321             case OPTIMISTIC_LOCKING_NONE:
322                 return "none";
323             case OPTIMISTIC_LOCKING_CHANGED:
324                 return "changed";
325             case OPTIMISTIC_LOCKING_VERSION:
326                 return "version";
327             case OPTIMISTIC_LOCKING_TIMESTAMP:
328                 return "timestamp";
329         }
330         return "unknown(" + o + ")";
331     }
332
333     /**
334      * Find a primary key field of this class or the topmost superclass in
335      * the heirachy by name or null if none.
336      */

337     public JdbcSimpleField findPkField(String JavaDoc fname) {
338         FieldMetaData f = cmd.findPkField(fname);
339         if (f == null) return null;
340         return (JdbcSimpleField)f.storeField;
341     }
342
343     /**
344      * Find the index of the primary key field f or -1 if none.
345      */

346     public int findPkFieldIndex(JdbcSimpleField f) {
347         for (int i = cmd.pkFields.length - 1; i >= 0; i--) {
348             if (cmd.pkFields[i].storeField == f) return i;
349         }
350         return -1;
351     }
352
353     /**
354      * Get the maximum number of OIDs for this class that can be included
355      * in an IN (?, .., ?) statement. This depends on the database and the
356      * number of columns in the primary key. The return value is zero if
357      * this class has a composite primary key.
358      */

359     public int getMaxOIDsForIN(SqlDriver sqlDriver) {
360         if (table.pkSimpleColumnCount > 1) return 0;
361         return sqlDriver.getMaxInOperands();
362     }
363
364     /**
365      * Find all fields in this class that have columnName in their main table
366      * columns and add them to list.
367      */

368     public void findFieldsForColumn(ClassMetaData cmd, String JavaDoc columnName,
369             List list) {
370         for (int i = 0; i < fields.length; i++) {
371             JdbcField f = fields[i];
372             if (f != null && f.findMainTableColumn(columnName) != null) {
373                 ClassMetaData fcmd = f.fmd.classMetaData;
374                 if ((cmd.isAncestorOrSelf(fcmd) || fcmd.isAncestorOrSelf(cmd))) {
375                     list.add(f);
376                 }
377             }
378         }
379     }
380
381     /**
382      * Find any columns in any of our subclasses (and recursively their
383      * subclasses) with columnName and set shared = true for them.
384      */

385     public void markSubclassColumnsShared(String JavaDoc columnName) {
386         if (cmd.pcSubclasses == null) return;
387         for (int i = cmd.pcSubclasses.length - 1; i >= 0; i--) {
388             JdbcClass jc = (JdbcClass)cmd.pcSubclasses[i].storeClass;
389             jc.markColumnsShared(columnName, table);
390             jc.markSubclassColumnsShared(columnName);
391         }
392     }
393
394     /**
395      * Mark any columns for fields in this class with name columnName
396      * shared = true unless they are primary key fields.
397      */

398     public void markColumnsShared(String JavaDoc columnName, JdbcTable cTable) {
399         for (int i = 0; i < fields.length; i++) {
400             JdbcField f = fields[i];
401             if (f == null || f.fmd.primaryKey) continue;
402             if (f.mainTable != cTable) continue;
403             JdbcColumn c = f.findMainTableColumn(columnName);
404             if (c != null) c.setShared(true);
405         }
406     }
407
408     /**
409      * Get the column that should be used for update locking.
410      */

411     public String JavaDoc getLockRowColumnName() {
412         if (lockRowColumnName == null) {
413             lockRowColumnName = table.getLockRowColumn().name;
414         }
415         return lockRowColumnName;
416     }
417
418     /**
419      * Set the table for this class. This will also set the tablename and
420      * allTables fields. This method must only be invoked one a subclass
421      * if it has been called on its superclass.
422      */

423     public void setTable(JdbcTable table) {
424         this.table = table;
425         tableName = table.name;
426         ArrayList a = new ArrayList();
427         JdbcTable prev = null;
428         for (int i = 0; i < cmd.pcHeirachy.length; i++) {
429             JdbcTable t = ((JdbcClass)cmd.pcHeirachy[i].storeClass).table;
430             if (t != prev) a.add(t);
431             prev = t;
432         }
433         allTables = new JdbcTable[a.size()];
434         a.toArray(allTables);
435     }
436
437     /**
438      * Get a LiteralExp for our jdbc-class-id. If subclasses is true then
439      * a list of LiteralExp's are returned including all the
440      */

441     public LiteralExp getClassIdExp(boolean subclasses) {
442         LiteralExp root = classIdCol.createClassIdLiteralExp(jdbcClassId);
443
444         if (subclasses && cmd.pcSubclasses != null) {
445             ClassMetaData[] a = cmd.pcSubclasses;
446             SqlExp pos = root;
447             for (int i = a.length - 1; i >= 0; i--) {
448                 pos = pos.next = ((JdbcClass)a[i].storeClass).getClassIdExp(true);
449                 for (; pos.next != null; pos = pos.next) ;
450             }
451         }
452         return root;
453     }
454
455     /**
456      * Get an SqlExp that will only return instances of this class or one
457      * of its subclasses from this table.
458      *
459      * @param se A select against our table
460      */

461     public SqlExp getCheckClassIdExp(SelectExp se) {
462         if (classIdCol == null) return null;
463         SqlExp colExp = classIdCol.toSqlExp(se);
464         LiteralExp idExp = getClassIdExp(true);
465         if (idExp.next == null) {
466             return new BinaryOpExp(colExp, BinaryOpExp.EQUAL, idExp);
467         } else {
468             colExp.next = idExp;
469             return new InExp(colExp);
470         }
471     }
472
473     /**
474      * See if our jdbcClassId that those of all of our subclasses are ints.
475      * This is used to default the type of the descriminator column to
476      * INTEGER if possible.
477      */

478     public boolean isIntJdbcClassIdHeirachy() {
479         if (jdbcClassId == null) return false;
480         if (jdbcClassId instanceof Integer JavaDoc) return true;
481         try {
482             Integer.parseInt((String JavaDoc)jdbcClassId);
483         } catch (NumberFormatException JavaDoc e) {
484             return false;
485         }
486         if (cmd.pcSubclasses == null) return true;
487         for (int i = cmd.pcSubclasses.length - 1; i >= 0; i--) {
488             if (!((JdbcClass)cmd.pcSubclasses[i].storeClass).isIntJdbcClassIdHeirachy()) {
489                 return false;
490             }
491         }
492         return true;
493     }
494
495     /**
496      * Convert our jdbcClassId to an Integer if it is not already an Integer.
497      * Recursively process subclasses.
498      */

499     public void convertJdbcClassIdToInteger() {
500         if (jdbcClassId instanceof String JavaDoc) {
501             jdbcClassId = new Integer JavaDoc((String JavaDoc)jdbcClassId);
502         }
503         if (cmd.pcSubclasses != null) {
504             for (int i = cmd.pcSubclasses.length - 1; i >= 0; i--) {
505                 ((JdbcClass)cmd.pcSubclasses[i].storeClass).convertJdbcClassIdToInteger();
506             }
507         }
508     }
509
510     public Map getColNamesToJdbcField() {
511         if (colNamesToJdbcField == null) {
512             colNamesToJdbcField = new HashMap(stateFields.length);
513             JdbcField[] fs = stateFields;
514             for (int i = 0; i < fs.length; i++) {
515                 JdbcField f = fs[i];
516                 if (f.mainTableCols != null) {
517                     JdbcColumn[] cols = f.mainTableCols;
518                     for (int j = 0; j < cols.length; j++) {
519                         JdbcColumn col = cols[j];
520                         colNamesToJdbcField.put(col.name.toUpperCase(), f);
521                     }
522                 }
523             }
524         }
525         return colNamesToJdbcField;
526     }
527
528     /**
529      * Get SQL to lock a row in our table.
530      */

531     public String JavaDoc getLockRowSql() {
532         if (lockRowSql == null) {
533             CharBuf s = new CharBuf();
534             s.append("UPDATE ");
535             s.append(table.name);
536             s.append(" SET ");
537             String JavaDoc c = getLockRowColumnName();
538             s.append(c);
539             s.append('=');
540             s.append(c);
541             s.append(" WHERE ");
542             table.appendWherePK(s);
543             lockRowSql = s.toString();
544         }
545         return lockRowSql;
546     }
547
548 }
549
550
Popular Tags