Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 351602 - Privately owned object removed from its owner is not deleted
Summary: Privately owned object removed from its owner is not deleted
Status: CLOSED FIXED
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 354813 (view as bug list)
Depends on:
Blocks:
 
Reported: 2011-07-08 14:14 EDT by Andrei Ilitchev CLA
Modified: 2022-06-09 10:08 EDT (History)
1 user (show)

See Also:


Attachments
Suggested patch. (19.21 KB, patch)
2011-07-08 16:02 EDT, Andrei Ilitchev CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Andrei Ilitchev CLA 2011-07-08 14:14:03 EDT
To reproduce need a class model with private ownership in the base class and at least two level inheritance.

Class A privately owns D (ToOne or ToMany);
Class B inherits from A;
Class C inherit from B.

To reproduce:
Setup:
  create and instance of C that has an instance of D;
Test:
  remove the instance of D from its owner C, commit.
Result:
  The instance of D is still around - both in the shared cache and in the database.
Expected result:
  As a privately owned object removed from its master the instance of D should have been deleted.
Comment 1 Andrei Ilitchev CLA 2011-07-08 15:59:30 EDT
The problem is in InheritancePolicy.postInitialize method: its called for a child before being called for its parent.

Therefore all the following three assignments fail for C: 

if (parent.hasPreDeleteMappings()) {
getDescriptor().getPreDeleteMappings().addAll(parent.getPreDeleteMappings());
}
if (parent.hasMappingsPostCalculateChanges()) {
getDescriptor().getMappingsPostCalculateChanges().addAll(parent.getMappingsPostCalculateChanges());
}
if (parent.hasMappingsPostCalculateChangesOnDeleted()) {
getDescriptor().getMappingsPostCalculateChangesOnDeleted().addAll(parent.getMappingsPostCalculateChangesOnDeleted());
}

When they are called for C, they copy settings from B - but nothing yet set on B.
Next the method is called for B - and the settings are copied from A to B ok - but they never copied to C.


It's getMappingsPostCalculateChanges that's responsible for the bug, however all three should be assignments should be fixed - they should be performed in "top - down" order (parent first, then child).

Workaround:
public void postLogin(SessionEvent event) {
    Session session = event.getSession();
    Collection<ClassDescriptor> descriptors = session.getDescriptors().values();
    for (ClassDescriptor descriptor : descriptors) {
        // process descriptors for base classes
        if (descriptor.hasInheritance() && !descriptor.getInheritancePolicy().isChildDescriptor()) {
            copyToChildren(descriptor);
        }
    }
}

public static void copyToChildren(ClassDescriptor parentDescriptor) {
    List<ClassDescriptor> childDescriptors = parentDescriptor.getInheritancePolicy().getChildDescriptors();
    if (childDescriptors != null) {
    for (ClassDescriptor childDescriptor : childDescriptors) {
        if (parentDescriptor.hasPreDeleteMappings()) {
            for (DatabaseMapping mapping : parentDescriptor.getPreDeleteMappings()) {
                if (!childDescriptor.getPreDeleteMappings().contains(mapping)) {
                    childDescriptor.getPreDeleteMappings().add(mapping);
                }
            }
        }
        if (parentDescriptor.hasMappingsPostCalculateChanges()) {
            for (DatabaseMapping mapping : parentDescriptor.getMappingsPostCalculateChanges()) {
                if (!childDescriptor.getMappingsPostCalculateChanges().contains(mapping)) {
                    childDescriptor.getMappingsPostCalculateChanges().add(mapping);
                }
            }
        }
        if (parentDescriptor.hasMappingsPostCalculateChangesOnDeleted()) {
            for (DatabaseMapping mapping : parentDescriptor.getMappingsPostCalculateChangesOnDeleted()) {
                if (!childDescriptor.getMappingsPostCalculateChangesOnDeleted().contains(mapping)) {
                    childDescriptor.getMappingsPostCalculateChangesOnDeleted().add(mapping);
                }
            }
        }
        copyToChildren(childDescriptor);
    }
    }
}
Comment 2 Andrei Ilitchev CLA 2011-07-08 16:02:34 EDT
Created attachment 199359 [details]
Suggested patch.

The three assignments are moved from InheritancePolicy.postInitialize to ClassDescriptor.initialize method (those are called for parent first, then for child).
RemovePrivatelyOwnedTestCase is added to InsuranceBasicTestModel
(in getDeletePrivateOwnedTestSuite).
Comment 3 Andrei Ilitchev CLA 2011-07-11 12:09:35 EDT
Reviewed by Chris.
Checked the fix into both 2.3.1 and 2.4.
Comment 4 Andrei Ilitchev CLA 2011-07-21 12:23:10 EDT
The workaround provided above is a SessionEventListener - not a session customizer:

public class MySessionEventListener extends org.eclipse.persistence.sessions.SessionEventAdaptor {
  public void postLogin(SessionEvent event) {
    ...
  }
  public static void copyToChildren(ClassDescriptor parentDescriptor) {
    ...
  }
}

SessionEventAdapter should be passed to pu through a property:
<property name="eclipselink.session-event-listener" value="MySessionEventListener"/>
Comment 5 Guy Pelletier CLA 2011-09-22 15:56:14 EDT
*** Bug 354813 has been marked as a duplicate of this bug. ***
Comment 6 Eclipse Webmaster CLA 2022-06-09 10:08:31 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink