KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > cmp > jdbc > JDBCStartCommand


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software 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 GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb.plugins.cmp.jdbc;
23
24 import java.sql.Connection JavaDoc;
25 import java.sql.Statement JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Set JavaDoc;
32 import javax.sql.DataSource JavaDoc;
33 import javax.transaction.Transaction JavaDoc;
34 import javax.transaction.TransactionManager JavaDoc;
35
36 import org.jboss.deployment.DeploymentException;
37 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
38 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge;
39 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
40 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
41 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData;
42 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData;
43 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
44 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
45 import org.jboss.ejb.plugins.cmp.bridge.EntityBridge;
46 import org.jboss.logging.Logger;
47
48 /**
49  * JDBCStartCommand creates the table if specified in xml.
50  *
51  * @author <a HREF="mailto:dain@daingroup.com">Dain Sundstrom</a>
52  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
53  * @author <a HREF="mailto:marc.fleury@telkel.com">Marc Fleury</a>
54  * @author <a HREF="mailto:shevlandj@kpi.com.au">Joe Shevland</a>
55  * @author <a HREF="mailto:justin@j-m-f.demon.co.uk">Justin Forder</a>
56  * @author <a HREF="mailto:michel.anke@wolmail.nl">Michel de Groot</a>
57  * @author <a HREF="mailto:alex@jboss.org">Alex Loubyansky</a>
58  * @author <a HREF="mailto:heiko.rupp@cellent.de">Heiko W.Rupp</a>
59  * @author <a HREF="mailto:joachim@cabsoft.be">Joachim Van der Auwera</a>
60  * @version $Revision: 46026 $
61  */

