Index: entity-persistence/src/java/oracle/toplink/essentials/internal/queryframework/ExpressionQueryMechanism.java =================================================================== RCS file: /cvs/glassfish/entity-persistence/src/java/oracle/toplink/essentials/internal/queryframework/ExpressionQueryMechanism.java,v retrieving revision 1.24 diff -c -w -r1.24 ExpressionQueryMechanism.java *** entity-persistence/src/java/oracle/toplink/essentials/internal/queryframework/ExpressionQueryMechanism.java 19 Mar 2007 12:54:06 -0000 1.24 --- entity-persistence/src/java/oracle/toplink/essentials/internal/queryframework/ExpressionQueryMechanism.java 31 Mar 2007 15:06:30 -0000 *************** *** 1284,1289 **** --- 1284,1320 ---- return deleteStatements; } + /** + * Build delete statements with temporary table for ManyToMany and DirectCollection mappings. + * @return Vector + */ + protected Vector buildDeleteAllStatementsForMappingsWithTempTable(ClassDescriptor descriptor, DatabaseTable rootTable, Collection rootTablePrimaryKeyFields, boolean dontCheckDescriptor) { + Vector deleteStatements = new Vector(); + Iterator itMappings = descriptor.getMappings().iterator(); + while(itMappings.hasNext()) { + DatabaseMapping mapping = (DatabaseMapping)itMappings.next(); + if(mapping.isManyToManyMapping() || mapping.isDirectCollectionMapping()) { + if(dontCheckDescriptor || mapping.getDescriptor().equals(descriptor)) { + Vector sourceFields = null; + Vector targetFields = null; + if(mapping.isManyToManyMapping()) { + sourceFields = ((ManyToManyMapping)mapping).getSourceKeyFields(); + targetFields = ((ManyToManyMapping)mapping).getSourceRelationKeyFields(); + } else if(mapping.isDirectCollectionMapping()) { + sourceFields = ((DirectCollectionMapping)mapping).getSourceKeyFields(); + targetFields = ((DirectCollectionMapping)mapping).getReferenceKeyFields(); + } + + DatabaseTable targetTable = ((DatabaseField)targetFields.firstElement()).getTable(); + SQLDeleteAllStatementForTempTable deleteStatement + = buildDeleteAllStatementForTempTable(rootTable, rootTablePrimaryKeyFields, targetTable, targetFields); + deleteStatements.addElement(deleteStatement); + } + } + } + return deleteStatements; + } + protected SQLSelectStatement createSQLSelectStatementForModifyAll(Expression whereClause) { return createSQLSelectStatementForModifyAll(whereClause, null, getDescriptor(), false); } *************** *** 2058,2066 **** --- 2089,2102 ---- super.prepareUpdateAll(); } + /** + * Build SQLStatements for delete all using temporary table. + * @return Vector + */ protected Vector buildStatementsForDeleteAllForTempTables() { Vector statements = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(); + // retrieve rootTable and its primary key fields for composing temporary table DatabaseTable rootTable = (DatabaseTable)getDescriptor().getMultipleTableInsertOrder().firstElement(); Collection rootTablePrimaryKeyFields = getPrimaryKeyFieldsForTable(rootTable); ClassDescriptor rootDescriptor = getDescriptor(); *************** *** 2078,2100 **** // statements will be executed in reverse order SQLDeleteAllStatementForTempTable cleanupStatement = new SQLDeleteAllStatementForTempTable(); cleanupStatement.setMode(SQLModifyAllStatementForTempTable.CLEANUP_TEMP_TABLE); cleanupStatement.setTable(rootTable); statements.addElement(cleanupStatement); ! Iterator itTables = getDescriptor().getMultipleTableInsertOrder().iterator(); ! while(itTables.hasNext()) { ! DatabaseTable table = (DatabaseTable)itTables.next(); ! SQLDeleteAllStatementForTempTable deleteStatement = new SQLDeleteAllStatementForTempTable(); ! deleteStatement.setMode(SQLModifyAllStatementForTempTable.UPDATE_ORIGINAL_TABLE); ! deleteStatement.setTable(rootTable); ! deleteStatement.setPrimaryKeyFields(rootTablePrimaryKeyFields); ! deleteStatement.setTargetTable(table); ! deleteStatement.setTargetPrimaryKeyFields(getPrimaryKeyFieldsForTable(table)); ! statements.addElement(deleteStatement); ! } SQLSelectStatement selectStatement = createSQLSelectStatementForModifyAllForTempTable(null); SQLCall selectCall = (SQLCall)selectStatement.buildCall(getSession()); SQLDeleteAllStatementForTempTable insertStatement = new SQLDeleteAllStatementForTempTable(); --- 2114,2130 ---- // statements will be executed in reverse order + // statement for temporary table cleanup (Drop table or Delete from temp_table) SQLDeleteAllStatementForTempTable cleanupStatement = new SQLDeleteAllStatementForTempTable(); cleanupStatement.setMode(SQLModifyAllStatementForTempTable.CLEANUP_TEMP_TABLE); cleanupStatement.setTable(rootTable); statements.addElement(cleanupStatement); ! // delete statements using temporary table ! Vector deleteStatements = buildDeleteAllStatementsForTempTable(getDescriptor(), rootTable, rootTablePrimaryKeyFields, null); ! statements.addAll(deleteStatements); + // Insert statement populating temporary table with criteria SQLSelectStatement selectStatement = createSQLSelectStatementForModifyAllForTempTable(null); SQLCall selectCall = (SQLCall)selectStatement.buildCall(getSession()); SQLDeleteAllStatementForTempTable insertStatement = new SQLDeleteAllStatementForTempTable(); *************** *** 2105,2110 **** --- 2135,2141 ---- insertStatement.setPrimaryKeyFields(rootTablePrimaryKeyFields); statements.addElement(insertStatement); + // Create temporary table statement SQLDeleteAllStatementForTempTable createTempTableStatement = new SQLDeleteAllStatementForTempTable(); createTempTableStatement.setMode(SQLModifyAllStatementForTempTable.CREATE_TEMP_TABLE); createTempTableStatement.setTable(rootTable); *************** *** 2115,2120 **** --- 2146,2251 ---- return statements; } + /** + * Build delete all SQLStatements using temporary table. + * This is recursively called for multiple table child descriptors. + * @return Vector + */ + private Vector buildDeleteAllStatementsForTempTable(ClassDescriptor descriptor, DatabaseTable rootTable, Collection rootTablePrimaryKeyFields, Vector tablesToIgnore) { + Vector statements = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(); + + Vector tablesInInsertOrder; + if(tablesToIgnore == null) { + // It's original (not a nested) method call. + tablesInInsertOrder = descriptor.getMultipleTableInsertOrder(); + } else { + // It's a nested method call: tableInInsertOrder filled with descriptor's tables (in insert order), + // the tables found in tablesToIgnore are thrown away - + // they have already been taken care of by the caller. + // In Employee example, query with reference class Project gets here + // to handle LPROJECT table; tablesToIgnore contains PROJECT table. + tablesInInsertOrder = new Vector(descriptor.getMultipleTableInsertOrder().size()); + for (Iterator tablesEnum = descriptor.getMultipleTableInsertOrder().iterator(); + tablesEnum.hasNext();) { + DatabaseTable table = (DatabaseTable)tablesEnum.next(); + if(!tablesToIgnore.contains(table)) { + tablesInInsertOrder.addElement(table); + } + } + } + + if(!tablesInInsertOrder.isEmpty()) { + Iterator itTables = tablesInInsertOrder.iterator(); + while(itTables.hasNext()) { + DatabaseTable table = (DatabaseTable)itTables.next(); + SQLDeleteAllStatementForTempTable deleteStatement + = buildDeleteAllStatementForTempTable(rootTable, rootTablePrimaryKeyFields, table, getPrimaryKeyFieldsForTable(descriptor, table)); + statements.addElement(deleteStatement); + } + + // Add statements for ManyToMany and DirectCollection mappings + Vector deleteStatementsForMappings + = buildDeleteAllStatementsForMappingsWithTempTable(descriptor, rootTable, rootTablePrimaryKeyFields, tablesToIgnore == null); + statements.addAll(deleteStatementsForMappings); + } + + // Indicates whether the descriptor has children using extra tables. + boolean hasChildrenWithExtraTables = descriptor.hasInheritance() && descriptor.getInheritancePolicy().hasChildren() && descriptor.getInheritancePolicy().hasMultipleTableChild(); + + // TBD: should we ignore subclasses in case descriptor doesn't want us to read them in? + //** Currently in this code we do ignore. + //** If it will be decided that we need to handle children in all cases + //** the following statement should be changed to: boolean shouldHandleChildren = hasChildrenWithExtraTables; + boolean shouldHandleChildren = hasChildrenWithExtraTables && descriptor.getInheritancePolicy().shouldReadSubclasses(); + + // Perform a nested method call for each child + if(shouldHandleChildren) { + // In Employee example: query for Project will make nested calls to + // LargeProject and SmallProject and ask them to ignore PROJECT table + Vector tablesToIgnoreForChildren = new Vector(); + // The tables this descriptor has ignored, its children also should ignore. + if(tablesToIgnore != null) { + tablesToIgnoreForChildren.addAll(tablesToIgnore); + } + + // If the desctiptor reads subclasses there is no need for + // subclasses to process its tables for the second time. + if (descriptor.getInheritancePolicy().shouldReadSubclasses()) { + tablesToIgnoreForChildren.addAll(tablesInInsertOrder); + } + + Iterator it = descriptor.getInheritancePolicy().getChildDescriptors().iterator(); + while(it.hasNext()) { + ClassDescriptor childDescriptor = (ClassDescriptor)it.next(); + + // Need to process only "multiple tables" child descriptors + if ((childDescriptor.getTables().size() > descriptor.getTables().size()) || + (childDescriptor.getInheritancePolicy().hasMultipleTableChild())) + { + //recursively build for child desciptors + Vector childStatements = buildDeleteAllStatementsForTempTable(childDescriptor, rootTable, rootTablePrimaryKeyFields, tablesToIgnoreForChildren); + statements.addAll(childStatements); + } + } + } + + return statements; + } + + /** + * Build SQLDeleteAllStatementForTempTable which delete from target table using temporary table. + * @return SQLDeleteAllStatementForTempTable + */ + private SQLDeleteAllStatementForTempTable buildDeleteAllStatementForTempTable(DatabaseTable rootTable, Collection rootTablePrimaryKeyFields, DatabaseTable targetTable, Collection targetTablePrimaryKeyFields) { + SQLDeleteAllStatementForTempTable deleteStatement = new SQLDeleteAllStatementForTempTable(); + deleteStatement.setMode(SQLModifyAllStatementForTempTable.UPDATE_ORIGINAL_TABLE); + deleteStatement.setTable(rootTable); + deleteStatement.setPrimaryKeyFields(rootTablePrimaryKeyFields); + deleteStatement.setTargetTable(targetTable); + deleteStatement.setTargetPrimaryKeyFields(targetTablePrimaryKeyFields); + return deleteStatement; + } + protected Vector buildStatementsForUpdateAllForTempTables(DatabaseTable table, HashMap databaseFieldsToValues, Collection primaryKeyFields) { Vector statements = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(4); *************** *** 2186,2196 **** } protected Collection getPrimaryKeyFieldsForTable(DatabaseTable table) { Collection primaryKeyFields; ! if(table.equals(getDescriptor().getTables().firstElement())) { ! primaryKeyFields = getDescriptor().getPrimaryKeyFields(); } else { ! primaryKeyFields = ((Map)getDescriptor().getAdditionalTablePrimaryKeyFields().get(table)).values(); } return primaryKeyFields; } --- 2317,2331 ---- } protected Collection getPrimaryKeyFieldsForTable(DatabaseTable table) { + return getPrimaryKeyFieldsForTable(getDescriptor(), table); + } + + protected Collection getPrimaryKeyFieldsForTable(ClassDescriptor descriptor, DatabaseTable table) { Collection primaryKeyFields; ! if(table.equals(descriptor.getTables().firstElement())) { ! primaryKeyFields = descriptor.getPrimaryKeyFields(); } else { ! primaryKeyFields = ((Map)descriptor.getAdditionalTablePrimaryKeyFields().get(table)).values(); } return primaryKeyFields; } Index: entity-persistence-tests/src/java/oracle/toplink/essentials/testing/tests/cmp3/advanced/EntityManagerJUnitTestSuite.java =================================================================== RCS file: /cvs/glassfish/entity-persistence-tests/src/java/oracle/toplink/essentials/testing/tests/cmp3/advanced/EntityManagerJUnitTestSuite.java,v retrieving revision 1.45 diff -c -w -r1.45 EntityManagerJUnitTestSuite.java *** entity-persistence-tests/src/java/oracle/toplink/essentials/testing/tests/cmp3/advanced/EntityManagerJUnitTestSuite.java 2 Mar 2007 20:40:12 -0000 1.45 --- entity-persistence-tests/src/java/oracle/toplink/essentials/testing/tests/cmp3/advanced/EntityManagerJUnitTestSuite.java 31 Mar 2007 15:06:33 -0000 *************** *** 3099,3109 **** try { em.createQuery("DELETE FROM "+className+" p WHERE p.name = '"+name+"' AND p.teamLeader IS NULL").executeUpdate(); em.getTransaction().commit(); ! } catch (Exception e) { if (em.getTransaction().isActive()){ em.getTransaction().rollback(); } ! fail("Exception thrown: " + e.getClass()); } finally { em.close(); } --- 3099,3109 ---- try { em.createQuery("DELETE FROM "+className+" p WHERE p.name = '"+name+"' AND p.teamLeader IS NULL").executeUpdate(); em.getTransaction().commit(); ! } catch (RuntimeException e) { if (em.getTransaction().isActive()){ em.getTransaction().rollback(); } ! throw e; } finally { em.close(); }