users@glassfish.java.net

Error cascading delete to child item (_at_OneToMany relationship)

From: <glassfish_at_javadesktop.org>
Date: Mon, 11 Jun 2007 18:07:30 PDT

Hi all. I'm getting this error using the latest release of Glassfish Persistence (using Derby as the underlying database). I have an object called CachedItem, to which I've added a Collection of CachedItemReference objects. These represents any items that the CachedItem object reference. I've added the following methods to the CachedItem object

    @OneToMany(mappedBy="cachedItem", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
    public Collection<CachedItemReference> getReferencedItems() {
        if (referencedItems == null) {
            referencedItems = new java.util.HashSet<CachedItemReference>();
        }
        return referencedItems;
    }

    public void setReferencedItems(final Collection<CachedItemReference> references) {
        Collection<CachedItemReference> existingRefs = getReferencedItems();
        if (!(existingRefs.isEmpty())) {
            final Collection<CachedItemReference> clone
                    = new java.util.HashSet<CachedItemReference>(existingRefs);
            for (CachedItemReference reference : clone) {
                removeReference(reference);
            }
        }
        
        for (CachedItemReference reference : references) {
            addReference(reference);
        }
    }

    public void addReference(CachedItemReference reference) {
        getReferencedItems().add(reference);
        reference.setCachedItem(this);
    }

    public void removeReference(CachedItemReference reference) {
        getReferencedItems().remove(reference);
        reference.setCachedItem(null);
    }

The CachedItemReference object simply has the following method defined

    @ManyToOne
    public CachedItem getCachedItem() {
        return cachedItem;
    }


I've made these changes, then execute the new code against the existing test cases. We have some test cases that create, update and delete the CachedItem objects. When we attempt to delete the CachedItem object, the following exception is thrown:

Local Exception Stack:
Exception [TOPLINK-4002] (Oracle TopLink Essentials - 2.0 (Build b50-beta3 (06/07/2007))): oracle.toplink.essentials.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: DELETE on table 'CACHED_ITEM' caused a violation of foreign key constraint 'CCHDTMRFRNCCCHDTMD' for key (1). The statement has been rolled back.
Error Code: 20000
Call: DELETE FROM REPLIB.CACHED_ITEM WHERE NOT EXISTS(SELECT t0.ID FROM REPLIB.CACHED_ITEM t0, REPLIB.CACHED_ITEM_VALUE t1 WHERE (t1.ID = t0.ID) AND t0.ID = REPLIB.CACHED_ITEM.ID)
Query: DeleteAllQuery()
        at oracle.toplink.essentials.exceptions.DatabaseException.sqlException(DatabaseException.java:311)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:654)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:703)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:492)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:452)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeCall(AbstractSession.java:690)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
        at oracle.toplink.essentials.internal.queryframework.DatasourceCallQueryMechanism.deleteAll(DatasourceCallQueryMechanism.java:117)
        at oracle.toplink.essentials.queryframework.DeleteAllQuery.executeDatabaseQuery(DeleteAllQuery.java:198)
        at oracle.toplink.essentials.queryframework.DatabaseQuery.execute(DatabaseQuery.java:628)
        at oracle.toplink.essentials.queryframework.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:555)
        at oracle.toplink.essentials.queryframework.ModifyAllQuery.executeInUnitOfWork(ModifyAllQuery.java:168)
        at oracle.toplink.essentials.queryframework.DeleteAllQuery.executeInUnitOfWork(DeleteAllQuery.java:124)
        at oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2233)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:952)
        at oracle.toplink.essentials.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:924)
        at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.executeUpdate(EJBQueryImpl.java:391)
        at com.intecbilling.replib.cache.CacheService.removeAllFromFolder(CacheService.java:190)
        at com.intecbilling.replib.cache.Cache.setContentForFolders(Cache.java:364)
        at com.intecbilling.replib.CacheRepository.updateCachedItemsForFolders(CacheRepository.java:260)
        at com.intecbilling.replib.CacheRepository.updateCacheForQuery(CacheRepository.java:157)
        at com.intecbilling.replib.CacheRepository.findFiles(CacheRepository.java:274)
        at com.intecbilling.replib.CacheRepository.findFiles(CacheRepository.java:267)
        at com.intecbilling.replib.LocalConfigurationRepositoryTest.testCreateAndDelete(LocalConfigurationRepositoryTest.java:270)