62 public final class JDBCStartCommand
63 {
64    private static final String JavaDoc IDX_POSTFIX = "_idx";
65    private static final String JavaDoc COULDNT_SUSPEND = "Could not suspend current transaction before ";
66    private static final String JavaDoc COULDNT_REATTACH = "Could not reattach original transaction after ";
67    private final static Object JavaDoc CREATED_TABLES_KEY = new Object JavaDoc();
68    private final JDBCEntityPersistenceStore manager;
69    private final JDBCAbstractEntityBridge entity;
70    private final JDBCEntityMetaData entityMetaData;
71    private final Logger log;
72    private static int idxCount = 0;
73
74    public JDBCStartCommand(JDBCEntityPersistenceStore manager)
75    {
76       this.manager = manager;
77       entity = manager.getEntityBridge();
78       entityMetaData = manager.getMetaData();
79
80       // Create the Log
81
log = Logger.getLogger(this.getClass().getName() +
82          "." +
83          manager.getMetaData().getName());
84
85       // Create the created tables set
86
Set JavaDoc tables = (Set JavaDoc) manager.getApplicationData(CREATED_TABLES_KEY);
87       if(tables == null)
88       {
89          manager.putApplicationData(CREATED_TABLES_KEY, new HashSet JavaDoc());
90       }
91    }
92
93    public void execute() throws DeploymentException
94    {
95       Set JavaDoc existedTables = getExistedTables(manager);
96
97       boolean tableExisted = SQLUtil.tableExists(entity.getQualifiedTableName(), entity.getDataSource());
98       if(tableExisted)
99       {
100          existedTables.add(entity.getEntityName());
101       }
102
103       if(tableExisted)
104       {
105          if(entityMetaData.getAlterTable())
106          {
107             SQLUtil.OldColumns oldColumns = SQLUtil.getOldColumns(entity.getQualifiedTableName(), entity.getDataSource());
108             ArrayList JavaDoc oldNames = oldColumns.getColumnNames();
109             ArrayList JavaDoc oldTypes = oldColumns.getTypeNames();
110             ArrayList JavaDoc oldSizes = oldColumns.getColumnSizes();
111             SQLUtil.OldIndexes oldIndexes = null;
112             ArrayList JavaDoc newNames = new ArrayList JavaDoc();
113             JDBCFieldBridge fields[] = entity.getTableFields();
114             String JavaDoc tableName = entity.getQualifiedTableName();
115             for(int i = 0; i < fields.length; i++)
116             {
117                JDBCFieldBridge field = fields[i];
118                JDBCType jdbcType = field.getJDBCType();
119                String JavaDoc[] columnNames = jdbcType.getColumnNames();
120                String JavaDoc[] sqlTypes = jdbcType.getSQLTypes();
121                boolean[] notNull = jdbcType.getNotNull();
122
123                for(int j = 0; j < columnNames.length; j++)
124                {
125                   String JavaDoc name = columnNames[j];
126                   String JavaDoc ucName = name.toUpperCase();
127
128                   newNames.add( ucName );
129
130                   int oldIndex = oldNames.indexOf( ucName );
131                   if(oldIndex == -1)
132                   {
133                      // add new column
134
StringBuffer JavaDoc buf = new StringBuffer JavaDoc( sqlTypes[j] );
135                      if( notNull[j] )
136                      {
137                         buf.append(SQLUtil.NOT).append(SQLUtil.NULL);
138                      }
139                      alterTable(entity.getDataSource(),
140                            entityMetaData.getTypeMapping().getAddColumnTemplate(),
141                            tableName, name, buf.toString());
142                   }
143                   else
144                   {
145                      // alter existing columns
146
// only CHAR and VARCHAR fields are altered, and only when they are longer then before
147
String JavaDoc type = (String JavaDoc) oldTypes.get(oldIndex);
148                      if(type.equals("CHAR") || type.equals("VARCHAR"))
149                      {
150                         try
151                         {
152                            // get new length
153
String JavaDoc l = sqlTypes[j];
154                            l = l.substring(l.indexOf('(') + 1, l.length() - 1);
155                            Integer JavaDoc oldLength = (Integer JavaDoc) oldSizes.get(oldIndex);
156                            if(Integer.parseInt(l) > oldLength.intValue())
157                            {
158                               alterTable(entity.getDataSource(),
159                                     entityMetaData.getTypeMapping().getAlterColumnTemplate(),
160                                     tableName, name, sqlTypes[j] );
161                            }
162                         }
163                         catch(Exception JavaDoc e)
164                         {
165                            log.warn("EXCEPTION ALTER :" + e.toString());
166                         }
167                      }
168                   }
169                }
170
171                // see if we have to add an index for the field
172
JDBCCMPFieldMetaData fieldMD = entity.getMetaData().getCMPFieldByName(field.getFieldName());
173                if(fieldMD != null && fieldMD.isIndexed())
174                {
175                   if(oldIndexes == null)
176                   {
177                      oldIndexes = SQLUtil.getOldIndexes(entity.getQualifiedTableName(), entity.getDataSource());
178                      idxCount = oldIndexes.getIndexNames().size();
179                   }
180                   if(!hasIndex(oldIndexes, field))
181                   {
182                      createCMPIndex( entity.getDataSource(), field, oldIndexes.getIndexNames() );
183                   }
184
185                }
186             } // for int i;
187

188             // delete old columns
189
Iterator JavaDoc it = oldNames.iterator();
190             while(it.hasNext())
191             {
192                String JavaDoc name = (String JavaDoc) (it.next());
193                if(!newNames.contains(name))
194                {
195                   alterTable(entity.getDataSource(),
196                         entityMetaData.getTypeMapping().getDropColumnTemplate(),
197                         tableName, name, "");
198                }
199             }
200
201          }
202       }
203
204       // Create table if necessary
205
Set JavaDoc createdTables = getCreatedTables(manager);
206
207       if(entityMetaData.getCreateTable() && !createdTables.contains(entity.getEntityName()))
208       {
209          DataSource JavaDoc dataSource = entity.getDataSource();
210          createTable(dataSource, entity.getQualifiedTableName(), getEntityCreateTableSQL(dataSource));
211
212          // create indices only if table did not yet exist.
213
if(!tableExisted)
214          {
215             createCMPIndices( dataSource,
216                               SQLUtil.getOldIndexes( entity.getQualifiedTableName(),
217                                                      entity.getDataSource() ).getIndexNames() );
218          }
219          else
220          {
221             if(log.isDebugEnabled())
222             {
223                log.debug("Indices for table " + entity.getQualifiedTableName() + "not created as table existed");
224             }
225          }
226
227
228          // issue extra (user-defined) sql for table
229
if(!tableExisted)
230          {
231             issuePostCreateSQL(dataSource,
232                entity.getMetaData().getDefaultTablePostCreateCmd(),
233                entity.getQualifiedTableName());
234          }
235
236          createdTables.add(entity.getEntityName());
237       }
238       else
239       {
240          log.debug("Table not create as requested: " + entity.getQualifiedTableName());
241       }
242
243       // create relation tables
244
JDBCAbstractCMRFieldBridge[] cmrFields = entity.getCMRFields();
245       for(int i = 0; i < cmrFields.length; ++i)
246       {
247          JDBCAbstractCMRFieldBridge cmrField = cmrFields[i];
248          JDBCRelationMetaData relationMetaData = cmrField.getMetaData().getRelationMetaData();
249
250          // if the table for the related entity has been created
251
final EntityBridge relatedEntity = cmrField.getRelatedEntity();
252          if(relationMetaData.isTableMappingStyle() && createdTables.contains(relatedEntity.getEntityName()))
253          {
254             DataSource JavaDoc dataSource = relationMetaData.getDataSource();
255
256             boolean relTableExisted = SQLUtil.tableExists(cmrField.getQualifiedTableName(), entity.getDataSource());
257
258             if(relTableExisted)
259             {
260                if(relationMetaData.getAlterTable())
261                {
262                   ArrayList JavaDoc oldNames = SQLUtil.getOldColumns(cmrField.getQualifiedTableName(), dataSource).getColumnNames();
263                   ArrayList JavaDoc newNames = new ArrayList JavaDoc();
264                   JDBCFieldBridge[] leftKeys = cmrField.getTableKeyFields();
265                   JDBCFieldBridge[] rightKeys = cmrField.getRelatedCMRField().getTableKeyFields();
266                   JDBCFieldBridge[] fields = new JDBCFieldBridge[leftKeys.length + rightKeys.length];
267                   System.arraycopy(leftKeys, 0, fields, 0, leftKeys.length);
268                   System.arraycopy(rightKeys, 0, fields, leftKeys.length, rightKeys.length);
269                   // have to append field names to leftKeys, rightKeys...
270

271                   boolean different = false;
272                   for(int j = 0; j < fields.length; j++)
273                   {
274                      JDBCFieldBridge field = fields[j];
275
276                      String JavaDoc name = field.getJDBCType().getColumnNames()[0].toUpperCase();
277                      newNames.add(name);
278
279                      if(!oldNames.contains(name))
280                      {
281                         different = true;
282                         break;
283                      }
284                   } // for int j;
285

286                   if(!different)
287                   {
288                      Iterator JavaDoc it = oldNames.iterator();
289                      while(it.hasNext())
290                      {
291                         String JavaDoc name = (String JavaDoc) (it.next());
292                         if(!newNames.contains(name))
293                         {
294                            different = true;
295                            break;
296                         }
297                      }
298                   }
299
300                   if(different)
301                   {
302                      // only log, don't drop table is this can cause data loss
303
log.error("CMR table structure is incorrect for " + cmrField.getQualifiedTableName());
304                      //SQLUtil.dropTable(entity.getDataSource(), cmrField.getQualifiedTableName());
305
}
306
307                } // if alter-table
308

309             } // if existed
310

311             // create the relation table
312
if(relationMetaData.isTableMappingStyle() && !relationMetaData.isTableCreated())
313             {
314                if(relationMetaData.getCreateTable())
315                {
316                   createTable(dataSource, cmrField.getQualifiedTableName(),
317                      getRelationCreateTableSQL(cmrField, dataSource));
318                }
319                else
320                {
321                   log.debug("Relation table not created as requested: " + cmrField.getQualifiedTableName());
322                }
323                // create Indices if needed
324
createCMRIndex(dataSource, cmrField);
325
326                if(relationMetaData.getCreateTable())
327                {
328                   issuePostCreateSQL(dataSource,
329                      relationMetaData.getDefaultTablePostCreateCmd(),
330                      cmrField.getQualifiedTableName());
331                }
332             }
333          }
334       }
335    }
336
337    public void addForeignKeyConstraints() throws DeploymentException
338    {
339       // Create table if necessary
340
Set JavaDoc createdTables = getCreatedTables(manager);
341
342       JDBCAbstractCMRFieldBridge[] cmrFields = entity.getCMRFields();
343       for(int i = 0; i < cmrFields.length; ++i)
344       {
345          JDBCAbstractCMRFieldBridge cmrField = cmrFields[i];
346          JDBCRelationMetaData relationMetaData = cmrField.getMetaData().getRelationMetaData();
347
348          // if the table for the related entity has been created
349
final EntityBridge relatedEntity = cmrField.getRelatedEntity();
350
351          // Only generate indices on foreign key columns if
352
// the table was freshly created. If not, we risk
353
// creating an index twice and get an exception from the DB
354
if(relationMetaData.isForeignKeyMappingStyle() && createdTables.contains(relatedEntity.getEntityName()))
355          {
356             createCMRIndex(((JDBCAbstractEntityBridge)relatedEntity).getDataSource(), cmrField);
357          }
358
359          // Create my fk constraint
360
addForeignKeyConstraint(cmrField);
361       }
362    }
363
364    public static Set JavaDoc getCreatedTables(JDBCEntityPersistenceStore manager)
365    {
366       final String JavaDoc key = "CREATED_TABLES";
367       Set JavaDoc createdTables = (Set JavaDoc) manager.getApplicationData(key);
368       if(createdTables == null)
369       {
370          createdTables = new HashSet JavaDoc();
371          manager.putApplicationData(key, createdTables);
372       }
373       return createdTables;
374    }
375
376    public static Set JavaDoc getExistedTables(JDBCEntityPersistenceStore manager)
377    {
378       final String JavaDoc key = "EXISTED_TABLES";
379       Set JavaDoc existedTables = (Set JavaDoc) manager.getApplicationData(key);
380       if(existedTables == null)
381       {
382          existedTables = new HashSet JavaDoc();
383          manager.putApplicationData(key, existedTables);
384       }
385       return existedTables;
386    }
387
388    /**
389     * Check whether a required index already exists on a table
390     *
391     * @param oldIndexes list of existing indexes
392     * @param field field we test the existence of an index for
393     * @return True if the field has an index; otherwise false
394     */

395    private boolean hasIndex(SQLUtil.OldIndexes oldIndexes, JDBCFieldBridge field)
396    {
397       JDBCType jdbcType = field.getJDBCType();
398       String JavaDoc[] columns = jdbcType.getColumnNames();
399       ArrayList JavaDoc idxNames = oldIndexes.getIndexNames();
400       ArrayList JavaDoc idxColumns = oldIndexes.getColumnNames();
401
402       // check if the columns are in the same index
403
String JavaDoc indexName = null;
404       for(int i = 0; i < columns.length; ++i)
405       {
406          String JavaDoc column = columns[i];
407          int index = columnIndex(idxColumns, column);
408          if(index == -1)
409          {
410             return false;
411          }
412
413          if(indexName == null)
414          {
415             indexName = (String JavaDoc)idxNames.get(index);
416          }
417          else if(!indexName.equals(idxNames.get(index)))
418          {
419             return false;
420          }
421       }
422
423       return true;
424 /*
425 previous implementation
426       ArrayList idxAscDesc = oldIndexes.getColumnAscDesc();
427
428       // search for for column in index
429       if(idxAscDesc != null)
430       {
431          for(int i = 0; i < idxColumns.size(); i++)
432          {
433             // only match ascending columns
434             if("A".equals(idxAscDesc.get(i)))
435             {
436                String name = columns[0];
437                String testCol = (String) idxColumns.get(i);
438                if(testCol.charAt(0) == '\"' &&
439                   testCol.charAt(testCol.charAt(testCol.length() - 1)) == '\"')
440                {
441                   testCol = testCol.substring(1, testCol.length() - 1);
442                }
443
444                if(testCol.equalsIgnoreCase(name))
445                {
446                   // first column matches, now check the others
447                   String idxName = (String) idxNames.get(i);
448                   int j = 1;
449                   for(; j < columns.length; j++)
450                   {
451                      name = columns[j];
452                      testCol = (String) idxColumns.get(i + j);
453                      if(testCol.charAt(0) == '\"' &&
454                         testCol.charAt(testCol.charAt(testCol.length() - 1)) == '\"')
455                      {
456                         testCol = testCol.substring(1, testCol.length() - 1);
457                      }
458
459                      String testName = (String) idxNames.get(i + j);
460                      if(!(testName.equals(idxName)
461                         &&
462                         testCol.equalsIgnoreCase(name)
463                         && idxAscDesc.get(i + j).equals("A")))
464                      {
465                         break;
466                      }
467                   }
468                   // if they all matched -> found
469                   if(j == columns.length) return true;
470                }
471             }
472          }
473       }
474       return false;
475 */

476    }
477
478    private int columnIndex(ArrayList JavaDoc idxColumns, String JavaDoc column)
479    {
480       for(int j = 0; j < idxColumns.size(); ++j)
481       {
482          String JavaDoc idxColumn = (String JavaDoc)idxColumns.get(j);
483          if(idxColumn.charAt(0) == '\"' &&
484             idxColumn.charAt(idxColumn.charAt(idxColumn.length() - 1)) == '\"')
485          {
486             idxColumn = idxColumn.substring(1, idxColumn.length() - 1);
487          }
488
489          if(idxColumn.equalsIgnoreCase(column))
490          {
491             return j;
492          }
493       }
494       return -1;
495    }
496
497    private void alterTable(DataSource JavaDoc dataSource, JDBCFunctionMappingMetaData mapping, String JavaDoc tableName, String JavaDoc fieldName, String JavaDoc fieldStructure)
498       throws DeploymentException
499    {
500       StringBuffer JavaDoc sqlBuf = new StringBuffer JavaDoc();
501       mapping.getFunctionSql( new String JavaDoc[]{tableName, fieldName, fieldStructure}, sqlBuf );
502       String JavaDoc sql = sqlBuf.toString();
503
504       log.warn( sql );
505
506       // suspend the current transaction
507
TransactionManager JavaDoc tm = manager.getContainer().getTransactionManager();
508       Transaction JavaDoc oldTransaction;
509       try
510       {
511          oldTransaction = tm.suspend();
512       }
513       catch(Exception JavaDoc e)
514       {
515          throw new DeploymentException(COULDNT_SUSPEND + " alter table.", e);
516       }
517
518       try
519       {
520          Connection JavaDoc con = null;
521          Statement JavaDoc statement = null;
522          try
523          {
524             con = dataSource.getConnection();
525             statement = con.createStatement();
526             statement.executeUpdate(sql);
527          }
528          finally
529          {
530             // make sure to close the connection and statement before
531
// comitting the transaction or XA will break
532
JDBCUtil.safeClose(statement);
533             JDBCUtil.safeClose(con);
534          }
535       }
536       catch(Exception JavaDoc e)
537       {
538          log.error("Could not alter table " + tableName + ": " + e.getMessage());
539          throw new DeploymentException("Error while alter table " + tableName + " " + sql, e);
540       }
541       finally
542       {
543          try
544          {
545             // resume the old transaction
546
if(oldTransaction != null)
547             {
548                tm.resume(oldTransaction);
549             }
550          }
551          catch(Exception JavaDoc e)
552          {
553             throw new DeploymentException(COULDNT_REATTACH + "alter table");
554          }
555       }
556
557       // success
558
if ( log.isDebugEnabled() )
559          log.debug("Table altered successfully.");
560    }
561
562    private void createTable(DataSource JavaDoc dataSource, String JavaDoc tableName, String JavaDoc sql)
563       throws DeploymentException
564    {
565       // does this table already exist
566
if(SQLUtil.tableExists(tableName, dataSource))
567       {
568          log.debug("Table '" + tableName + "' already exists");
569          return;
570       }
571
572       // since we use the pools, we have to do this within a transaction
573

574       // suspend the current transaction
575
TransactionManager JavaDoc tm = manager.getContainer().getTransactionManager();
576       Transaction JavaDoc oldTransaction;
577       try
578       {
579          oldTransaction = tm.suspend();
580       }
581       catch(Exception JavaDoc e)
582       {
583          throw new DeploymentException(COULDNT_SUSPEND + "creating table.", e);
584       }
585
586       try
587       {
588          Connection JavaDoc con = null;
589          Statement JavaDoc statement = null;
590          try
591          {
592             // execute sql
593
if(log.isDebugEnabled())
594             {
595                log.debug("Executing SQL: " + sql);
596             }
597
598             con = dataSource.getConnection();
599             statement = con.createStatement();
600             statement.executeUpdate(sql);
601          }
602          finally
603          {
604             // make sure to close the connection and statement before
605
// comitting the transaction or XA will break
606
JDBCUtil.safeClose(statement);
607             JDBCUtil.safeClose(con);
608          }
609       }
610       catch(Exception JavaDoc e)
611       {
612          log.debug("Could not create table " + tableName);
613          throw new DeploymentException("Error while creating table " + tableName, e);
614       }
615       finally
616       {
617          try
618          {
619             // resume the old transaction
620
if(oldTransaction != null)
621             {
622                tm.resume(oldTransaction);
623             }
624          }
625          catch(Exception JavaDoc e)
626          {
627             throw new DeploymentException(COULDNT_REATTACH + "create table");
628          }
629       }
630
631       // success
632
Set JavaDoc createdTables = (Set JavaDoc) manager.getApplicationData(CREATED_TABLES_KEY);
633       createdTables.add(tableName);
634    }
635
636    /**
637     * Create an index on a field. Does the create
638     *
639     * @param dataSource
640     * @param tableName In which table is the index?
641     * @param indexName Which is the index?
642     * @param sql The SQL statement to issue
643     * @throws DeploymentException
644     */

645    private void createIndex(DataSource JavaDoc dataSource, String JavaDoc tableName, String JavaDoc indexName, String JavaDoc sql)
646       throws DeploymentException
647    {
648       // we are only called directly after creating a table
649
// since we use the pools, we have to do this within a transaction
650
// suspend the current transaction
651
TransactionManager JavaDoc tm = manager.getContainer().getTransactionManager();
652       Transaction JavaDoc oldTransaction;
653       try
654       {
655          oldTransaction = tm.suspend();
656       }
657       catch(Exception JavaDoc e)
658       {
659          throw new DeploymentException(COULDNT_SUSPEND + "creating index.", e);
660       }
661
662       try
663       {
664          Connection JavaDoc con = null;
665          Statement JavaDoc statement = null;
666          try
667          {
668             // execute sql
669
if(log.isDebugEnabled())
670             {
671                log.debug("Executing SQL: " + sql);
672             }
673             con = dataSource.getConnection();
674             statement = con.createStatement();
675             statement.executeUpdate(sql);
676          }
677          finally
678          {
679             // make sure to close the connection and statement before
680
// comitting the transaction or XA will break
681
JDBCUtil.safeClose(statement);
682             JDBCUtil.safeClose(con);
683          }
684       }
685       catch(Exception JavaDoc e)
686       {
687          log.debug("Could not create index " + indexName + "on table" + tableName);
688          throw new DeploymentException("Error while creating table", e);
689       }
690       finally
691       {
692          try
693          {
694             // resume the old transaction
695
if(oldTransaction != null)
696             {
697                tm.resume(oldTransaction);
698             }
699          }
700          catch(Exception JavaDoc e)
701          {
702             throw new DeploymentException(COULDNT_REATTACH + "create index");
703          }
704       }
705    }
706
707
708    /**
709     * Send (user-defined) SQL commands to the server.
710     * The commands can be found in the &lt;sql-statement&gt; elements
711     * within the &lt;post-table-create&gt; tag in jbossjdbc-cmp.xml
712     *
713     * @param dataSource
714     */

715    private void issuePostCreateSQL(DataSource JavaDoc dataSource, List JavaDoc sql, String JavaDoc table)
716       throws DeploymentException
717    {
718       if(sql == null)
719       { // no work to do.
720
log.trace("issuePostCreateSQL: sql is null");
721          return;
722       }
723
724       log.debug("issuePostCreateSQL::sql: " + sql.toString() + " on table " + table);
725
726       TransactionManager JavaDoc tm = manager.getContainer().getTransactionManager();
727       Transaction JavaDoc oldTransaction;
728
729       try
730       {
731          oldTransaction = tm.suspend();
732       }
733       catch(Exception JavaDoc e)
734       {
735          throw new DeploymentException(COULDNT_SUSPEND + "sending sql command.", e);
736       }
737
738       String JavaDoc currentCmd = "";
739
740       try
741       {
742          Connection JavaDoc con = null;
743          Statement JavaDoc statement = null;
744          try
745          {
746             con = dataSource.getConnection();
747             statement = con.createStatement();
748
749             // execute sql
750
for(int i = 0; i < sql.size(); i++)
751             {
752                currentCmd = (String JavaDoc) sql.get(i);
753                /*
754                 * Replace %%t in the sql command with the current table name
755                 */

756                currentCmd = replaceTable(currentCmd, table);
757                currentCmd = replaceIndexCounter(currentCmd);
758                log.debug("Executing SQL: " + currentCmd);
759                statement.executeUpdate(currentCmd);
760             }
761          }
762          finally
763          {
764             // make sure to close the connection and statement before
765
// comitting the transaction or XA will break
766
JDBCUtil.safeClose(statement);
767             JDBCUtil.safeClose(con);
768          }
769       }
770       catch(Exception JavaDoc e)
771       {
772          log.warn("Issuing sql " + currentCmd + " failed: " + e.toString());
773          throw new DeploymentException("Error while issuing sql in post-table-create", e);
774       }
775       finally
776       {
777          try
778          {
779             // resume the old transaction
780
if(oldTransaction != null)
781             {
782                tm.resume(oldTransaction);
783             }
784          }
785          catch(Exception JavaDoc e)
786          {
787             throw new DeploymentException(COULDNT_REATTACH + "create index");
788          }
789       }
790
791       // success
792
log.debug("Issued SQL " + sql + " successfully.");
793    }
794
795    private String JavaDoc getEntityCreateTableSQL(DataSource JavaDoc dataSource)
796       throws DeploymentException
797    {
798       StringBuffer JavaDoc sql = new StringBuffer JavaDoc();
799       sql.append(SQLUtil.CREATE_TABLE).append(entity.getQualifiedTableName()).append(" (");
800
801       // add fields
802
boolean comma = false;
803       JDBCFieldBridge[] fields = entity.getTableFields();
804       for(int i = 0; i < fields.length; ++i)
805       {
806          JDBCFieldBridge field = fields[i];
807          JDBCType type = field.getJDBCType();
808          if(comma)
809          {
810             sql.append(SQLUtil.COMMA);
811          }
812          else
813          {
814             comma = true;
815          }
816          addField(type, sql);
817       }
818
819       // add a pk constraint
820
if(entityMetaData.hasPrimaryKeyConstraint())
821       {
822          JDBCFunctionMappingMetaData pkConstraint = manager.getMetaData().getTypeMapping().getPkConstraintTemplate();
823          if(pkConstraint == null)
824          {
825             throw new IllegalStateException JavaDoc("Primary key constraint is " +
826                "not allowed for this type of data source");
827          }
828
829          String JavaDoc defTableName = entity.getManager().getMetaData().getDefaultTableName();
830          String JavaDoc name = "pk_" + SQLUtil.unquote(defTableName, dataSource);
831          name = SQLUtil.fixConstraintName(name, dataSource);
832          String JavaDoc[] args = new String JavaDoc[]{
833             name,
834             SQLUtil.getColumnNamesClause(entity.getPrimaryKeyFields(), new StringBuffer JavaDoc(100)).toString()
835          };
836          sql.append(SQLUtil.COMMA);
837          pkConstraint.getFunctionSql(args, sql);
838       }
839
840       return sql.append(')').toString();
841    }
842
843    /**
844     * Create indices for the fields in the table that have a
845     * &lt;dbindex&gt; tag in jbosscmp-jdbc.xml
846     *
847     * @param dataSource
848     * @throws DeploymentException
849     */

850    private void createCMPIndices(DataSource JavaDoc dataSource, ArrayList JavaDoc indexNames)
851       throws DeploymentException
852    {
853       // Only create indices on CMP fields
854
JDBCFieldBridge[] cmpFields = entity.getTableFields();
855       for(int i = 0; i < cmpFields.length; ++i)
856       {
857          JDBCFieldBridge field = cmpFields[i];
858          JDBCCMPFieldMetaData fieldMD = entity.getMetaData().getCMPFieldByName(field.getFieldName());
859
860          if(fieldMD != null && fieldMD.isIndexed())
861          {
862             createCMPIndex(dataSource, field, indexNames);
863          }
864       }
865
866       final JDBCAbstractCMRFieldBridge[] cmrFields = entity.getCMRFields();
867       if(cmrFields != null)
868       {
869          for(int i = 0; i < cmrFields.length; ++i)
870          {
871             JDBCAbstractCMRFieldBridge cmrField = cmrFields[i];
872             if(cmrField.getRelatedCMRField().getMetaData().isIndexed())
873             {
874                final JDBCFieldBridge[] fkFields = cmrField.getForeignKeyFields();
875                if(fkFields != null)
876                {
877                   for(int fkInd = 0; fkInd < fkFields.length; ++fkInd)
878                   {
879                      createCMPIndex(dataSource, fkFields[fkInd], indexNames);
880                   }
881                }
882             }
883          }
884       }
885    }
886
887    /**
888     * Create indix for one specific field
889     *
890     * @param dataSource
891     * @param field to create index for
892     * @throws DeploymentException
893     */

894    private void createCMPIndex(DataSource JavaDoc dataSource, JDBCFieldBridge field, ArrayList JavaDoc indexNames)
895       throws DeploymentException
896    {
897       StringBuffer JavaDoc sql;
898       log.debug("Creating index for field " + field.getFieldName());
899       sql = new StringBuffer JavaDoc();
900       sql.append(SQLUtil.CREATE_INDEX);
901       String JavaDoc indexName;
902       boolean indexExists;
903       do
904       {
905          indexName = entity.getQualifiedTableName() + IDX_POSTFIX + idxCount;
906          idxCount++;
907          indexExists = false;
908          if ( indexNames != null )
909          {
910             for ( int i = 0 ; i < indexNames.size() && !indexExists ; i++ )
911             {
912                indexExists = indexName.equalsIgnoreCase( ( (String JavaDoc) indexNames.get( i ) ) );
913             }
914          }
915       }
916       while ( indexExists );
917
918       sql.append( indexName );
919       sql.append(SQLUtil.ON);
920       sql.append(entity.getQualifiedTableName() + " (");
921       SQLUtil.getColumnNamesClause(field, sql);
922       sql.append(")");
923
924       createIndex(dataSource, entity.getQualifiedTableName(), indexName, sql.toString());
925    }
926
927    private void createCMRIndex(DataSource JavaDoc dataSource, JDBCAbstractCMRFieldBridge field)
928       throws DeploymentException
929    {
930       JDBCRelationMetaData rmd;
931       String JavaDoc tableName;
932
933       rmd = field.getMetaData().getRelationMetaData();
934
935       if(rmd.isTableMappingStyle())
936       {
937          tableName = rmd.getDefaultTableName();
938          createFKIndex(rmd.getLeftRelationshipRole(), dataSource, tableName);
939          createFKIndex(rmd.getRightRelationshipRole(), dataSource, tableName);
940       }
941       else if(field.hasForeignKey())
942       {
943          tableName = field.getEntity().getQualifiedTableName();
944          createFKIndex(field.getRelatedCMRField().getMetaData(), dataSource, tableName);
945       }
946    }
947
948    private void createFKIndex(JDBCRelationshipRoleMetaData metadata, DataSource JavaDoc dataSource, String JavaDoc tableName)
949       throws DeploymentException
950    {
951       Collection JavaDoc kfl = metadata.getKeyFields();
952       Iterator JavaDoc it = kfl.iterator();
953       while(it.hasNext())
954       {
955          JDBCCMPFieldMetaData fi = (JDBCCMPFieldMetaData) it.next();
956          if(metadata.isIndexed())
957          {
958             createIndex(dataSource, tableName, fi.getFieldName(), createIndexSQL(fi, tableName));
959             idxCount++;
960          }
961       }
962    }
963
964    private static String JavaDoc createIndexSQL(JDBCCMPFieldMetaData fi, String JavaDoc tableName)
965    {
966       StringBuffer JavaDoc sql = new StringBuffer JavaDoc();
967       sql.append(SQLUtil.CREATE_INDEX);
968       sql.append(fi.getColumnName() + IDX_POSTFIX + idxCount);
969       sql.append(SQLUtil.ON);
970       sql.append(tableName + " (");
971       sql.append(fi.getColumnName());
972       sql.append(')');
973       return sql.toString();
974    }
975
976    private void addField(JDBCType type, StringBuffer JavaDoc sqlBuffer) throws DeploymentException
977    {
978       // apply auto-increment template
979
if(type.getAutoIncrement()[0])
980       {
981          String JavaDoc columnClause = SQLUtil.getCreateTableColumnsClause(type);
982          JDBCFunctionMappingMetaData autoIncrement =
983             manager.getMetaData().getTypeMapping().getAutoIncrementTemplate();
984          if(autoIncrement == null)
985          {
986             throw new IllegalStateException JavaDoc("auto-increment template not found");
987          }
988          String JavaDoc[] args = new String JavaDoc[]{columnClause};
989          autoIncrement.getFunctionSql(args, sqlBuffer);
990       }
991       else
992       {
993          sqlBuffer.append(SQLUtil.getCreateTableColumnsClause(type));
994       }
995    }
996
997    private String JavaDoc getRelationCreateTableSQL(JDBCAbstractCMRFieldBridge cmrField,
998                                             DataSource JavaDoc dataSource)
999       throws DeploymentException
1000   {
1001      JDBCFieldBridge[] leftKeys = cmrField.getTableKeyFields();
1002      JDBCFieldBridge[] rightKeys = cmrField.getRelatedCMRField().getTableKeyFields();
1003      JDBCFieldBridge[] fieldsArr = new JDBCFieldBridge[leftKeys.length + rightKeys.length];
1004      System.arraycopy(leftKeys, 0, fieldsArr, 0, leftKeys.length);
1005      System.arraycopy(rightKeys, 0, fieldsArr, leftKeys.length, rightKeys.length);
1006
1007      StringBuffer JavaDoc sql = new StringBuffer JavaDoc();
1008      sql.append(SQLUtil.CREATE_TABLE).append(cmrField.getQualifiedTableName())
1009         .append(" (")
1010         // add field declaration
1011
.append(SQLUtil.getCreateTableColumnsClause(fieldsArr));
1012
1013      // add a pk constraint
1014
final JDBCRelationMetaData relationMetaData = cmrField.getMetaData().getRelationMetaData();
1015      if(relationMetaData.hasPrimaryKeyConstraint())
1016      {
1017         JDBCFunctionMappingMetaData pkConstraint =
1018            manager.getMetaData().getTypeMapping().getPkConstraintTemplate();
1019         if(pkConstraint == null)
1020         {
1021            throw new IllegalStateException JavaDoc("Primary key constraint is not allowed for this type of data store");
1022         }
1023
1024         String JavaDoc name = "pk_" + relationMetaData.getDefaultTableName();
1025         name = SQLUtil.fixConstraintName(name, dataSource);
1026         String JavaDoc[] args = new String JavaDoc[]{
1027            name,
1028            SQLUtil.getColumnNamesClause(fieldsArr, new StringBuffer JavaDoc(100).toString(), new StringBuffer JavaDoc()).toString()
1029         };
1030         sql.append(SQLUtil.COMMA);
1031         pkConstraint.getFunctionSql(args, sql);
1032      }
1033      sql.append(')');
1034      return sql.toString();
1035   }
1036
1037   private void addForeignKeyConstraint(JDBCAbstractCMRFieldBridge cmrField)
1038      throws DeploymentException
1039   {
1040      JDBCRelationshipRoleMetaData metaData = cmrField.getMetaData();
1041      if(metaData.hasForeignKeyConstraint())
1042      {
1043         if(metaData.getRelationMetaData().isTableMappingStyle())
1044         {
1045            addForeignKeyConstraint(metaData.getRelationMetaData().getDataSource(),
1046               cmrField.getQualifiedTableName(),
1047               cmrField.getFieldName(),
1048               cmrField.getTableKeyFields(),
1049               cmrField.getEntity().getQualifiedTableName(),
1050               cmrField.getEntity().getPrimaryKeyFields());
1051
1052         }
1053         else if(cmrField.hasForeignKey())
1054         {
1055            JDBCAbstractEntityBridge relatedEntity = (JDBCAbstractEntityBridge) cmrField.getRelatedEntity();
1056            addForeignKeyConstraint(cmrField.getEntity().getDataSource(),
1057               cmrField.getEntity().getQualifiedTableName(),
1058               cmrField.getFieldName(),
1059               cmrField.getForeignKeyFields(),
1060               relatedEntity.getQualifiedTableName(),
1061               relatedEntity.getPrimaryKeyFields());
1062         }
1063      }
1064      else
1065      {
1066         log.debug("Foreign key constraint not added as requested: relationshipRolename=" + metaData.getRelationshipRoleName());
1067      }
1068   }
1069
1070   private void addForeignKeyConstraint(DataSource JavaDoc dataSource,
1071                                        String JavaDoc tableName,
1072                                        String JavaDoc cmrFieldName,
1073                                        JDBCFieldBridge[] fields,
1074                                        String JavaDoc referencesTableName,
1075                                        JDBCFieldBridge[] referencesFields) throws DeploymentException
1076   {
1077      // can only alter tables we created
1078
Set JavaDoc createdTables = (Set JavaDoc) manager.getApplicationData(CREATED_TABLES_KEY);
1079      if(!createdTables.contains(tableName))
1080      {
1081         return;
1082      }
1083
1084      JDBCFunctionMappingMetaData fkConstraint = manager.getMetaData().getTypeMapping().getFkConstraintTemplate();
1085      if(fkConstraint == null)
1086      {
1087         throw new IllegalStateException JavaDoc("Foreign key constraint is not allowed for this type of datastore");
1088      }
1089      String JavaDoc a = SQLUtil.getColumnNamesClause(fields, new StringBuffer JavaDoc(50)).toString();
1090      String JavaDoc b = SQLUtil.getColumnNamesClause(referencesFields, new StringBuffer JavaDoc(50)).toString();
1091
1092      String JavaDoc[] args = new String JavaDoc[]{
1093         tableName,
1094         SQLUtil.fixConstraintName("fk_" + tableName + "_" + cmrFieldName, dataSource),
1095         a,
1096         referencesTableName,
1097         b};
1098
1099      String JavaDoc sql = fkConstraint.getFunctionSql(args, new StringBuffer JavaDoc(100)).toString();
1100
1101      // since we use the pools, we have to do this within a transaction
1102
// suspend the current transaction
1103
TransactionManager JavaDoc tm = manager.getContainer().getTransactionManager();
1104      Transaction JavaDoc oldTransaction;
1105      try
1106      {
1107         oldTransaction = tm.suspend();
1108      }
1109      catch(Exception JavaDoc e)
1110      {
1111         throw new DeploymentException(COULDNT_SUSPEND + "alter table create foreign key.", e);
1112      }
1113
1114      try
1115      {
1116         Connection JavaDoc con = null;
1117         Statement JavaDoc statement = null;
1118         try
1119         {
1120            if(log.isDebugEnabled())
1121            {
1122               log.debug("Executing SQL: " + sql);
1123            }
1124            con = dataSource.getConnection();
1125            statement = con.createStatement();
1126            statement.executeUpdate(sql);
1127         }
1128         finally
1129         {
1130            // make sure to close the connection and statement before
1131
// comitting the transaction or XA will break
1132
JDBCUtil.safeClose(statement);
1133            JDBCUtil.safeClose(con);
1134         }
1135      }
1136      catch(Exception JavaDoc e)
1137      {
1138         log.warn("Could not add foreign key constraint: table=" + tableName);
1139         throw new DeploymentException("Error while adding foreign key constraint", e);
1140      }
1141      finally
1142      {
1143         try
1144         {
1145            // resume the old transaction
1146
if(oldTransaction != null)
1147            {
1148               tm.resume(oldTransaction);
1149            }
1150         }
1151         catch(Exception JavaDoc e)
1152         {
1153            throw new DeploymentException(COULDNT_REATTACH + "create table");
1154         }
1155      }
1156   }
1157
1158
1159   /**
1160    * Replace %%t in the sql command with the current table name
1161    *
1162    * @param in sql statement with possible %%t to substitute with table name
1163    * @param table the table name
1164    * @return String with sql statement
1165    */

1166   private static String JavaDoc replaceTable(String JavaDoc in, String JavaDoc table)
1167   {
1168      int pos;
1169
1170      pos = in.indexOf("%%t");
1171      // No %%t -> return input
1172
if(pos == -1)
1173      {
1174         return in;
1175      }
1176
1177      String JavaDoc first = in.substring(0, pos);
1178      String JavaDoc last = in.substring(pos + 3);
1179
1180      return first + table + last;
1181   }
1182
1183   /**
1184    * Replace %%n in the sql command with a running (index) number
1185    *
1186    * @param in
1187    * @return
1188    */

1189   private static String JavaDoc replaceIndexCounter(String JavaDoc in)
1190   {
1191      int pos;
1192
1193      pos = in.indexOf("%%n");
1194      // No %%n -> return input
1195
if(pos == -1)
1196      {
1197         return in;
1198      }
1199
1200      String JavaDoc first = in.substring(0, pos);
1201      String JavaDoc last = in.substring(pos + 3);
1202      idxCount++;
1203      return first + idxCount + last;
1204   }
1205}
1206
Popular Tags