Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 351602

Summary: Privately owned object removed from its owner is not deleted
Product: z_Archived Reporter: Andrei Ilitchev <andrei.ilitchev>
Component: EclipselinkAssignee: Project Inbox <eclipselink.foundation-inbox>
Status: CLOSED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: tor.jarnbjo.ext
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Windows XP   
Whiteboard:
Attachments:
Description Flags
Suggested patch. none

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