KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opensubsystems > core > persist > db > VersionedDatabaseSchema


1 /*
2  * Copyright (c) 2003 - 2007 OpenSubsystems s.r.o. Slovak Republic. All rights reserved.
3  *
4  * Project: OpenSubsystems
5  *
6  * $Id: VersionedDatabaseSchema.java,v 1.21 2007/01/10 05:16:58 bastafidli Exp $
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */

21
22 package org.opensubsystems.core.persist.db;
23
24 import java.sql.Connection JavaDoc;
25 import java.sql.PreparedStatement JavaDoc;
26 import java.sql.ResultSet JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import java.sql.Statement JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.logging.Level JavaDoc;
33 import java.util.logging.Logger JavaDoc;
34
35 import org.apache.commons.collections.map.LinkedMap;
36 import org.opensubsystems.core.error.OSSDatabaseAccessException;
37 import org.opensubsystems.core.error.OSSException;
38 import org.opensubsystems.core.util.DatabaseUtils;
39 import org.opensubsystems.core.util.GlobalConstants;
40 import org.opensubsystems.core.util.Log;
41
42 /**
43  * Versioned database schema provide functionality of creating and upgrading
44  * of individual database schemas in the database based on their versions.
45  * This class keeps track of existing and current versiones of database
46  * schemas and upgrades them as necesary
47  *
48  * @version $Id: VersionedDatabaseSchema.java,v 1.21 2007/01/10 05:16:58 bastafidli Exp $
49  * @author Miro Halas
50  * @code.reviewer Miro Halas
51  * @code.reviewed 1.14 2006/04/05 05:00:52 bastafidli
52  */

