1 22 package org.jboss.ejb.plugins.cmp.jdbc2.schema; 23 24 import org.jboss.deployment.DeploymentException; 25 import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil; 26 import org.jboss.ejb.plugins.cmp.jdbc.JDBCUtil; 27 import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore; 28 import org.jboss.ejb.plugins.cmp.jdbc.JDBCTypeFactory; 29 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge; 30 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData; 31 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData; 32 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCTypeMappingMetaData; 33 import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCEntityBridge2; 34 import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCCMPFieldBridge2; 35 import org.jboss.logging.Logger; 36 import org.jboss.mx.util.MBeanServerLocator; 37 import org.jboss.mx.util.MBeanProxyExt; 38 import org.jboss.system.ServiceControllerMBean; 39 import org.jboss.system.Registry; 40 import org.jboss.metadata.ConfigurationMetaData; 41 import org.jboss.metadata.MetaData; 42 import org.jboss.metadata.EntityMetaData; 43 import org.jboss.cache.invalidation.InvalidationManagerMBean; 44 import org.jboss.cache.invalidation.InvalidationGroup; 45 import org.w3c.dom.Element ; 46 47 import javax.naming.InitialContext ; 48 import javax.naming.NamingException ; 49 import javax.sql.DataSource ; 50 import javax.ejb.DuplicateKeyException ; 51 import javax.ejb.EJBException ; 52 import javax.ejb.NoSuchEntityException ; 53 import javax.ejb.NoSuchObjectLocalException ; 54 import javax.transaction.Transaction ; 55 import javax.management.MBeanServer ; 56 import javax.management.ObjectName ; 57 import java.sql.Connection ; 58 import java.sql.PreparedStatement ; 59 import java.sql.SQLException ; 60 import java.sql.ResultSet ; 61 import java.util.Map ; 62 import java.util.HashMap ; 63 import java.util.ArrayList ; 64 import java.util.List ; 65 66 67 73 public class EntityTable 74 implements Table 75 { 76 private static final byte UNREFERENCED = 0; 77 private static final byte CLEAN = 1; 78 private static final byte DIRTY = 2; 79 private static final byte CREATED = 4; 80 private static final byte DELETED = 8; 81 private static final byte DIRTY_RELATIONS = 16; 82 83 private static final Object NOT_LOADED = new Object (); 84 85 private JDBCEntityBridge2 entity; 86 private String tableName; 87 private int fieldsTotal; 88 private int relationsTotal; 89 private DataSource dataSource; 90 private Schema schema; 91 private int tableId; 92 private boolean dontFlushCreated; 93 94 private String deleteSql; 95 private String updateSql; 96 private String insertSql; 97 private String selectSql; 98 private String duplicatePkSql; 99 100 private final CommitStrategy insertStrategy; 101 private final CommitStrategy deleteStrategy; 102 private final CommitStrategy updateStrategy; 103 104 private Logger log; 105 106 private Cache cache; 107 private ServiceControllerMBean serviceController; 108 private ObjectName cacheName; 109 110 private int[] references; 111 private int[] referencedBy; 112 113 private ForeignKeyConstraint[] fkConstraints; 114 115 private CacheInvalidator cacheInvalidator; 116 117 public EntityTable(JDBCEntityMetaData metadata, JDBCEntityBridge2 entity, Schema schema, int tableId) 118 throws DeploymentException 119 { 120 try 121 { 122 InitialContext ic = new InitialContext (); 123 dataSource = (DataSource ) ic.lookup(metadata.getDataSourceName()); 124 } 125 catch(NamingException e) 126 { 127 throw new DeploymentException("Filed to lookup: " + metadata.getDataSourceName(), e); 128 } 129 130 this.entity = entity; 131 tableName = SQLUtil.fixTableName(metadata.getDefaultTableName(), dataSource); 132 log = Logger.getLogger(getClass().getName() + "." + tableName); 133 134 this.schema = schema; 135 this.tableId = tableId; 136 137 final EntityMetaData entityMetaData = ((EntityMetaData)entity.getContainer().getBeanMetaData()); 138 final ConfigurationMetaData containerConf = entityMetaData.getContainerConfiguration(); 139 dontFlushCreated = containerConf.isInsertAfterEjbPostCreate(); 140 141 final Element cacheConf = containerConf.getContainerCacheConf(); 143 final Element cachePolicy = cacheConf == null ? null : MetaData.getOptionalChild(cacheConf, "cache-policy-conf"); 144 145 int minCapacity; 146 int maxCapacity; 147 if(cachePolicy != null) 148 { 149 String str = MetaData.getOptionalChildContent(cachePolicy, "min-capacity"); 150 minCapacity = (str == null ? 1000 : Integer.parseInt(str)); 151 str = MetaData.getOptionalChildContent(cachePolicy, "max-capacity"); 152 maxCapacity = (str == null ? 10000 : Integer.parseInt(str)); 153 } 154 else 155 { 156 minCapacity = 1000; 157 maxCapacity = 10000; 158 } 159 160 final Element otherConf = cacheConf == null ? null : MetaData.getOptionalChild(cacheConf, "cache-policy-conf-other"); 161 162 int partitionsTotal; 163 final boolean invalidable; 164 final Element batchCommitStrategy; 165 if(otherConf != null) 166 { 167 String str = MetaData.getOptionalChildContent(otherConf, "partitions"); 168 partitionsTotal = (str == null ? 10 : Integer.parseInt(str)); 169 batchCommitStrategy = MetaData.getOptionalChild(otherConf, "batch-commit-strategy"); 170 invalidable = MetaData.getOptionalChild(otherConf, "invalidable") == null ? false : true; 171 } 172 else 173 { 174 partitionsTotal = 10; 175 batchCommitStrategy = null; 176 invalidable = false; 177 } 178 179 if(cachePolicy != null) 180 { 181 cache = new PartitionedTableCache(minCapacity, maxCapacity, partitionsTotal); 182 183 String periodStr = MetaData.getOptionalChildContent(cachePolicy, "overager-period"); 184 String maxAgeStr = MetaData.getOptionalChildContent(cachePolicy, "max-bean-age"); 185 if(periodStr != null && maxAgeStr == null || maxAgeStr != null && periodStr == null) 186 { 187 throw new DeploymentException( 188 "Failed to initialize age-out thread for entity " + entity.getEntityName() + 189 ": overager-period or max-bean-age is missing!"); 190 } 191 else if(periodStr != null && maxAgeStr != null) 192 { 193 long period = Long.parseLong(periodStr); 194 long maxAge = Long.parseLong(maxAgeStr); 195 ((PartitionedTableCache)cache).initOverager(period, maxAge, entity.getEntityName() + " overager"); 196 197 if(log.isTraceEnabled()) 198 { 199 log.trace("initialized age-out thread for " + entity.getEntityName() + 200 ": overager-period=" + period + ", max-bean-age=" + maxAge); 201 } 202 } 203 204 final MBeanServer server = MBeanServerLocator.locateJBoss(); 205 serviceController = (ServiceControllerMBean) 206 MBeanProxyExt.create(ServiceControllerMBean.class, 207 ServiceControllerMBean.OBJECT_NAME, 208 server); 209 try 210 { 211 cacheName = 212 new ObjectName ("jboss.cmp:service=tablecache,ejbname=" + metadata.getName() + ",table=" + tableName); 213 server.registerMBean(cache, cacheName); 214 serviceController.create(cacheName); 215 } 216 catch(Exception e) 217 { 218 throw new DeploymentException("Failed to register table cache for " + tableName, e); 219 } 220 } 221 else 222 { 223 cache = Cache.NONE; 224 } 225 226 if(invalidable) 227 { 228 String groupName = entityMetaData.getDistributedCacheInvalidationConfig().getInvalidationGroupName(); 229 String imName = entityMetaData.getDistributedCacheInvalidationConfig().getInvalidationManagerName(); 230 231 InvalidationManagerMBean im = (InvalidationManagerMBean) Registry.lookup(imName); 232 InvalidationGroup invalidationGroup = im.getInvalidationGroup(groupName); 233 234 cacheInvalidator = new CacheInvalidator(cache, entity.getContainer().getTransactionManager(), invalidationGroup); 235 } 236 237 if(batchCommitStrategy == null) 238 { 239 insertStrategy = NON_BATCH_UPDATE; 240 deleteStrategy = NON_BATCH_UPDATE; 241 updateStrategy = NON_BATCH_UPDATE; 242 } 243 else 244 { 245 log.debug("batch-commit-strategy enabled"); 246 insertStrategy = BATCH_UPDATE; 247 deleteStrategy = BATCH_UPDATE; 248 updateStrategy = BATCH_UPDATE; 249 } 250 } 251 252 public void start() throws DeploymentException 253 { 254 final JDBCAbstractCMRFieldBridge[] cmrFields = entity.getCMRFields(); 255 relationsTotal = (cmrFields != null ? cmrFields.length : 0); 256 257 JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields(); 258 JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields(); 259 260 deleteSql = "delete from " + tableName + " where "; 262 deleteSql += pkFields[0].getColumnName() + "=?"; 263 for(int i = 1; i < pkFields.length; ++i) 264 { 265 deleteSql += " and " + pkFields[i].getColumnName() + "=?"; 266 } 267 log.debug("delete sql: " + deleteSql); 268 269 insertSql = "insert into " + tableName + "("; 271 insertSql += tableFields[0].getColumnName(); 272 for(int i = 1; i < tableFields.length; ++i) 273 { 274 insertSql += ", " + tableFields[i].getColumnName(); 275 } 276 insertSql += ") values (?"; 277 for(int i = 1; i < tableFields.length; ++i) 278 { 279 insertSql += ", ?"; 280 } 281 insertSql += ")"; 282 log.debug("insert sql: " + insertSql); 283 284 updateSql = "update " + tableName + " set "; 286 int setFields = 0; 287 for(int i = 0; i < tableFields.length; ++i) 288 { 289 JDBCCMPFieldBridge2 field = tableFields[i]; 290 if(!field.isPrimaryKeyMember()) 291 { 292 if(setFields++ > 0) 293 { 294 updateSql += ", "; 295 } 296 updateSql += field.getColumnName() + "=?"; 297 } 298 } 299 updateSql += " where "; 300 updateSql += pkFields[0].getColumnName() + "=?"; 301 for(int i = 1; i < pkFields.length; ++i) 302 { 303 updateSql += " and " + pkFields[i].getColumnName() + "=?"; 304 } 305 306 if(entity.getVersionField() != null) 307 { 308 updateSql += " and " + entity.getVersionField().getColumnName() + "=?"; 309 } 310 log.debug("update sql: " + updateSql); 311 312 String selectColumns = tableFields[0].getColumnName(); 314 for(int i = 1; i < tableFields.length; ++i) 315 { 316 JDBCCMPFieldBridge2 field = tableFields[i]; 317 selectColumns += ", " + field.getColumnName(); 318 } 319 320 String whereColumns = pkFields[0].getColumnName() + "=?"; 321 for(int i = 1; i < pkFields.length; ++i) 322 { 323 whereColumns += " and " + pkFields[i].getColumnName() + "=?"; 324 } 325 326 if(entity.getMetaData().hasRowLocking()) 327 { 328 JDBCEntityPersistenceStore manager = entity.getManager(); 329 JDBCTypeFactory typeFactory = manager.getJDBCTypeFactory(); 330 JDBCTypeMappingMetaData typeMapping = typeFactory.getTypeMapping(); 331 JDBCFunctionMappingMetaData rowLockingTemplate = typeMapping.getRowLockingTemplate(); 332 if(rowLockingTemplate == null) 333 { 334 throw new DeploymentException("Row locking template is not defined for mapping: " + typeMapping.getName()); 335 } 336 337 selectSql = rowLockingTemplate.getFunctionSql(new Object []{selectColumns, tableName, whereColumns, null}, 338 new StringBuffer ()).toString(); 339 } 340 else 341 { 342 selectSql = "select "; 343 selectSql += selectColumns; 344 selectSql += " from " + tableName + " where "; 345 selectSql += whereColumns; 346 } 347 log.debug("select sql: " + selectSql); 348 349 if(dontFlushCreated) 351 { 352 duplicatePkSql = "select "; 353 duplicatePkSql += pkFields[0].getColumnName(); 354 for(int i = 1; i < pkFields.length; ++i) 355 { 356 duplicatePkSql += ", " + pkFields[i].getColumnName(); 357 } 358 duplicatePkSql += " from " + tableName + " where "; 359 duplicatePkSql += pkFields[0].getColumnName() + "=?"; 360 for(int i = 1; i < pkFields.length; ++i) 361 { 362 duplicatePkSql += " and " + pkFields[i].getColumnName() + "=?"; 363 } 364 log.debug("duplicate pk sql: " + duplicatePkSql); 365 } 366 367 if(cacheName != null) 368 { 369 try 370 { 371 serviceController.start(cacheName); 372 } 373 catch(Exception e) 374 { 375 throw new DeploymentException("Failed to start table cache.", e); 376 } 377 } 378 } 379 380 public void stop() throws Exception 381 { 382 if(cacheInvalidator != null) 383 { 384 cacheInvalidator.unregister(); 385 } 386 387 if(cacheName != null) 388 { 389 serviceController.stop(cacheName); 390 serviceController.destroy(cacheName); 391 serviceController.remove(cacheName); 392 } 393 serviceController = null; 394 } 395 396 public StringBuffer appendColumnNames(JDBCCMPFieldBridge2[] fields, String alias, StringBuffer buf) 397 { 398 for(int i = 0; i < fields.length; ++i) 399 { 400 if(i > 0) 401 { 402 buf.append(", "); 403 } 404 405 if(alias != null) 406 { 407 buf.append(alias).append("."); 408 } 409 410 buf.append(fields[i].getColumnName()); 411 } 412 413 return buf; 414 } 415 416 public void addField() 417 { 418 ++fieldsTotal; 419 } 420 421 public int addVersionField() 422 { 423 return fieldsTotal++; 424 } 425 426 public ForeignKeyConstraint addFkConstraint(JDBCCMPFieldBridge2[] fkFields, EntityTable referenced) 427 { 428 addReference(referenced); 429 referenced.addReferencedBy(this); 430 431 if(fkConstraints == null) 432 { 433 fkConstraints = new ForeignKeyConstraint[1]; 434 } 435 else 436 { 437 ForeignKeyConstraint[] tmp = fkConstraints; 438 fkConstraints = new ForeignKeyConstraint[tmp.length + 1]; 439 System.arraycopy(tmp, 0, fkConstraints, 0, tmp.length); 440 } 441 final int fkindex = fkConstraints.length - 1; 442 final ForeignKeyConstraint fkc = new ForeignKeyConstraint(fkindex, fkFields); 443 fkConstraints[fkindex] = fkc; 444 return fkc; 445 } 446 447 public DataSource getDataSource() 448 { 449 return dataSource; 450 } 451 452 public Object loadRow(ResultSet rs, boolean searchableOnly) 453 { 454 View view = getView(); 455 Object pk = view.loadPk(rs); 456 if(pk != null) 457 { 458 view.loadRow(rs, pk, searchableOnly); 459 } 460 else if(log.isTraceEnabled()) 461 { 462 log.trace("loaded pk is null."); 463 } 464 return pk; 465 } 466 467 public Row getRow(Object id) 468 { 469 return getView().getRow(id); 470 } 471 472 public boolean hasRow(Object id) 473 { 474 return getView().hasRow(id); 475 } 476 477 public Row loadRow(Object id) throws SQLException 478 { 479 View view = getView(); 480 481 Row row = view.getRowByPk(id, false); 482 if(row != null) 483 { 484 if(log.isTraceEnabled()) 485 { 486 log.trace("row is already loaded: pk=" + id); 487 } 488 return row; 489 } 490 491 JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields(); 492 493 Connection con = null; 494 PreparedStatement ps = null; 495 ResultSet rs = null; 496 try 497 { 498 if(log.isDebugEnabled()) 499 { 500 log.debug("executing sql: " + selectSql); 501 } 502 503 con = dataSource.getConnection(); 504 ps = con.prepareStatement(selectSql); 505 506 int paramInd = 1; 507 for(int i = 0; i < pkFields.length; ++i) 508 { 509 JDBCCMPFieldBridge2 pkField = pkFields[i]; 510 Object pkValue = pkField.getPrimaryKeyValue(id); 511 paramInd = pkField.setArgumentParameters(ps, paramInd, pkValue); 512 } 513 514 rs = ps.executeQuery(); 515 516 if(!rs.next()) 517 { 518 throw new NoSuchEntityException ("Row not found: " + id); 519 } 520 521 return view.loadRow(rs, id, false); 522 } 523 catch(SQLException e) 524 { 525 log.error("Failed to load row: table=" + tableName + ", pk=" + id); 526 throw e; 527 } 528 finally 529 { 530 JDBCUtil.safeClose(rs); 531 JDBCUtil.safeClose(ps); 532 JDBCUtil.safeClose(con); 533 } 534 } 535 536 538 public int getTableId() 539 { 540 return tableId; 541 } 542 543 public String getTableName() 544 { 545 return tableName; 546 } 547 548 public Table.View createView(Transaction tx) 549 { 550 return new View(tx); 551 } 552 553 555 private void addReference(EntityTable table) 556 { 557 boolean wasRegistered = false; 558 if(references != null) 559 { 560 for(int i = 0; i < references.length; ++i) 561 { 562 if(references[i] == table.getTableId()) 563 { 564 wasRegistered = true; 565 break; 566 } 567 } 568 569 if(!wasRegistered) 570 { 571 int[] tmp = references; 572 references = new int[references.length + 1]; 573 System.arraycopy(tmp, 0, references, 0, tmp.length); 574 references[tmp.length] = table.getTableId(); 575 } 576 } 577 else 578 { 579 references = new int[1]; 580 references[0] = table.getTableId(); 581 } 582 583 if(!wasRegistered) 584 { 585 if(log.isTraceEnabled()) 586 { 587 log.trace("references " + table.getTableName()); 588 } 589 } 590 } 591 592 private void addReferencedBy(EntityTable table) 593 { 594 boolean wasRegistered = false; 595 if(referencedBy != null) 596 { 597 for(int i = 0; i < referencedBy.length; ++i) 598 { 599 if(referencedBy[i] == table.getTableId()) 600 { 601 wasRegistered = true; 602 break; 603 } 604 } 605 606 if(!wasRegistered) 607 { 608 int[] tmp = referencedBy; 609 referencedBy = new int[referencedBy.length + 1]; 610 System.arraycopy(tmp, 0, referencedBy, 0, tmp.length); 611 referencedBy[tmp.length] = table.getTableId(); 612 } 613 } 614 else 615 { 616 referencedBy = new int[1]; 617 referencedBy[0] = table.getTableId(); 618 } 619 620 if(!wasRegistered) 621 { 622 if(log.isTraceEnabled()) 623 { 624 log.trace("referenced by " + table.getTableName()); 625 } 626 } 627 } 628 629 private void delete(View view) throws SQLException 630 { 631 JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields(); 632 633 Connection con = null; 634 PreparedStatement ps = null; 635 try 636 { 637 if(log.isDebugEnabled()) 638 { 639 log.debug("executing : " + deleteSql); 640 } 641 642 con = dataSource.getConnection(); 643 ps = con.prepareStatement(deleteSql); 644 645 int batchCount = 0; 646 while(view.deleted != null) 647 { 648 Row row = view.deleted; 649 650 int paramInd = 1; 651 for(int pkInd = 0; pkInd < pkFields.length; ++pkInd) 652 { 653 JDBCCMPFieldBridge2 pkField = pkFields[pkInd]; 654 Object fieldValue = row.fields[pkField.getRowIndex()]; 655 paramInd = pkField.setArgumentParameters(ps, paramInd, fieldValue); 656 } 657 658 deleteStrategy.executeUpdate(ps); 659 660 ++batchCount; 661 row.flushStatus(); 662 } 663 664 deleteStrategy.executeBatch(ps); 665 666 if(view.deleted != null) 667 { 668 throw new IllegalStateException ("There are still rows to delete!"); 669 } 670 671 if(log.isTraceEnabled()) 672 { 673 log.trace("deleted rows: " + batchCount); 674 } 675 } 676 catch(SQLException e) 677 { 678 log.error("Failed to delete view: " + e.getMessage(), e); 679 throw e; 680 } 681 finally 682 { 683 JDBCUtil.safeClose(ps); 684 JDBCUtil.safeClose(con); 685 } 686 } 687 688 private void update(View view) throws SQLException 689 { 690 JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields(); 691 JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields(); 692 693 Connection con = null; 694 PreparedStatement ps = null; 695 try 696 { 697 if(log.isDebugEnabled()) 698 { 699 log.debug("executing : " + updateSql); 700 } 701 702 con = dataSource.getConnection(); 703 ps = con.prepareStatement(updateSql); 704 705 int batchCount = 0; 706 while(view.dirty != null) 707 { 708 Row row = view.dirty; 709 710 int paramInd = 1; 711 for(int fInd = 0; fInd < tableFields.length; ++fInd) 712 { 713 JDBCCMPFieldBridge2 field = tableFields[fInd]; 714 if(!field.isPrimaryKeyMember()) 715 { 716 Object fieldValue = row.fields[field.getRowIndex()]; 717 paramInd = field.setArgumentParameters(ps, paramInd, fieldValue); 718 } 719 } 720 721 for(int fInd = 0; fInd < pkFields.length; ++fInd) 722 { 723 JDBCCMPFieldBridge2 pkField = pkFields[fInd]; 724 Object fieldValue = row.fields[pkField.getRowIndex()]; 725 paramInd = pkField.setArgumentParameters(ps, paramInd, fieldValue); 726 } 727 728 JDBCCMPFieldBridge2 versionField = entity.getVersionField(); 729 if(versionField != null) 730 { 731 int versionIndex = versionField.getVersionIndex(); 732 Object curVersion = row.fields[versionIndex]; 733 paramInd = versionField.setArgumentParameters(ps, paramInd, curVersion); 734 735 Object newVersion = row.fields[versionField.getRowIndex()]; 736 row.fields[versionIndex] = newVersion; 737 } 738 739 updateStrategy.executeUpdate(ps); 740 741 ++batchCount; 742 row.flushStatus(); 743 } 744 745 updateStrategy.executeBatch(ps); 746 747 if(log.isTraceEnabled()) 748 { 749 log.trace("updated rows: " + batchCount); 750 } 751 } 752 catch(SQLException e) 753 { 754 log.error("Failed to update: table=" + tableName, e); 755 throw e; 756 } 757 finally 758 { 759 JDBCUtil.safeClose(ps); 760 JDBCUtil.safeClose(con); 761 } 762 } 763 764 private void insert(View view) throws SQLException 765 { 766 JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields(); 767 Connection con = null; 768 PreparedStatement ps = null; 769 try 770 { 771 if(log.isDebugEnabled()) 772 { 773 log.debug("executing : " + insertSql); 774 } 775 776 con = dataSource.getConnection(); 777 ps = con.prepareStatement(insertSql); 778 779 int batchCount = 0; 780 while(view.created != null) 781 { 782 Row row = view.created; 783 784 int paramInd = 1; 785 for(int fInd = 0; fInd < tableFields.length; ++fInd) 786 { 787 JDBCCMPFieldBridge2 field = tableFields[fInd]; 788 Object fieldValue = row.fields[field.getRowIndex()]; 789 paramInd = field.setArgumentParameters(ps, paramInd, fieldValue); 790 } 791 792 insertStrategy.executeUpdate(ps); 793 794 ++batchCount; 795 row.flushStatus(); 796 } 797 798 insertStrategy.executeBatch(ps); 799 800 if(log.isTraceEnabled()) 801 { 802 log.trace("inserted rows: " + batchCount); 803 } 804 } 805 catch(SQLException e) 806 { 807 log.error("Failed to insert new rows: " + e.getMessage(), e); 808 throw e; 809 } 810 finally 811 { 812 JDBCUtil.safeClose(ps); 813 JDBCUtil.safeClose(con); 814 } 815 } 816 817 private EntityTable.View getView() 818 { 819 return (EntityTable.View) schema.getView(this); 820 } 821 822 public class View implements Table.View 823 { 824 private final Transaction tx; 825 826 private Map rowByPk = new HashMap (); 827 private Row created; 828 private Row deleted; 829 private Row dirty; 830 private Row dirtyRelations; 831 private Row clean; 832 833 private Row cacheUpdates; 834 835 private List rowsWithNullFks; 836 837 private boolean inFlush; 838 839 public View(Transaction tx) 840 { 841 this.tx = tx; 842 } 843 844 public Row getRow(Object pk) 845 { 846 Row row; 847 if(pk == null) 848 { 849 row = new Row(this); 850 } 851 else 852 { 853 row = getRowByPk(pk, false); 854 if(row == null) 855 { 856 row = createCleanRow(pk); 857 } 858 } 859 return row; 860 } 861 862 public Row getRowByPk(Object pk, boolean required) 863 { 864 895 896 Row row = (Row) rowByPk.get(pk); 897 898 if(row == null) 899 { 900 Object [] fields; 901 Object [] relations = null; 902 try 903 { 904 cache.lock(pk); 905 906 fields = cache.getFields(pk); 907 if(fields != null && relationsTotal > 0) 908 { 909 relations = cache.getRelations(pk); 910 if(relations == null) 911 { 912 relations = new Object [relationsTotal]; 913 } 914 } 915 } 916 finally 917 { 918 cache.unlock(pk); 919 } 920 921 if(fields != null) 922 { 923 row = createCleanRow(pk, fields, relations); 924 } 925 } 926 927 if(row == null && required) 928 { 929 throw new IllegalStateException ("row not found: pk=" + pk); 930 } 931 932 return row; 933 } 934 935 public void addClean(Row row) 936 { 937 943 944 if(clean != null) 945 { 946 row.next = clean; 947 clean.prev = row; 948 } 949 950 clean = row; 951 row.state = CLEAN; 952 953 rowByPk.put(row.pk, row); 954 } 955 956 public void addCreated(Row row) throws DuplicateKeyException 957 { 958 963 if(created != null) 964 { 965 row.next = created; 966 created.prev = row; 967 } 968 969 created = row; 970 row.state = CREATED; 971 972 rowByPk.put(row.pk, row); 973 974 JDBCCMPFieldBridge2 versionField = entity.getVersionField(); 975 if(versionField != null) 976 { 977 row.fields[versionField.getVersionIndex()] = row.fields[versionField.getRowIndex()]; 978 } 979 } 980 981 public Row loadRow(ResultSet rs, Object pk, boolean searchableOnly) 982 { 983 Row row = getRowByPk(pk, false); 984 if(row != null) 985 { 986 if(log.isTraceEnabled()) 987 { 988 log.trace("row is already loaded: pk=" + pk); 989 } 990 return row; 991 } 992 else if(log.isTraceEnabled()) 993 { 994 log.trace("reading result set: pk=" + pk); 995 } 996 997 row = createCleanRow(pk); 998 JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields(); 999 int rsOffset = 1; 1003 for(int i = 0; i < tableFields.length; ++i) 1004 { 1005 JDBCCMPFieldBridge2 field = tableFields[i]; 1006 if(searchableOnly && !field.getJDBCType().isSearchable()) 1007 { 1008 row.fields[field.getRowIndex()] = NOT_LOADED; 1009 --rsOffset; 1010 continue; 1011 } 1012 1013 Object columnValue = field.loadArgumentResults(rs, field.getRowIndex() + rsOffset); 1014 row.fields[field.getRowIndex()] = columnValue; 1015 1016 if(field.getVersionIndex() != -1) 1017 { 1018 row.fields[field.getVersionIndex()] = columnValue; 1019 } 1020 } 1021 1022 Object [] relations = (relationsTotal > 0 ? new Object [relationsTotal] : null); 1023 1024 try 1025 { 1026 cache.lock(row.pk); 1027 cache.put(tx, row.pk, row.fields, relations); 1028 } 1029 finally 1030 { 1031 cache.unlock(row.pk); 1032 } 1033 1034 return row; 1035 } 1036 1037 public Object loadPk(ResultSet rs) 1038 { 1039 Object pk = null; 1040 JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields(); 1041 for(int i = 0; i < pkFields.length; ++i) 1043 { 1044 JDBCCMPFieldBridge2 field = pkFields[i]; 1045 Object columnValue = field.loadArgumentResults(rs, field.getRowIndex() + 1); 1047 pk = field.setPrimaryKeyValue(pk, columnValue); 1048 } 1049 return pk; 1050 } 1051 1052 public boolean hasRow(Object id) 1053 { 1054 boolean has = rowByPk.containsKey(id); 1055 if(!has) 1056 { 1057 try 1058 { 1059 cache.lock(id); 1060 has = cache.contains(tx, id); 1061 } 1062 finally 1063 { 1064 cache.unlock(id); 1065 } 1066 } 1067 return has; 1068 } 1069 1070 public void addRowWithNullFk(Row row) 1071 { 1072 if(rowsWithNullFks == null) 1073 { 1074 rowsWithNullFks = new ArrayList (); 1075 } 1076 rowsWithNullFks.add(row); 1077 } 1078 1079 private Row createCleanRow(Object pk) 1080 { 1081 Row row = new Row(this); 1082 row.pk = pk; 1083 addClean(row); 1084 return row; 1085 } 1086 1087 private Row createCleanRow(Object pk, Object [] fields, Object [] relations) 1088 { 1089 Row row = new Row(this, fields, relations); 1090 row.pk = pk; 1091 addClean(row); 1092 return row; 1093 } 1094 1095 1097 public void flushDeleted(Schema.Views views) throws SQLException 1098 { 1099 if(rowsWithNullFks != null) 1100 { 1101 nullifyForeignKeys(); 1102 rowsWithNullFks = null; 1103 } 1104 1105 if(deleted == null) 1106 { 1107 if(log.isTraceEnabled()) 1108 { 1109 log.trace("no rows to delete"); 1110 } 1111 return; 1112 } 1113 1114 if(referencedBy != null) 1115 { 1116 if(inFlush) 1117 { 1118 if(log.isTraceEnabled()) 1119 { 1120 log.trace("inFlush, ignoring flushDeleted"); 1121 } 1122 return; 1123 } 1124 1125 inFlush = true; 1126 1127 try 1128 { 1129 for(int i = 0; i < referencedBy.length; ++i) 1130 { 1131 final Table.View view = views.entityViews[referencedBy[i]]; 1132 if(view != null) 1133 { 1134 view.flushDeleted(views); 1135 } 1136 } 1137 } 1138 finally 1139 { 1140 inFlush = false; 1141 } 1142 } 1143 1144 delete(this); 1145 } 1146 1147 public void flushCreated(Schema.Views views) throws SQLException 1148 { 1149 if(created == null || dontFlushCreated) 1150 { 1151 if(log.isTraceEnabled()) 1152 { 1153 log.trace("no rows to insert"); 1154 } 1155 return; 1156 } 1157 1158 if(references != null) 1159 { 1160 if(inFlush) 1161 { 1162 if(log.isTraceEnabled()) 1163 { 1164 log.trace("inFlush, ignorning flushCreated"); 1165 } 1166 return; 1167 } 1168 else if(log.isTraceEnabled()) 1169 { 1170 log.trace("flushing created references"); 1171 } 1172 1173 inFlush = true; 1174 try 1175 { 1176 for(int i = 0; i < references.length; ++i) 1177 { 1178 final Table.View view = views.entityViews[references[i]]; 1179 if(view != null) 1180 { 1181 view.flushCreated(views); 1182 } 1183 } 1184 } 1185 finally 1186 { 1187 inFlush = false; 1188 } 1189 } 1190 1191 insert(this); 1192 } 1193 1194 public void flushUpdated() throws SQLException 1195 { 1196 if(dirtyRelations != null) 1197 { 1198 while(dirtyRelations != null) 1199 { 1200 Row row = dirtyRelations; 1201 row.flushStatus(); 1202 } 1203 } 1204 1205 if(dirty == null) 1206 { 1207 if(log.isTraceEnabled()) 1208 { 1209 log.trace("no rows to update"); 1210 } 1211 return; 1212 } 1213 1214 update(this); 1215 } 1216 1217 public void beforeCompletion() 1218 { 1219 1245 } 1246 1247 public void committed() 1248 { 1249 if(cacheUpdates != null) 1250 { 1251 Row cursor = cacheUpdates; 1252 1253 while(cursor != null) 1254 { 1255 cache.lock(cursor.pk); 1258 try 1259 { 1260 switch(cursor.state) 1261 { 1262 case CLEAN: 1263 cache.put(tx, cursor.pk, cursor.fields, cursor.relations); 1264 break; 1265 case DELETED: 1266 try 1267 { 1268 cache.remove(tx, cursor.pk); 1269 } 1270 catch(Cache.RemoveException e) 1271 { 1272 log.warn(e.getMessage()); 1273 } 1274 break; 1275 default: 1276 throw new IllegalStateException ("Unexpected row state: table=" + 1277 entity.getQualifiedTableName() + 1278 ", pk=" + cursor.pk + ", state=" + cursor.state); 1279 } 1280 } 1281 finally 1282 { 1283 cache.unlock(cursor.pk); 1284 } 1285 cursor = cursor.nextCacheUpdate; 1288 } 1289 } 1290 } 1291 1292 public void rolledback() 1293 { 1294 1322 } 1323 1324 private void nullifyForeignKeys() 1325 throws SQLException 1326 { 1327 if(log.isTraceEnabled()) 1328 { 1329 log.trace("nullifying foreign keys"); 1330 } 1331 1332 Connection con = null; 1333 PreparedStatement [] ps = new PreparedStatement [fkConstraints.length]; 1334 1335 try 1336 { 1337 final JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields(); 1338 con = dataSource.getConnection(); 1339 1340 for(int i = 0; i < rowsWithNullFks.size(); ++i) 1341 { 1342 final Row row = (Row) rowsWithNullFks.get(i); 1343 if(row.state != DELETED) 1344 { 1345 final ForeignKeyConstraint[] cons = row.fkUpdates; 1346 for(int c = 0; c < fkConstraints.length; ++c) 1347 { 1348 if(cons[c] != null) 1349 { 1350 PreparedStatement s = ps[c]; 1351 if(s == null) 1352 { 1353 if(log.isDebugEnabled()) 1354 { 1355 log.debug("nullifying fk: " + cons[c].nullFkSql); 1356 } 1357 s = con.prepareStatement(cons[c].nullFkSql); 1358 ps[c] = s; 1359 } 1360 1361 int paramInd = 1; 1362 for(int fInd = 0; fInd < pkFields.length; ++fInd) 1363 { 1364 JDBCCMPFieldBridge2 pkField = pkFields[fInd]; 1365 Object fieldValue = row.fields[pkField.getRowIndex()]; 1366 paramInd = pkField.setArgumentParameters(s, paramInd, fieldValue); 1367 } 1368 1369 final int affected = s.executeUpdate(); 1370 if(affected != 1) 1371 { 1372 throw new EJBException ("Affected " + affected + " rows while expected just one"); 1373 } 1374 } 1375 } 1376 } 1377 } 1378 } 1379 finally 1380 { 1381 for(int i = 0; i < ps.length; ++i) 1382 { 1383 JDBCUtil.safeClose(ps[i]); 1384 } 1385 JDBCUtil.safeClose(con); 1386 } 1387 } 1388 } 1389 1390 public class Row 1391 { 1392 private EntityTable.View view; 1393 private Object pk; 1394 private final Object [] fields; 1395 private final Object [] relations; 1396 1397 private byte state; 1398 1399 private Row prev; 1400 private Row next; 1401 1402 private boolean cacheUpdateScheduled; 1403 private Row nextCacheUpdate; 1404 1406 private ForeignKeyConstraint[] fkUpdates; 1407 1408 public Row(EntityTable.View view) 1409 { 1410 this.view = view; 1411 fields = new Object [fieldsTotal]; 1412 relations = (relationsTotal == 0 ? null : new Object [relationsTotal]); 1413 state = UNREFERENCED; 1414 } 1415 1416 public Row(EntityTable.View view, Object [] fields, Object [] relations) 1417 { 1418 this.view = view; 1419 this.fields = fields; 1420 this.relations = relations; 1421 state = UNREFERENCED; 1422 } 1423 1424 public Object getPk() 1425 { 1426 return pk; 1427 } 1428 1429 public void loadCachedRelations(int index, Cache.CacheLoader loader) 1430 { 1431 if(relations != null) 1432 { 1433 final Object cached = relations[index]; 1434 relations[index] = loader.loadFromCache(cached); 1435 } 1436 } 1437 1438 public void cacheRelations(int index, Cache.CacheLoader loader) 1439 { 1440 relations[index] = loader.getCachedValue(); 1441 scheduleCacheUpdate(); 1442 } 1443 1444 public void insert(Object pk) throws DuplicateKeyException 1445 { 1446 this.pk = pk; 1447 view.addCreated(this); 1448 } 1449 1450 public Object getFieldValue(int i) 1451 { 1452 if(state == DELETED) 1453 { 1454 throw new NoSuchObjectLocalException ("The instance was removed: " + pk); 1455 } 1456 1457 Object value = fields[i]; 1458 if(value == NOT_LOADED) 1459 { 1460 value = loadField(i); 1461 } 1462 1463 return value; 1464 } 1465 1466 public void setFieldValue(int i, Object value) 1467 { 1468 fields[i] = value; 1469 } 1470 1471 public boolean isDirty() 1472 { 1473 return state != CLEAN && state != DIRTY_RELATIONS; 1474 } 1475 1476 public void setDirty() 1477 { 1478 if(state == CLEAN || state == DIRTY_RELATIONS) 1479 { 1480 updateState(DIRTY); 1481 } 1482 } 1483 1484 public void setDirtyRelations() 1485 { 1486 if(state == CLEAN) 1487 { 1488 updateState(DIRTY_RELATIONS); 1489 } 1490 } 1491 1492 public void delete() 1493 { 1494 if(state == CLEAN || state == DIRTY || state == DIRTY_RELATIONS) 1495 { 1496 updateState(DELETED); 1497 } 1498 else if(state == CREATED) 1499 { 1500 dereference(); 1501 state = DELETED; 1502 view.rowByPk.remove(pk); 1503 } 1504 else if(state == DELETED) 1505 { 1506 throw new IllegalStateException ("The row is already deleted: pk=" + pk); 1507 } 1508 } 1509 1510 public void nullForeignKey(ForeignKeyConstraint constraint) 1511 { 1512 if(fkUpdates == null) 1513 { 1514 fkUpdates = new ForeignKeyConstraint[fkConstraints.length]; 1515 view.addRowWithNullFk(this); 1516 } 1517 1518 fkUpdates[constraint.index] = constraint; 1519 } 1520 1521 public void nonNullForeignKey(ForeignKeyConstraint constraint) 1522 { 1523 if(fkUpdates != null) 1524 { 1525 fkUpdates[constraint.index] = null; 1526 } 1527 } 1528 1529 private void flushStatus() 1530 { 1531 if(state == CREATED || state == DIRTY) 1532 { 1533 updateState(CLEAN); 1534 } 1535 else if(state == DELETED) 1536 { 1537 dereference(); 1538 } 1539 else if(state == DIRTY_RELATIONS) 1540 { 1541 updateState(CLEAN); 1542 } 1543 1544 scheduleCacheUpdate(); 1545 } 1546 1547 private void scheduleCacheUpdate() 1548 { 1549 if(!cacheUpdateScheduled) 1550 { 1551 if(view.cacheUpdates == null) 1552 { 1553 view.cacheUpdates = this; 1554 } 1555 else 1556 { 1557 nextCacheUpdate = view.cacheUpdates; 1558 view.cacheUpdates = this; 1559 } 1560 cacheUpdateScheduled = true; 1561 } 1562 } 1563 1564 private void updateState(byte state) 1565 { 1566 dereference(); 1567 1568 if(state == CLEAN) 1569 { 1570 if(view.clean != null) 1571 { 1572 next = view.clean; 1573 view.clean.prev = this; 1574 } 1575 view.clean = this; 1576 } 1577 else if(state == DIRTY) 1578 { 1579 if(view.dirty != null) 1580 { 1581 next = view.dirty; 1582 view.dirty.prev = this; 1583 } 1584 view.dirty = this; 1585 } 1586 else if(state == CREATED) 1587 { 1588 if(view.created != null) 1589 { 1590 next = view.created; 1591 view.created.prev = this; 1592 } 1593 view.created = this; 1594 } 1595 else if(state == DELETED) 1596 { 1597 if(view.deleted != null) 1598 { 1599 next = view.deleted; 1600 view.deleted.prev = this; 1601 } 1602 view.deleted = this; 1603 } 1604 else if(state == DIRTY_RELATIONS) 1605 { 1606 if(view.dirtyRelations != null) 1607 { 1608 next = view.dirtyRelations; 1609 view.dirtyRelations.prev = this; 1610 } 1611 view.dirtyRelations = this; 1612 } 1613 else 1614 { 1615 throw new IllegalStateException ("Can't update to state: " + state); 1616 } 1617 1618 this.state = state; 1619 } 1620 1621 private void dereference() 1622 { 1623 if(state == CLEAN && view.clean == this) 1624 { 1625 view.clean = next; 1626 } 1627 else if(state == DIRTY && view.dirty == this) 1628 { 1629 view.dirty = next; 1630 } 1631 else if(state == CREATED && view.created == this) 1632 { 1633 view.created = next; 1634 } 1635 else if(state == DELETED && view.deleted == this) 1636 { 1637 view.deleted = next; 1638 } 1639 else if(state == DIRTY_RELATIONS && view.dirtyRelations == this) 1640 { 1641 view.dirtyRelations = next; 1642 } 1643 1644 if(next != null) 1645 { 1646 next.prev = prev; 1647 } 1648 1649 if(prev != null) 1650 { 1651 prev.next = next; 1652 } 1653 1654 prev = null; 1655 next = null; 1656 } 1657 1658 public void flush() throws SQLException , DuplicateKeyException 1659 { 1660 1662 if(state != CREATED) 1663 { 1664 if(log.isTraceEnabled()) 1665 { 1666 log.trace("The row is already inserted: pk=" + pk); 1667 } 1668 return; 1669 } 1670 1671 Connection con = null; 1672 PreparedStatement duplicatePkPs = null; 1673 PreparedStatement insertPs = null; 1674 ResultSet rs = null; 1675 try 1676 { 1677 int paramInd; 1678 con = dataSource.getConnection(); 1679 1680 1704 1705 if(log.isDebugEnabled()) 1707 { 1708 log.debug("executing : " + insertSql); 1709 } 1710 1711 insertPs = con.prepareStatement(insertSql); 1712 1713 paramInd = 1; 1714 JDBCCMPFieldBridge2[] tableFields = (JDBCCMPFieldBridge2[]) entity.getTableFields(); 1715 for(int fInd = 0; fInd < tableFields.length; ++fInd) 1716 { 1717 JDBCCMPFieldBridge2 field = tableFields[fInd]; 1718 Object fieldValue = fields[field.getRowIndex()]; 1719 paramInd = field.setArgumentParameters(insertPs, paramInd, fieldValue); 1720 } 1721 1722 insertPs.executeUpdate(); 1723 1724 flushStatus(); 1725 } 1726 catch(SQLException e) 1727 { 1728 log.error("Failed to insert new rows: " + e.getMessage(), e); 1729 throw e; 1730 } 1731 finally 1732 { 1733 JDBCUtil.safeClose(rs); 1734 JDBCUtil.safeClose(duplicatePkPs); 1735 JDBCUtil.safeClose(insertPs); 1736 JDBCUtil.safeClose(con); 1737 } 1738 } 1739 1740 private Object loadField(int i) 1741 { 1742 JDBCCMPFieldBridge2 field = (JDBCCMPFieldBridge2)entity.getFields().get(i); 1743 1744 StringBuffer query = new StringBuffer (); 1745 query.append("select ") 1746 .append(field.getColumnName()) 1747 .append(" from ") 1748 .append(tableName) 1749 .append(" where "); 1750 1751 JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[])entity.getPrimaryKeyFields(); 1752 for(int pkI = 0; pkI < pkFields.length; ++pkI) 1753 { 1754 if(pkI > 0) 1755 { 1756 query.append(" and "); 1757 } 1758 query.append(pkFields[pkI].getColumnName()).append("=?"); 1759 } 1760 1761 if(log.isDebugEnabled()) 1762 { 1763 log.debug("executing: " + query.toString()); 1764 } 1765 1766 Object value = null; 1767 Connection con = null; 1768 PreparedStatement ps = null; 1769 ResultSet rs = null; 1770 1771 try 1772 { 1773 con = dataSource.getConnection(); 1774 ps = con.prepareStatement(query.toString()); 1775 1776 for(int pkI = 0; pkI < pkFields.length; ++pkI) 1777 { 1778 JDBCCMPFieldBridge2 pkField = pkFields[pkI]; 1779 Object fieldValue = fields[pkField.getRowIndex()]; 1780 pkField.setArgumentParameters(ps, pkI + 1, fieldValue); 1781 } 1782 1783 rs = ps.executeQuery(); 1784 1785 if(!rs.next()) 1786 { 1787 throw new NoSuchEntityException ("Row not found: " + pk); 1788 } 1789 1790 value = field.loadArgumentResults(rs, 1); 1791 } 1792 catch(SQLException e) 1793 { 1794 throw new EJBException ("Failed to load field " + 1795 entity.getEntityName() + "." + field.getFieldName() + 1796 ": " + e.getMessage(), e); 1797 } 1798 finally 1799 { 1800 JDBCUtil.safeClose(rs); 1801 JDBCUtil.safeClose(ps); 1802 JDBCUtil.safeClose(con); 1803 } 1804 1805 fields[field.getRowIndex()] = value; 1806 return value; 1807 } 1808 } 1809 1810 public static interface CommitStrategy 1811 { 1812 void executeUpdate(PreparedStatement ps) throws SQLException ; 1813 1814 void executeBatch(PreparedStatement ps) throws SQLException ; 1815 } 1816 1817 private static final CommitStrategy BATCH_UPDATE = new CommitStrategy() 1818 { 1819 public void executeUpdate(PreparedStatement ps) throws SQLException 1820 { 1821 ps.addBatch(); 1822 } 1823 1824 public void executeBatch(PreparedStatement ps) throws SQLException 1825 { 1826 int[] updates = ps.executeBatch(); 1827 for(int i = 0; i < updates.length; ++i) 1828 { 1829 int status = updates[i]; 1830 if(status != 1 && status != -2 ) 1831 { 1832 String msg = (status == -3 ? 1833 "One of the commands in the batch failed to execute" : 1834 "Each command in the batch should update exactly 1 row but " + 1835 "one of the commands updated " + updates[i] + " rows."); 1836 throw new EJBException (msg); 1837 } 1838 } 1839 } 1840 }; 1841 1842 private static final CommitStrategy NON_BATCH_UPDATE = new CommitStrategy() 1843 { 1844 public void executeUpdate(PreparedStatement ps) throws SQLException 1845 { 1846 int rows = ps.executeUpdate(); 1847 if(rows != 1) 1848 { 1849 throw new EJBException ("Expected one updated row but got: " + rows); 1850 } 1851 } 1852 1853 public void executeBatch(PreparedStatement ps) 1854 { 1855 } 1856 }; 1857 1858 public class ForeignKeyConstraint 1859 { 1860 public final int index; 1861 private final String nullFkSql; 1862 1863 public ForeignKeyConstraint(int index, JDBCCMPFieldBridge2[] fkFields) 1864 { 1865 this.index = index; 1866 1867 StringBuffer buf = new StringBuffer (); 1868 buf.append("update ").append(tableName).append(" set ") 1869 .append(fkFields[0].getColumnName()).append("=null"); 1870 for(int i = 1; i < fkFields.length; ++i) 1871 { 1872 buf.append(", ").append(fkFields[i].getColumnName()).append("=null"); 1873 } 1874 1875 buf.append(" where "); 1876 JDBCCMPFieldBridge2[] pkFields = (JDBCCMPFieldBridge2[]) entity.getPrimaryKeyFields(); 1877 buf.append(pkFields[0].getColumnName()).append("=?"); 1878 for(int i = 1; i < pkFields.length; ++i) 1879 { 1880 buf.append(" and ").append(pkFields[i].getColumnName()).append("=?"); 1881 } 1882 1883 nullFkSql = buf.toString(); 1884 if(log.isDebugEnabled()) 1885 { 1886 log.debug("update foreign key sql: " + nullFkSql); 1887 } 1888 } 1889 } 1890} 1891 | Popular Tags |