KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > triactive > jdo > store > BaseTable


1 /*
2  * Copyright 2002 (C) TJDO.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the TJDO License version 1.0.
6  * See the terms of the TJDO License in the documentation provided with this software.
7  *
8  * $Id: BaseTable.java,v 1.8 2003/04/28 00:52:17 jackknifebarber Exp $
9  */

10
11 package com.triactive.jdo.store;
12
13 import com.triactive.jdo.model.ClassMetaData;
14 import java.sql.Connection JavaDoc;
15 import java.sql.DatabaseMetaData JavaDoc;
16 import java.sql.ResultSet JavaDoc;
17 import java.sql.SQLException JavaDoc;
18 import java.sql.Statement JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.Set JavaDoc;
27 import org.apache.log4j.Category;
28
29
30 abstract class BaseTable extends AbstractTable
31 {
32     private static final Category LOG = Category.getInstance(BaseTable.class);
33
34     public BaseTable(StoreManager storeMgr)
35     {
36         super(storeMgr);
37     }
38
39
40     public BaseTable(SQLIdentifier name, StoreManager storeMgr)
41     {
42         super(name, storeMgr);
43     }
44
45
46     public PrimaryKey getExpectedPrimaryKey()
47     {
48         PrimaryKey pk = null;
49
50         Iterator JavaDoc i = columns.iterator();
51
52         while (i.hasNext())
53         {
54             Column col = (Column)i.next();
55
56             if (col.isPrimaryKeyPart())
57             {
58                 if (pk == null)
59                     pk = new PrimaryKey(this);
60
61                 pk.addColumn(col);
62             }
63         }
64
65         return pk;
66     }
67
68
69     public void create(Connection JavaDoc conn) throws SQLException JavaDoc
70     {
71         LOG.info("Creating table: " + this);
72
73         super.create(conn);
74     }
75
76
77     public boolean validate(int flags, Connection JavaDoc conn) throws SQLException JavaDoc
78     {
79         assertIsInitialized();
80
81         boolean dbWasModified = false;
82
83         if ((flags & VALIDATE) != 0)
84         {
85             int tableType = storeMgr.getTableType(name, conn);
86
87             if (tableType == TABLE_TYPE_MISSING)
88             {
89                 if ((flags & AUTO_CREATE) == 0)
90                     throw new MissingTableException(this);
91
92                 create(conn);
93                 dbWasModified = true;
94             }
95             else
96             {
97                 LOG.info("Validating table: " + this);
98
99                 if (tableType != TABLE_TYPE_BASE_TABLE)
100                     throw new NotABaseTableException(this);
101
102                 HashMap JavaDoc unvalidated = new HashMap JavaDoc(columnsByName);
103                 Iterator JavaDoc i = storeMgr.getColumnInfo(name, conn).iterator();
104
105                 while (i.hasNext())
106                 {
107                     ColumnInfo ci = (ColumnInfo)i.next();
108                     SQLIdentifier colName = new SQLIdentifier(dba, ci.columnName);
109
110                     Column col = (Column)unvalidated.get(colName);
111
112                     if (col == null)
113                     {
114                         if (!hasColumnName(colName))
115                             throw new UnexpectedColumnException(this, colName);
116                         /*
117                          * Otherwise it's a duplicate column name in the
118                          * metadata and we ignore it. Cloudscape is known to
119                          * do this, although I think that's probably a bug.
120                          */

121                     }
122                     else
123                     {
124                         col.validate(ci);
125                         unvalidated.remove(colName);
126                     }
127                 }
128
129                 if (unvalidated.size() > 0)
130                     throw new MissingColumnException(this, unvalidated.values());
131
132                 PrimaryKey expectedPK = getExpectedPrimaryKey();
133                 Map JavaDoc actualPKs = getExistingPrimaryKeys(conn);
134
135                 if (expectedPK == null)
136                 {
137                     if (!actualPKs.isEmpty())
138                         throw new WrongPrimaryKeyException(this, expectedPK, actualPKs.values());
139                 }
140                 else
141                 {
142                     if (actualPKs.size() != 1 || !actualPKs.values().contains(expectedPK))
143                         throw new WrongPrimaryKeyException(this, expectedPK, actualPKs.values());
144                 }
145             }
146         }
147
148         state = TABLE_STATE_VALIDATED;
149
150         return dbWasModified;
151     }
152
153
154     public boolean validateConstraints(int flags, Connection JavaDoc conn) throws SQLException JavaDoc
155     {
156         assertIsInitialized();
157
158         boolean dbWasModified = false;
159
160         if ((flags & VALIDATE) != 0)
161         {
162             boolean fksWereModified;
163             boolean idxsWereModified;
164
165             if (dba.createIndexesBeforeForeignKeys())
166             {
167                 idxsWereModified = validateIndices(flags, conn);
168                 fksWereModified = validateForeignKeys(flags, conn);
169             }
170             else
171             {
172                 fksWereModified = validateForeignKeys(flags, conn);
173                 idxsWereModified = validateIndices(flags, conn);
174             }
175
176             dbWasModified = fksWereModified || idxsWereModified;
177         }
178
179         return dbWasModified;
180     }
181
182
183     private boolean validateForeignKeys(int flags, Connection JavaDoc conn) throws SQLException JavaDoc
184     {
185         boolean dbWasModified = false;
186
187         /*
188          * Validate and/or create all foreign keys.
189          */

190         Map JavaDoc actualForeignKeysByName = getExistingForeignKeys(conn);
191         int numActualFKs = actualForeignKeysByName.size();
192
193         Map JavaDoc stmtsByFKName = getSQLAddFKStatements(actualForeignKeysByName);
194
195         if (stmtsByFKName.isEmpty())
196         {
197             if (numActualFKs > 0)
198                 LOG.info("Validated " + numActualFKs + " foreign key(s) for table: " + this);
199         }
200         else
201         {
202             if ((flags & AUTO_CREATE) == 0)
203                 throw new MissingForeignKeysException(this, stmtsByFKName.values());
204
205             Statement JavaDoc stmt = conn.createStatement();
206
207             try
208             {
209                 Iterator JavaDoc i = stmtsByFKName.entrySet().iterator();
210
211                 while (i.hasNext())
212                 {
213                     Map.Entry JavaDoc e = (Map.Entry JavaDoc)i.next();
214                     String JavaDoc fkName = (String JavaDoc)e.getKey();
215                     String JavaDoc stmtText = (String JavaDoc)e.getValue();
216
217                     LOG.info("Creating foreign key constraint: " + getSchemaName() + '.' + fkName);
218
219                     long startTime = System.currentTimeMillis();
220
221                     stmt.execute(stmtText);
222
223                     if (LOG.isDebugEnabled())
224                         LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + stmtText);
225
226                     storeMgr.logSQLWarnings(stmt);
227                 }
228             }
229             finally
230             {
231                 stmt.close();
232             }
233
234             dbWasModified = true;
235         }
236
237         return dbWasModified;
238     }
239
240
241     private boolean validateIndices(int flags, Connection JavaDoc conn) throws SQLException JavaDoc
242     {
243         boolean dbWasModified = false;
244
245         /*
246          * Validate and/or create all indices.
247          */

248         Map JavaDoc actualIndicesByName = getExistingIndices(conn);
249
250         /*
251          * Compute the number of existing indices *created by us*, which are
252          * recognized by having a name that starts with the table name. This
253          * number is only used to control what gets logged below. There are
254          * often other system-generated indices.
255          */

256         int numActualIdxs = 0;
257         Iterator JavaDoc names = actualIndicesByName.keySet().iterator();
258
259         while (names.hasNext())
260         {
261             SQLIdentifier idxName = (SQLIdentifier)names.next();
262
263             if (idxName.toString().startsWith(name.toString()))
264                 ++numActualIdxs;
265         }
266
267         Map JavaDoc stmtsByIdxName = getSQLCreateIndexStatements(actualIndicesByName);
268
269         if (stmtsByIdxName.isEmpty())
270         {
271             if (numActualIdxs > 0)
272                 LOG.info("Validated " + numActualIdxs + " index(s) for table: " + this);
273         }
274         else
275         {
276             if ((flags & AUTO_CREATE) == 0)
277                 throw new MissingIndicesException(this, stmtsByIdxName.values());
278
279             Statement JavaDoc stmt = conn.createStatement();
280
281             try
282             {
283                 Iterator JavaDoc i = stmtsByIdxName.entrySet().iterator();
284
285                 while (i.hasNext())
286                 {
287                     Map.Entry JavaDoc e = (Map.Entry JavaDoc)i.next();
288                     String JavaDoc idxName = (String JavaDoc)e.getKey();
289                     String JavaDoc stmtText = (String JavaDoc)e.getValue();
290
291                     LOG.info("Creating index: " + getSchemaName() + '.' + idxName);
292
293                     long startTime = System.currentTimeMillis();
294
295                     stmt.execute(stmtText);
296
297                     if (LOG.isDebugEnabled())
298                         LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + stmtText);
299
300                     storeMgr.logSQLWarnings(stmt);
301                 }
302             }
303             finally
304             {
305                 stmt.close();
306             }
307
308             dbWasModified = true;
309         }
310
311         return dbWasModified;
312     }
313
314
315     public void drop(Connection JavaDoc conn) throws SQLException JavaDoc
316     {
317         LOG.info("Dropping table: " + this);
318
319         super.drop(conn);
320     }
321
322
323     public void dropConstraints(Connection JavaDoc conn) throws SQLException JavaDoc
324     {
325         assertIsInitialized();
326
327         if (!dba.supportsAlterTableDropConstraint())
328             return;
329
330         /*
331          * There's no need to drop indices; we assume they'll go away quietly
332          * when the table is dropped.
333          */

334         DatabaseMetaData JavaDoc dmd = conn.getMetaData();
335
336         HashSet JavaDoc fkNames = new HashSet JavaDoc();
337         Iterator JavaDoc i = storeMgr.getForeignKeyInfo(name, conn).iterator();
338
339         while (i.hasNext())
340         {
341             ForeignKeyInfo fki = (ForeignKeyInfo)i.next();
342
343             /*
344              * Legally, JDBC drivers are allowed to return null names for
345              * foreign keys. If they do, we simply have to skip DROP
346              * CONSTRAINT.
347              */

348             if (fki.fkName != null)
349                 fkNames.add(fki.fkName);
350         }
351
352         int numFKs = fkNames.size();
353
354         if (numFKs > 0)
355         {
356             LOG.info("Dropping " + numFKs + " foreign key(s) for table: " + this);
357
358             i = fkNames.iterator();
359             Statement JavaDoc stmt = conn.createStatement();
360
361             try
362             {
363                 while (i.hasNext())
364                 {
365                     String JavaDoc constraintName = (String JavaDoc)i.next();
366                     String JavaDoc stmtText = "ALTER TABLE " + name + " DROP CONSTRAINT " + constraintName;
367
368                     long startTime = System.currentTimeMillis();
369
370                     stmt.execute(stmtText);
371
372                     if (LOG.isDebugEnabled())
373                         LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + stmtText);
374
375                     storeMgr.logSQLWarnings(stmt);
376                 }
377             }
378             finally
379             {
380                 stmt.close();
381             }
382         }
383     }
384
385
386     protected List JavaDoc getExpectedForeignKeys()
387     {
388         assertIsInitialized();
389
390         ArrayList JavaDoc foreignKeys = new ArrayList JavaDoc();
391         Iterator JavaDoc i = columns.iterator();
392
393         while (i.hasNext())
394         {
395             Column col = (Column)i.next();
396
397             ClassMetaData cmd = ClassMetaData.forClass(col.getType());
398             
399             if (cmd != null)
400             {
401                 ClassBaseTable referencedTable = (ClassBaseTable)storeMgr.getTable(cmd);
402
403                 if (referencedTable != null)
404                     foreignKeys.add(new ForeignKey(col, referencedTable, true));
405             }
406         }
407
408         return foreignKeys;
409     }
410
411
412     protected Set JavaDoc getExpectedIndices()
413     {
414         assertIsInitialized();
415
416         HashSet JavaDoc indices = new HashSet JavaDoc();
417         PrimaryKey pk = getExpectedPrimaryKey();
418         Iterator JavaDoc i = getExpectedForeignKeys().iterator();
419
420         /*
421          * For each foreign key, add to the list an index made up of the "from"
422          * column(s) of the key, *unless* those columns also happen to be the
423          * leading columns of the primary key. If they are, we're assuming that
424          * they're going to be indexed anyway, regardless of whether that fact
425          * gets reflected by getIndexInfo().
426          */

427         while (i.hasNext())
428         {
429             ForeignKey fk = (ForeignKey)i.next();
430
431             if (!pk.startsWith(fk))
432                 indices.add(new Index(fk));
433         }
434
435         return indices;
436     }
437
438
439     private Map JavaDoc getExistingPrimaryKeys(Connection JavaDoc conn) throws SQLException JavaDoc
440     {
441         DatabaseMetaData JavaDoc dmd = conn.getMetaData();
442
443         HashMap JavaDoc primaryKeysByName = new HashMap JavaDoc();
444         ResultSet JavaDoc rs = dmd.getPrimaryKeys(null, getSchemaName(), name.getSQLIdentifier());
445
446         try
447         {
448             while (rs.next())
449             {
450                 SQLIdentifier pkName;
451                 String JavaDoc s = rs.getString(6);
452
453                 if (s == null)
454                     pkName = new PrimaryKeyIdentifier(this);
455                 else
456                     pkName = new SQLIdentifier(dba, s);
457
458                 PrimaryKey pk = (PrimaryKey)primaryKeysByName.get(pkName);
459
460                 if (pk == null)
461                 {
462                     pk = new PrimaryKey(this);
463                     primaryKeysByName.put(pkName, pk);
464                 }
465
466                 int keySeq = rs.getInt(5) - 1;
467                 SQLIdentifier colName = new SQLIdentifier(dba, rs.getString(4));
468
469                 Column col = (Column)columnsByName.get(colName);
470
471                 if (col == null)
472                     throw new UnexpectedColumnException(this, colName);
473
474                 pk.setColumn(keySeq, col);
475             }
476         }
477         finally
478         {
479             rs.close();
480         }
481
482         return primaryKeysByName;
483     }
484
485
486     private Map JavaDoc getExistingForeignKeys(Connection JavaDoc conn) throws SQLException JavaDoc
487     {
488         DatabaseMetaData JavaDoc dmd = conn.getMetaData();
489
490         HashMap JavaDoc foreignKeysByName = new HashMap JavaDoc();
491         Iterator JavaDoc i = storeMgr.getForeignKeyInfo(name, conn).iterator();
492
493         while (i.hasNext())
494         {
495             ForeignKeyInfo fki = (ForeignKeyInfo)i.next();
496             SQLIdentifier fkName;
497
498             if (fki.fkName == null)
499                 fkName = new ForeignKeyIdentifier(this, foreignKeysByName.size());
500             else
501                 fkName = new SQLIdentifier(dba, fki.fkName);
502
503             boolean initiallyDeferred = fki.deferrability == DatabaseMetaData.importedKeyInitiallyDeferred;
504
505             ForeignKey fk = (ForeignKey)foreignKeysByName.get(fkName);
506
507             if (fk == null)
508             {
509                 fk = new ForeignKey(initiallyDeferred);
510                 foreignKeysByName.put(fkName, fk);
511             }
512
513             BaseTable refTable = (BaseTable)storeMgr.getTable(new SQLIdentifier(dba, fki.pkTableName));
514
515             if (refTable != null)
516             {
517                 SQLIdentifier colName = new SQLIdentifier(dba, fki.fkColumnName);
518                 SQLIdentifier refColName = new SQLIdentifier(dba, fki.pkColumnName);
519
520                 Column col = (Column)columnsByName.get(colName);
521                 Column refCol = (Column)refTable.columnsByName.get(refColName);
522
523                 if (col == null)
524                     throw new UnexpectedColumnException(this, colName);
525                 if (refCol == null)
526                     throw new UnexpectedColumnException(this, refColName);
527
528                 fk.addColumn(col, refCol);
529             }
530         }
531
532         return foreignKeysByName;
533     }
534
535
536     private Map JavaDoc getExistingIndices(Connection JavaDoc conn) throws SQLException JavaDoc
537     {
538         DatabaseMetaData JavaDoc dmd = conn.getMetaData();
539
540         HashMap JavaDoc indicesByName = new HashMap JavaDoc();
541         ResultSet JavaDoc rs = dmd.getIndexInfo(null, getSchemaName(), name.getSQLIdentifier(), false, true);
542
543         try
544         {
545             while (rs.next())
546             {
547                 short idxType = rs.getShort(7);
548
549                 if (idxType == DatabaseMetaData.tableIndexStatistic)
550                     continue;
551
552                 SQLIdentifier idxName = new SQLIdentifier(dba, rs.getString(6));
553                 Index idx = (Index)indicesByName.get(idxName);
554
555                 if (idx == null)
556                 {
557                     boolean isUnique = !rs.getBoolean(4);
558                     idx = new Index(this, isUnique);
559                     indicesByName.put(idxName, idx);
560                 }
561
562                 int colSeq = rs.getShort(8) - 1;
563                 SQLIdentifier colName = new SQLIdentifier(dba, rs.getString(9));
564
565                 Column col = (Column)columnsByName.get(colName);
566                 if (col == null)
567                     throw new UnexpectedColumnException(this, colName);
568
569                 idx.setColumn(colSeq, col);
570             }
571         }
572         finally
573         {
574             rs.close();
575         }
576
577         return indicesByName;
578     }
579
580
581     protected List JavaDoc getSQLCreateStatements()
582     {
583         assertIsInitialized();
584
585         ArrayList JavaDoc stmts = new ArrayList JavaDoc();
586
587         stmts.add(dba.getCreateTableStatement(this, (Column[])columns.toArray(new Column[columns.size()])));
588
589         PrimaryKey pk = getExpectedPrimaryKey();
590
591         if (pk != null)
592             stmts.add(dba.getAddPrimaryKeyStatement(new PrimaryKeyIdentifier(this), pk));
593
594         return stmts;
595     }
596
597
598     protected Map JavaDoc getSQLAddFKStatements(Map JavaDoc actualForeignKeysByName)
599     {
600         assertIsInitialized();
601
602         HashMap JavaDoc stmtsByFKName = new HashMap JavaDoc();
603
604         List JavaDoc expectedForeignKeys = getExpectedForeignKeys();
605
606         Iterator JavaDoc i = expectedForeignKeys.iterator();
607         int n = 1;
608
609         while (i.hasNext())
610         {
611             ForeignKey fk = (ForeignKey)i.next();
612
613             if (!actualForeignKeysByName.containsValue(fk))
614             {
615                 ForeignKeyIdentifier fkName;
616
617                 do
618                 {
619                     fkName = new ForeignKeyIdentifier(this, n++);
620                 } while (actualForeignKeysByName.containsKey(fkName));
621
622                 String JavaDoc stmtText = dba.getAddForeignKeyStatement(fkName, fk);
623
624                 stmtsByFKName.put(fkName.getSQLIdentifier(), stmtText);
625             }
626         }
627
628         return stmtsByFKName;
629     }
630
631
632     private boolean isIndexReallyNeeded(Index requiredIdx, Collection JavaDoc actualIndices)
633     {
634         Iterator JavaDoc i = actualIndices.iterator();
635
636         while (i.hasNext())
637         {
638             Index actualIdx = (Index)i.next();
639
640             if (actualIdx.startsWith(requiredIdx))
641                 return false;
642         }
643
644         return true;
645     }
646
647
648     protected Map JavaDoc getSQLCreateIndexStatements(Map JavaDoc actualIndicesByName)
649     {
650         assertIsInitialized();
651
652         HashMap JavaDoc stmtsByIdxName = new HashMap JavaDoc();
653
654         Set JavaDoc expectedIndices = getExpectedIndices();
655
656         Iterator JavaDoc i = expectedIndices.iterator();
657         int n = 1;
658
659         while (i.hasNext())
660         {
661             Index idx = (Index)i.next();
662
663             if (isIndexReallyNeeded(idx, actualIndicesByName.values()))
664             {
665                 IndexIdentifier idxName;
666
667                 do
668                 {
669                     idxName = new IndexIdentifier(this, idx.getUnique(), n++);
670                 } while (actualIndicesByName.containsKey(idxName));
671
672                 String JavaDoc stmtText = dba.getCreateIndexStatement(idxName, idx);
673
674                 stmtsByIdxName.put(idxName.getSQLIdentifier(), stmtText);
675             }
676         }
677
678         return stmtsByIdxName;
679     }
680
681
682     protected List JavaDoc getSQLDropStatements()
683     {
684         assertIsInitialized();
685
686         ArrayList JavaDoc stmts = new ArrayList JavaDoc();
687         stmts.add(dba.getDropTableStatement(this));
688
689         return stmts;
690     }
691 }
692
Popular Tags