Caused by: java.sql.SQLException: DELETE on table 'CACHED_ITEM' caused a violation of foreign key constraint 'CCHDTMRFRNCCCHDTMD' for key (1). The statement has been rolled back.
        at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
        at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
        at org.apache.derby.impl.jdbc.TransactionResourceImpl.wrapInSQLException(Unknown Source)
        at org.apache.derby.impl.jdbc.TransactionResourceImpl.handleException(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedConnection.handleException(Unknown Source)
        at org.apache.derby.impl.jdbc.ConnectionChild.handleException(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedStatement.executeStatement(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedPreparedStatement.executeStatement(Unknown Source)
        at org.apache.derby.impl.jdbc.EmbedPreparedStatement.executeUpdate(Unknown Source)
        at oracle.toplink.essentials.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:647)
        ... 43 more
... Removed 21 stack frames

Now, I've looked at the SQL that Derby used to create the database schema objects, and it looks like

CREATE TABLE REPLIB.CACHED_ITEM (ID INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, LAST_MODIFIED TIMESTAMP, FOLDER VARCHAR(255) NOT NULL, PARSEABLE SMALLINT DEFAULT 0, TYPE VARCHAR(255), SECURITY_GROUP VARCHAR(255), NAME VARCHAR(255) NOT NULL, REPOSITORY VARCHAR(255) NOT NULL, VERSION INTEGER, ID_ATTRIBUTE INTEGER, PRIMARY KEY (ID))
CREATE TABLE REPLIB.CONFREP_RESERVATION (ID INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, REPOSITORY VARCHAR(255) NOT NULL, SYSTEM_ID VARCHAR(255) NOT NULL, VERSION INTEGER, CONFREP VARCHAR(255) NOT NULL, PRIMARY KEY (ID))
CREATE TABLE REPLIB.CACHED_ITEM_REFERENCE (ID INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, URL VARCHAR(255) NOT NULL, CACHEDITEM_ID INTEGER, PRIMARY KEY (ID))
CREATE TABLE REPLIB.CACHE_PROPERTY (NAME VARCHAR(255) NOT NULL, VALUE VARCHAR(255) NOT NULL, VERSION INTEGER, PRIMARY KEY (NAME))
CREATE TABLE REPLIB.CACHED_ITEM_VALUE (ID INTEGER NOT NULL, VALUE BLOB, PRIMARY KEY (ID))
ALTER TABLE REPLIB.CACHED_ITEM ADD CONSTRAINT UNQ_CACHED_ITEM_0 UNIQUE (REPOSITORY, FOLDER, NAME)
ALTER TABLE REPLIB.CONFREP_RESERVATION ADD CONSTRAINT UNQ_CONFREP_RESERVATION_0 UNIQUE (REPOSITORY, SYSTEM_ID)
ALTER TABLE REPLIB.CONFREP_RESERVATION ADD CONSTRAINT UNQ_CONFREP_RESERVATION_1 UNIQUE (REPOSITORY, CONFREP)
ALTER TABLE REPLIB.CACHED_ITEM_REFERENCE ADD CONSTRAINT CCHDTMRFRNCCCHDTMD FOREIGN KEY (CACHEDITEM_ID) REFERENCES REPLIB.CACHED_ITEM (ID)
ALTER TABLE REPLIB.CACHED_ITEM_VALUE ADD CONSTRAINT CACHEDITEM_VALUEID FOREIGN KEY (ID) REFERENCES REPLIB.CACHED_ITEM (ID)


So, the constraint CCHDTMRFRNCCCHDTMD is the index for the foreign key constraint in CACHED_ITEM_REFERENCE referencing back to CACHED_ITEM table.

The test case is just removing the CachedItem object from the entity manager, and is not doing anything with the child objects themselves. It seems to be attempting to delete the parent item before deleting the child items? Is this right?

One odd thing I found was, when trying to figure out what's going on, I added a @PreRemove method to the CachedItem object, like this....

    @PreRemove
    public void preRemove() {
        System.out.println("Clearing references");
        setReferencedItems(NO_REFERENCES);
    }


Thinking I could clear the child object references myself. But this is never executed (from what I can tell)?

Am I doing something wrong? Or is this a continuation of some of the prevous bugs that were fixed in releases 45 and 47?

Thanks for any help,
Ed
[Message sent by forum member 'edh' (edh)]

http://forums.java.net/jive/thread.jspa?messageID=221607