| 1 22 package org.jboss.ejb.plugins.cmp.jdbc; 23 24 import java.sql.Connection ; 25 import java.sql.Statement ; 26 import java.util.Collection ; 27 import java.util.Iterator ; 28 import java.util.ArrayList ; 29 import java.util.HashSet ; 30 import java.util.List ; 31 import java.util.Set ; 32 import javax.sql.DataSource ; 33 import javax.transaction.Transaction ; 34 import javax.transaction.TransactionManager ; 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 62 public final class JDBCStartCommand 63 { 64 private static final String IDX_POSTFIX = "_idx"; 65 private static final String COULDNT_SUSPEND = "Could not suspend current transaction before "; 66 private static final String COULDNT_REATTACH = "Could not reattach original transaction after "; 67 private final static Object CREATED_TABLES_KEY = new Object (); 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 log = Logger.getLogger(this.getClass().getName() + 82 "." + 83 manager.getMetaData().getName()); 84 85 Set tables = (Set ) manager.getApplicationData(CREATED_TABLES_KEY); 87 if(tables == null) 88 { 89 manager.putApplicationData(CREATED_TABLES_KEY, new HashSet ()); 90 } 91 } 92 93 public void execute() throws DeploymentException 94 { 95 Set 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 oldNames = oldColumns.getColumnNames(); 109 ArrayList oldTypes = oldColumns.getTypeNames(); 110 ArrayList oldSizes = oldColumns.getColumnSizes(); 111 SQLUtil.OldIndexes oldIndexes = null; 112 ArrayList newNames = new ArrayList (); 113 JDBCFieldBridge fields[] = entity.getTableFields(); 114 String 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 [] columnNames = jdbcType.getColumnNames(); 120 String [] sqlTypes = jdbcType.getSQLTypes(); 121 boolean[] notNull = jdbcType.getNotNull(); 122 123 for(int j = 0; j < columnNames.length; j++) 124 { 125 String name = columnNames[j]; 126 String ucName = name.toUpperCase(); 127 128 newNames.add( ucName ); 129 130 int oldIndex = oldNames.indexOf( ucName ); 131 if(oldIndex == -1) 132 { 133 StringBuffer buf = new StringBuffer ( 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 String type = (String ) oldTypes.get(oldIndex); 148 if(type.equals("CHAR") || type.equals("VARCHAR")) 149 { 150 try 151 { 152 String l = sqlTypes[j]; 154 l = l.substring(l.indexOf('(') + 1, l.length() - 1); 155 Integer oldLength = (Integer ) 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 e) 164 { 165 log.warn("EXCEPTION ALTER :" + e.toString()); 166 } 167 } 168 } 169 } 170 171 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 } 188 Iterator it = oldNames.iterator(); 190 while(it.hasNext()) 191 { 192 String name = (String ) (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 Set createdTables = getCreatedTables(manager); 206 207 if(entityMetaData.getCreateTable() && !createdTables.contains(entity.getEntityName())) 208 { 209 DataSource dataSource = entity.getDataSource(); 210 createTable(dataSource, entity.getQualifiedTableName(), getEntityCreateTableSQL(dataSource)); 211 212 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 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 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 final EntityBridge relatedEntity = cmrField.getRelatedEntity(); 252 if(relationMetaData.isTableMappingStyle() && createdTables.contains(relatedEntity.getEntityName())) 253 { 254 DataSource 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 oldNames = SQLUtil.getOldColumns(cmrField.getQualifiedTableName(), dataSource).getColumnNames(); 263 ArrayList newNames = new ArrayList (); 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 271 boolean different = false; 272 for(int j = 0; j < fields.length; j++) 273 { 274 JDBCFieldBridge field = fields[j]; 275 276 String name = field.getJDBCType().getColumnNames()[0].toUpperCase(); 277 newNames.add(name); 278 279 if(!oldNames.contains(name)) 280 { 281 different = true; 282 break; 283 } 284 } 286 if(!different) 287 { 288 Iterator it = oldNames.iterator(); 289 while(it.hasNext()) 290 { 291 String name = (String ) (it.next()); 292 if(!newNames.contains(name)) 293 { 294 different = true; 295 break; 296 } 297 } 298 } 299 300 if(different) 301 { 302 log.error("CMR table structure is incorrect for " + cmrField.getQualifiedTableName()); 304 } 306 307 } 309 } 311 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 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 Set 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 final EntityBridge relatedEntity = cmrField.getRelatedEntity(); 350 351 if(relationMetaData.isForeignKeyMappingStyle() && createdTables.contains(relatedEntity.getEntityName())) 355 { 356 createCMRIndex(((JDBCAbstractEntityBridge)relatedEntity).getDataSource(), cmrField); 357 } 358 359 addForeignKeyConstraint(cmrField); 361 } 362 } 363 364 public static Set getCreatedTables(JDBCEntityPersistenceStore manager) 365 { 366 final String key = "CREATED_TABLES"; 367 Set createdTables = (Set ) manager.getApplicationData(key); 368 if(createdTables == null) 369 { 370 createdTables = new HashSet (); 371 manager.putApplicationData(key, createdTables); 372 } 373 return createdTables; 374 } 375 376 public static Set getExistedTables(JDBCEntityPersistenceStore manager) 377 { 378 final String key = "EXISTED_TABLES"; 379 Set existedTables = (Set ) manager.getApplicationData(key); 380 if(existedTables == null) 381 { 382 existedTables = new HashSet (); 383 manager.putApplicationData(key, existedTables); 384 } 385 return existedTables; 386 } 387 388 395 private boolean hasIndex(SQLUtil.OldIndexes oldIndexes, JDBCFieldBridge field) 396 { 397 JDBCType jdbcType = field.getJDBCType(); 398 String [] columns = jdbcType.getColumnNames(); 399 ArrayList idxNames = oldIndexes.getIndexNames(); 400 ArrayList idxColumns = oldIndexes.getColumnNames(); 401 402 String indexName = null; 404 for(int i = 0; i < columns.length; ++i) 405 { 406 String 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 )idxNames.get(index); 416 } 417 else if(!indexName.equals(idxNames.get(index))) 418 { 419 return false; 420 } 421 } 422 423 return true; 424 476 } 477 478 private int columnIndex(ArrayList idxColumns, String column) 479 { 480 for(int j = 0; j < idxColumns.size(); ++j) 481 { 482 String idxColumn = (String )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 dataSource, JDBCFunctionMappingMetaData mapping, String tableName, String fieldName, String fieldStructure) 498 throws DeploymentException 499 { 500 StringBuffer sqlBuf = new StringBuffer (); 501 mapping.getFunctionSql( new String []{tableName, fieldName, fieldStructure}, sqlBuf ); 502 String sql = sqlBuf.toString(); 503 504 log.warn( sql ); 505 506 TransactionManager tm = manager.getContainer().getTransactionManager(); 508 Transaction oldTransaction; 509 try 510 { 511 oldTransaction = tm.suspend(); 512 } 513 catch(Exception e) 514 { 515 throw new DeploymentException(COULDNT_SUSPEND + " alter table.", e); 516 } 517 518 try 519 { 520 Connection con = null; 521 Statement statement = null; 522 try 523 { 524 con = dataSource.getConnection(); 525 statement = con.createStatement(); 526 statement.executeUpdate(sql); 527 } 528 finally 529 { 530 JDBCUtil.safeClose(statement); 533 JDBCUtil.safeClose(con); 534 } 535 } 536 catch(Exception 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 if(oldTransaction != null) 547 { 548 tm.resume(oldTransaction); 549 } 550 } 551 catch(Exception e) 552 { 553 throw new DeploymentException(COULDNT_REATTACH + "alter table"); 554 } 555 } 556 557 if ( log.isDebugEnabled() ) 559 log.debug("Table altered successfully."); 560 } 561 562 private void createTable(DataSource dataSource, String tableName, String sql) 563 throws DeploymentException 564 { 565 if(SQLUtil.tableExists(tableName, dataSource)) 567 { 568 log.debug("Table '" + tableName + "' already exists"); 569 return; 570 } 571 572 574 TransactionManager tm = manager.getContainer().getTransactionManager(); 576 Transaction oldTransaction; 577 try 578 { 579 oldTransaction = tm.suspend(); 580 } 581 catch(Exception e) 582 { 583 throw new DeploymentException(COULDNT_SUSPEND + "creating table.", e); 584 } 585 586 try 587 { 588 Connection con = null; 589 Statement statement = null; 590 try 591 { 592 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 JDBCUtil.safeClose(statement); 607 JDBCUtil.safeClose(con); 608 } 609 } 610 catch(Exception 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 if(oldTransaction != null) 621 { 622 tm.resume(oldTransaction); 623 } 624 } 625 catch(Exception e) 626 { 627 throw new DeploymentException(COULDNT_REATTACH + "create table"); 628 } 629 } 630 631 Set createdTables = (Set ) manager.getApplicationData(CREATED_TABLES_KEY); 633 createdTables.add(tableName); 634 } 635 636 645 private void createIndex(DataSource dataSource, String tableName, String indexName, String sql) 646 throws DeploymentException 647 { 648 TransactionManager tm = manager.getContainer().getTransactionManager(); 652 Transaction oldTransaction; 653 try 654 { 655 oldTransaction = tm.suspend(); 656 } 657 catch(Exception e) 658 { 659 throw new DeploymentException(COULDNT_SUSPEND + "creating index.", e); 660 } 661 662 try 663 { 664 Connection con = null; 665 Statement statement = null; 666 try 667 { 668 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 JDBCUtil.safeClose(statement); 682 JDBCUtil.safeClose(con); 683 } 684 } 685 catch(Exception 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 if(oldTransaction != null) 696 { 697 tm.resume(oldTransaction); 698 } 699 } 700 catch(Exception e) 701 { 702 throw new DeploymentException(COULDNT_REATTACH + "create index"); 703 } 704 } 705 } 706 707 708 715 private void issuePostCreateSQL(DataSource dataSource, List sql, String table) 716 throws DeploymentException 717 { 718 if(sql == null) 719 { log.trace("issuePostCreateSQL: sql is null"); 721 return; 722 } 723 724 log.debug("issuePostCreateSQL::sql: " + sql.toString() + " on table " + table); 725 726 TransactionManager tm = manager.getContainer().getTransactionManager(); 727 Transaction oldTransaction; 728 729 try 730 { 731 oldTransaction = tm.suspend(); 732 } 733 catch(Exception e) 734 { 735 throw new DeploymentException(COULDNT_SUSPEND + "sending sql command.", e); 736 } 737 738 String currentCmd = ""; 739 740 try 741 { 742 Connection con = null; 743 Statement  |