53 public class VersionedDatabaseSchema extends DatabaseSchemaImpl
54 {
55    /*
56       Database tables used to track database schemas in the database
57       The real name can be different based on prefix
58    
59       CREATE TABLE BF_SCHEMA
60       (
61          SCHEMA_NAME VARCHAR(50) NOT NULL,
62          SCHEMA_VERSION INTEGER NOT NULL,
63          CREATION_TIMESTAMP TIMESTAMP NOT NULL,
64          MODIFICATION_TIMESTAMP TIMESTAMP NOT NULL,
65          CONSTRAINT BF_SCH_PK PRIMARY KEY (SCHEMA_NAME)
66       );
67    */

68
69    // Constants ////////////////////////////////////////////////////////////////
70

71    /**
72     * Name identifies this schema in the database.
73     */

74    public static final String JavaDoc VERSIONED_SCHEMA_NAME = "SCHEMA";
75
76    /**
77     * Version of this schema in the database.
78     * Version 1 - original
79     * Version 2 - PostgreSQL added user defined type
80     */

81    public static final int VERSIONED_SCHEMA_VERSION = 2;
82
83    /**
84     * Full name of the table used by this schema.
85     */

86    public static final String JavaDoc SCHEMA_TABLE_NAME = getVersionedDatabaseSchemaTableName();
87
88    // Attributes ///////////////////////////////////////////////////////////////
89

90    /**
91     * Map of schemas which are versioned in the database.
92     * Key is the name of the schema, value is instance of DatabaseSchema object.
93     */

94    private Map JavaDoc m_mpSchemas;
95
96    // Cached values ////////////////////////////////////////////////////////////
97

98    /**
99     * Logger for this class
100     */

101    private static Logger JavaDoc s_logger = Log.getInstance(VersionedDatabaseSchema.class);
102
103    // Constructors /////////////////////////////////////////////////////////////
104

105    /**
106     * Default constructor.
107     *
108     * @throws OSSException - database cannot be started.
109     */

110    public VersionedDatabaseSchema(
111    ) throws OSSException
112    {
113       super(null, VERSIONED_SCHEMA_NAME, VERSIONED_SCHEMA_VERSION, true);
114       
115       // This has to be sequenced hashmap to create the schemas in the correct order
116
m_mpSchemas = new LinkedMap();
117       // This schema itself is manageged by itself
118
add(this);
119    }
120
121    // Accessors ////////////////////////////////////////////////////////////////
122

123    /**
124     * @return String - get full name of the table used by this schema.
125     */

126    private static String JavaDoc getVersionedDatabaseSchemaTableName(
127    )
128    {
129       return getSchemaPrefix() + "SCHEMA";
130    }
131
132    // Manipulators /////////////////////////////////////////////////////////////
133

134    /**
135     * Add new database schema to be managed by versioned schema.
136     *
137     * @param dsSchema - new database schema to be managed
138     * @throws OSSException - database cannot be started.
139     */

140    public void add(
141       DatabaseSchema dsSchema
142    ) throws OSSException
143    {
144       if (GlobalConstants.ERROR_CHECKING)
145       {
146          assert dsSchema != null : "Cannot add null schema.";
147          // This is not valid assert since due to dependencies the same
148
// schema mighe be added multiple times
149
// assert m_mpSchemas.get(dsSchema.getName())
150
// != null : "Database schema with name "
151
// + dsSchema.getName()
152
// + " is already managed by versioned database schema.";
153
}
154
155       if (m_mpSchemas.get(dsSchema.getName()) != null)
156       {
157          s_logger.log(Level.FINEST, "Database schema " + dsSchema.getName()
158                              + " is already managed by versioned schema."
159                              + " It is not added second time.");
160       }
161       else
162       {
163          DatabaseSchema[] arSchemas;
164
165          arSchemas = dsSchema.getDependentSchemas();
166          // add all dependent schemas
167
// TODO: Feature: Add schemas circular dependency check here
168
if (arSchemas != null)
169          {
170             for (int iIndex = 0; iIndex < arSchemas.length; iIndex++)
171             {
172                add(arSchemas[iIndex]);
173             }
174          }
175          m_mpSchemas.put(dsSchema.getName(), dsSchema);
176          s_logger.log(Level.FINEST, "Database schema " + dsSchema.getName()
177                              + " version " + dsSchema.getVersion()
178                              + " is now managed by versioned schema.");
179       }
180    }
181
182    // Lifecycle events /////////////////////////////////////////////////////////
183

184    /**
185     * Initialize the schema. This function should make sure that the schema in
186     * the database is up to date, perform any upgrades if necessary, etc.
187     *
188     * @param cntDBConnection - valid connection to database
189     * @param strUserName - name of user who will be accessing this table
190     * @throws OSSException - problem initializing the schema
191     * @throws SQLException - problem initializing the schema
192     */

193    public void init(
194       Connection JavaDoc cntDBConnection,
195       String JavaDoc strUserName
196    ) throws OSSException,
197             SQLException JavaDoc
198    {
199       if (GlobalConstants.ERROR_CHECKING)
200       {
201          assert cntDBConnection != null : "Connection is not valid.";
202       }
203
204       // MHALAS: This has to be sequenced hash map to create the schemas
205
// in correct order
206
// Current schemas will at the end contains schemas which must be added
207
Map JavaDoc mpSchemasToAdd = new LinkedMap(m_mpSchemas);
208       // Schemas to upgrade will contain schemas which must be upgraded
209
Map JavaDoc mpSchemasToUpgrade = new LinkedMap();
210
211       loadExistingSchemas(cntDBConnection, mpSchemasToAdd, mpSchemasToUpgrade);
212       createOrUpgradeSchemas(cntDBConnection, strUserName, mpSchemasToAdd,
213                              mpSchemasToUpgrade);
214    }
215
216    /**
217     * Create the schema.
218     *
219     * @param cntDBConnection - valid connection to database
220     * @param strUserName - name of user who will be accessing this table
221     * @throws SQLException - problem creating the schema
222     * @throws OSSException - problem creating the schema
223     */

224    public void create(
225       Connection JavaDoc cntDBConnection,
226       String JavaDoc strUserName
227    ) throws SQLException JavaDoc, OSSException
228    {
229       s_logger.entering(this.getClass().getName(), "create");
230
231       try
232       {
233          Statement JavaDoc stmQuery = null;
234          try
235          {
236
237             stmQuery = cntDBConnection.createStatement();
238             if (stmQuery.execute(
239                   "create table " + SCHEMA_TABLE_NAME + NL
240                      + "(" + NL
241                      + " SCHEMA_NAME VARCHAR(50) NOT NULL," + NL
242                      + " SCHEMA_VERSION INTEGER NOT NULL," + NL
243                      + " CREATION_TIMESTAMP TIMESTAMP NOT NULL," + NL
244                      + " MODIFICATION_TIMESTAMP TIMESTAMP NOT NULL," + NL
245                      + " CONSTRAINT " + getSchemaPrefix() + "SCH_PK PRIMARY KEY" +
246                            " (SCHEMA_NAME)" + NL
247                      + ")"))
248             {
249                // Close any results
250
stmQuery.getMoreResults(Statement.CLOSE_ALL_RESULTS);
251             }
252             s_logger.log(Level.FINEST, "Table " + SCHEMA_TABLE_NAME + " created.");
253             /*
254             if (stmQuery.execute("grant all on " + TABLE_NAME + " to " + strUserName))
255             {
256                // Close any results
257                stmQuery.getMoreResults(Statement.CLOSE_ALL_RESULTS);
258             }
259             Log.getLogger().log(Level.FINEST, "Access for table " + TABLE_NAME + " set.");
260             */

261          }
262          catch (SQLException JavaDoc sqleExc)
263          {
264             // Catch this just so we can log the message
265
s_logger.log(Level.WARNING, "Failed to create version schema.",
266                                 sqleExc);
267             throw sqleExc;
268          }
269          finally
270          {
271             DatabaseUtils.closeStatement(stmQuery);
272          }
273       }
274       finally
275       {
276          s_logger.exiting(this.getClass().getName(), "create");
277       }
278    }
279
280    // Helper methods ///////////////////////////////////////////////////////////
281

282    /**
283     * Create one schema and update the records.
284     *
285     * @param cntDBConnection - valid connection to database
286     * @param strUserName - name of user who will be accessing this table
287     * @param dsSchema - schema to create
288     * @param pstmUpdate - statement to use to update records if it is null
289     * it will be created and returned so it can be reused.
290     * Caller is then responsible for closing this statement.
291     * @return PreparedStatement
292     * @throws OSSException - problem creating the schema
293     * @throws SQLException - problem creating the schema
294     */

295    private PreparedStatement JavaDoc createSchema(
296       Connection JavaDoc cntDBConnection,
297       String JavaDoc strUserName,
298       DatabaseSchema dsSchema,
299       PreparedStatement JavaDoc pstmUpdate
300    ) throws OSSException,
301             SQLException JavaDoc
302    {
303       int iUpdateCount;
304
305       try
306       {
307
308          s_logger.log(Level.FINEST, "Creating new database schema "
309                              + dsSchema.getName() + " version " + dsSchema.getVersion()
310                              + ".");
311          dsSchema.create(cntDBConnection, strUserName);
312    
313          // Update the version schema tables with the latest version
314
// of just created schema
315
if (pstmUpdate == null)
316          {
317             // If this schema is created, we can prepare statement
318
pstmUpdate = cntDBConnection.prepareStatement(
319                   "insert into " + SCHEMA_TABLE_NAME + " (SCHEMA_NAME, SCHEMA_VERSION,"
320                   + " CREATION_TIMESTAMP, MODIFICATION_TIMESTAMP)"
321                   + " values (?, ?, "
322                   + DatabaseImpl.getInstance().getCurrentTimestampFunctionCall()
323                   + ", "
324                   + DatabaseImpl.getInstance().getCurrentTimestampFunctionCall()
325                   + ")");
326          }
327    
328          pstmUpdate.setString(1, dsSchema.getName());
329          pstmUpdate.setInt(2, dsSchema.getVersion());
330          iUpdateCount = pstmUpdate.executeUpdate();
331          if (iUpdateCount == 0)
332          {
333             String JavaDoc strMessage;
334             
335             strMessage = "Record for database schema " + dsSchema.getName()
336                          + " version " + dsSchema.getVersion()
337                          + " was not created.";
338             s_logger.log(Level.WARNING, strMessage);
339             throw new OSSDatabaseAccessException(strMessage);
340             
341          }
342          s_logger.log(Level.FINER, "Database schema " + dsSchema.getName()
343                              + " created.");
344
345          // Try to commit on the connection if the schema ws created successfully
346
// see Bug #1110485 for explanation what we are doing. We cannot user
347
// UserTransaction here since the transaction manager may not be even
348
// initialized and we don't know what kind of transaction this operation
349
// is performed in.
350

351          // At this point we don't know if this is just a single operation
352
// and we need to commit or if it is a part of bigger transaction
353
// and the commit is not desired until all operations proceed.
354
// Therefore let the DatabaseTransactionFactory resolve it
355
DatabaseTransactionFactoryImpl.getInstance().commitTransaction(cntDBConnection);
356       }
357       catch (OSSException ossExc)
358       {
359          // At this point we don't know if this is just a single operation
360
// and we need to commit or if it is a part of bigger transaction
361
// and the commit is not desired until all operations proceed.
362
// Therefore let the DatabaseTransactionFactory resolve it
363
DatabaseTransactionFactoryImpl.getInstance().rollbackTransaction(cntDBConnection);
364          // Just rethrow the original exception
365
throw ossExc;
366       }
367       catch (SQLException JavaDoc sqlExc)
368       {
369          // At this point we don't know if this is just a single operation
370
// and we need to commit or if it is a part of bigger transaction
371
// and the commit is not desired until all operations proceed.
372
// Therefore let the DatabaseTransactionFactory resolve it
373
DatabaseTransactionFactoryImpl.getInstance().rollbackTransaction(cntDBConnection);
374          // Just rethrow the original exception
375
throw sqlExc;
376       }
377       catch (Throwable JavaDoc thr)
378       {
379          // At this point we don't know if this is just a single operation
380
// and we need to commit or if it is a part of bigger transaction
381
// and the commit is not desired until all operations proceed.
382
// Therefore let the DatabaseTransactionFactory resolve it
383
DatabaseTransactionFactoryImpl.getInstance().rollbackTransaction(cntDBConnection);
384          throw new OSSDatabaseAccessException(thr);
385       }
386
387       return pstmUpdate;
388    }
389
390    /**
391     * Upgrade one schema and update its records.
392     *
393     * @param cntDBConnection - valid connection to database
394     * @param strUserName - name of user who will be accessing this table
395     * @param dsSchema - schema to upgrade
396     * @param iOriginalVersion - original version of the schema from which to upgrade
397     * @param pstmUpdate - statement to use to update records if it is null
398     * it will be created and returned so it can be reused.
399     * Caller is then responsible for closing this statement.
400     * @return PreparedStatement
401     * @throws OSSException - problem updating the schema
402     * @throws SQLException - problem updating the schema
403     */

404    private PreparedStatement JavaDoc upgradeSchema(
405       Connection JavaDoc cntDBConnection,
406       String JavaDoc strUserName,
407       DatabaseSchema dsSchema,
408       int iOriginalVersion,
409       PreparedStatement JavaDoc pstmUpdate
410    ) throws OSSException,
411             SQLException JavaDoc
412    {
413       int iUpdateCount;
414       
415       s_logger.log(Level.FINEST, "Upgrading new database schema "
416                           + dsSchema.getName() + " version " + dsSchema.getVersion()
417                           + ".");
418       dsSchema.upgrade(cntDBConnection, strUserName, iOriginalVersion);
419
420       // Update the version schema tables with the latest version
421
if (pstmUpdate == null)
422       {
423          // If this schema is updated, we can prepare statement
424
pstmUpdate = cntDBConnection.prepareStatement(
425                "update " + SCHEMA_TABLE_NAME + " set SCHEMA_VERSION = ?,"
426                + " MODIFICATION_TIMESTAMP = "
427                + DatabaseImpl.getInstance().getCurrentTimestampFunctionCall()
428                + " where SCHEMA_NAME = ? and SCHEMA_VERSION < ?");
429       }
430
431       pstmUpdate.setInt(1, dsSchema.getVersion());
432       pstmUpdate.setString(2, dsSchema.getName());
433       pstmUpdate.setInt(3, dsSchema.getVersion());
434       iUpdateCount = pstmUpdate.executeUpdate();
435       if (iUpdateCount == 0)
436       {
437          String JavaDoc strMessage;
438          
439          strMessage = "No record for database schema " + dsSchema.getName()
440                       + " version " + dsSchema.getVersion()
441                       + " was updated to new version.";
442          s_logger.log(Level.WARNING, strMessage);
443          throw new OSSDatabaseAccessException(strMessage);
444          
445       }
446       else if (iUpdateCount > 1)
447       {
448          String JavaDoc strMessage;
449          
450          strMessage = "Multiple (" + iUpdateCount + ") records for database schema "
451                       + dsSchema.getName() + " version " + dsSchema.getVersion()
452                       + " were updated to new version.";
453          s_logger.log(Level.WARNING, strMessage);
454          throw new OSSDatabaseAccessException(strMessage);
455          
456       }
457       s_logger.log(Level.FINER, "Database schema " + dsSchema.getName()
458                           + " upgraded.");
459
460       return pstmUpdate;
461    }
462
463    /**
464     * Check what schemas already exists in the repository.
465     *
466     * @param cntDBConnection - valid connection to database
467     * @param mpSchemasToAdd - current schemas will at the end contains schemas,
468     * which must be added
469     * @param mpSchemasToUpgrade - schemas to upgrade will contain schemas,
470     * which must be upgraded
471     * @throws OSSException - problem initializing the schema
472     */

473    protected void loadExistingSchemas(
474       Connection JavaDoc cntDBConnection,
475       Map JavaDoc mpSchemasToAdd,
476       Map JavaDoc mpSchemasToUpgrade
477    ) throws OSSException
478    {
479       // Try to load information about version of current schema
480
Statement JavaDoc stmQuery = null;
481       ResultSet JavaDoc rsQueryResults = null;
482       try
483       {
484          s_logger.fine("Verifying existing database schemas");
485
486          stmQuery = cntDBConnection.createStatement();
487          rsQueryResults = stmQuery.executeQuery(
488                "select SCHEMA_NAME, SCHEMA_VERSION from " + SCHEMA_TABLE_NAME);
489
490          String JavaDoc strSchemaName;
491          int iLastVersion;
492          DatabaseSchema dsSchema;
493          
494          while (rsQueryResults.next())
495          {
496             strSchemaName = rsQueryResults.getString(1);
497             // See if this schema exists, if it exists, remove it from the
498
// list and that way we will detect the schemas we need to create
499
dsSchema = (DatabaseSchema) mpSchemasToAdd.remove(strSchemaName);
500             if (dsSchema != null)
501             {
502                // This schema exists, compare versions
503
iLastVersion = rsQueryResults.getInt(2);
504                if (iLastVersion < dsSchema.getVersion())
505                {
506                   s_logger.finer("Database contains schema " + strSchemaName
507                                  + " version " + iLastVersion
508                                  + ", but the current schema has version "
509                                  + dsSchema.getVersion()
510                                  + ". It needs to be upgraded.");
511                   // Remember the last version
512
mpSchemasToUpgrade.put(dsSchema.getName(),
513                                          new Integer JavaDoc(iLastVersion));
514                }
515                else if (iLastVersion > dsSchema.getVersion())
516                {
517                   String JavaDoc strMessage;
518                   
519                   strMessage = "Database contains schema " + strSchemaName
520                                + " version " + iLastVersion
521                                + ", which is newer that the current schema version "
522                                + dsSchema.getVersion()
523                                + ". You should obtain newer version of the product.";
524                   
525                   s_logger.finer(strMessage);
526                   throw new OSSDatabaseAccessException(strMessage);
527                }
528                else
529                {
530                   s_logger.finer("Database schema " + strSchemaName
531                                  + " version " + iLastVersion + " is up to date.");
532                }
533             }
534             else
535             {
536                // There is schema in the database which doesn't exists anymore
537
// This is not an assert since the same database can be used
538
// by multiple products
539
s_logger.finer("Database contains schema " + strSchemaName
540                               + ", which doesn't exist anymore in current schema.");
541             }
542          }
543          
544          s_logger.fine("Existing database schemas verified: "
545                        + mpSchemasToAdd.size() + " schemas to add and "
546                        + mpSchemasToUpgrade.size() + " schemas to upgrade.");
547       }
548       catch (SQLException JavaDoc sqleExc)
549       {
550          s_logger.log(Level.FINER,
551                       "Failed to load version schema, trying to create one.",
552                       sqleExc);
553          // The code below will create all schemas in the mpSchemasToAdd
554
if (GlobalConstants.ERROR_CHECKING)
555          {
556             assert mpSchemasToUpgrade.isEmpty()
557                    : "There should be no schemas to upgrade.";
558             assert mpSchemasToAdd.size() == m_mpSchemas.size()
559                    : "All schemas should be set to be created.";
560          }
561       }
562       finally
563       {
564          DatabaseUtils.closeResultSetAndStatement(rsQueryResults, stmQuery);
565       }
566    }
567
568    /**
569     * Creating or upgrading schemas, which needs to be added or changes.
570     *
571     * @param cntDBConnection - valid connection to database
572     * @param strUserName - name of user who will be accessing this table
573     * @param mpSchemasToAdd - current schemas will at the end contains schemas which must be added
574     * @param mpSchemasToUpgrade - schemas to upgrade will contain schemas which must be upgraded
575     * @throws OSSException - problem initializing the schema
576     * @throws SQLException - problem initializing the schema
577     */

578    protected void createOrUpgradeSchemas(
579       Connection JavaDoc cntDBConnection,
580       String JavaDoc strUserName,
581       Map JavaDoc mpSchemasToAdd,
582       Map JavaDoc mpSchemasToUpgrade
583    ) throws OSSException,
584             SQLException JavaDoc
585    {
586       // Now see if there are some new schemas and schemas which needs to be
587
// upgraded. Since some of the new schemas may depend on some changes
588
// in the upgraded schemas or some changes in the upgraded schemas
589
// may depend on some new schemas, we sill need to create and upgrade
590
// them in the same order as they were specified
591
if ((!mpSchemasToAdd.isEmpty()) || (!mpSchemasToUpgrade.isEmpty()))
592       {
593          s_logger.fine("Adding new and upgraging existing database schemas");
594
595          Collection JavaDoc clSchemas;
596          Iterator JavaDoc itrSchemas;
597          DatabaseSchema dsSchema;
598          PreparedStatement JavaDoc pstmCreate = null;
599          PreparedStatement JavaDoc pstmUpdate = null;
600          Integer JavaDoc intLastVersion;
601
602          clSchemas = m_mpSchemas.values();
603          try
604          {
605             // First we need to create this schema if it needs to be created
606
// or update it if it needs to be updated
607
if (mpSchemasToAdd.get(getName()) != null)
608             {
609                pstmCreate = createSchema(cntDBConnection, strUserName,
610                                          this, pstmCreate);
611             }
612             else
613             {
614                intLastVersion = (Integer JavaDoc)mpSchemasToUpgrade.get(getName());
615                if (intLastVersion != null)
616                {
617                   pstmUpdate = upgradeSchema(cntDBConnection, strUserName,
618                                             this, intLastVersion.intValue(),
619                                             pstmUpdate);
620                }
621             }
622
623             // And now when versioned schema is up to date, we can create or update
624
// all other schemas, so just go through the list
625
for (itrSchemas = clSchemas.iterator(); itrSchemas.hasNext();)
626             {
627                dsSchema = (DatabaseSchema) itrSchemas.next();
628                if (dsSchema != this)
629                {
630                   if (mpSchemasToAdd.get(dsSchema.getName()) != null)
631                   {
632                      pstmCreate = createSchema(cntDBConnection, strUserName,
633                                                dsSchema, pstmCreate);
634                   }
635                   else
636                   {
637                      intLastVersion = (Integer JavaDoc)mpSchemasToUpgrade.get(dsSchema.getName());
638                      if (intLastVersion != null)
639                      {
640                         pstmUpdate = upgradeSchema(cntDBConnection, strUserName,
641                                                   dsSchema, intLastVersion.intValue(),
642                                                   pstmUpdate);
643                      }
644                   }
645                }
646             }
647             
648             s_logger.fine("Database schemas have been updated.");
649          }
650          finally
651          {
652             DatabaseUtils.closeStatement(pstmCreate);
653             DatabaseUtils.closeStatement(pstmUpdate);
654          }
655       }
656    }
657 }
658
Popular Tags