This Bugzilla instance is deprecated, and most Eclipse projects now use GitHub or Eclipse GitLab. Please see the deprecation plan for details.
Bug 284147 - inheritance tests failed to deploy
Summary: inheritance tests failed to deploy
Status: RESOLVED FIXED
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: Sun Solaris
: P2 normal (vote)
Target Milestone: ---   Edit
Assignee: Nobody - feel free to take it CLA
QA Contact:
URL: http://wiki.eclipse.org/EclipseLink/D...
Whiteboard:
Keywords:
Depends on:
Blocks: 266912 338140
  Show dependency tree
 
Reported: 2009-07-21 11:18 EDT by Sherry Hill CLA
Modified: 2022-06-09 10:28 EDT (History)
3 users (show)

See Also:


Attachments
Handle PK Field for MappedSuperclass - Option 1 - add in stage 1 and delete later in stage 2 - for reference only (9.49 KB, patch)
2009-08-12 12:25 EDT, Michael OBrien CLA
no flags Details | Diff
Handle PK Field for MappedSuperclass - Option 3 - search for future IdAccessor and add only if not found in stage 1 - recommended (8.73 KB, patch)
2009-08-12 12:26 EDT, Michael OBrien CLA
no flags Details | Diff
Handle PK Field for MappedSuperclass - Option 3b - call hasIdAccessor to check for future IdAccessor and add only if not found in stage 1 - no Iteration - reviewed by Guy - recommended (10.99 KB, patch)
2009-08-12 14:02 EDT, Michael OBrien CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Sherry Hill CLA 2009-07-21 11:18:59 EDT
Build ID: v20090713

Steps To Reproduce:
Test passed in M4, but failed in M5.
BaseAccount
--Account(abstract  Entity)
  --BankAccount(abstractMappedSuperclass)
    --CheckingAccount(Entity)
BaseClient
  --Client(MappedSuperclass)
    --DummyClient(Entity)
      --VIPClient (Entity)
Client and Account have 1xM relationship as

Client.java
@MappedSuperclass
public class Client extends BaseClient
   implements java.io.Serializable {

    private String clientId;
    Collection<Account> accounts = new Vector<Account>();  

    @Id
    @Column(length=55)
    public String getClientId () {
        return this.clientId;
    }
    public void setClientId (String v) {
       this.clientId = v;
    }
  
    @OneToMany (fetch = FetchType.EAGER)
    @JoinTable(name="CLIENT_ACCT",
        joinColumns=@JoinColumn(name="C_ID", referencedColumnName="CLIENTID"),
        inverseJoinColumns=@JoinColumn(name="A_ID", referencedColumnName="ACCTNUM")
        )
    public Collection<Account> getAccounts() {
         return this.accounts;
    }
    public void setAccounts(Collection<Account> v) {
    this.accounts = v;
    }
    public void addAccount(Account account){
         accounts.add(account);
         incrementAccountCount();
    }

Deployment error in server.log mentioned about composite primary key and referenceColumnName.   However, the composite primary key is not used in Client and Account,  and referenceColumnName is specified.

[#|2009-07-15T07:40:39.305-0700|SEVERE|glassfish|javax.enterprise.system.core.com.sun.enterprise.v3.server|_ThreadID=17;_ThreadName=Thread-1;|Exception
while invoking class org.glassfish.persistence.jpa.JPADeployer prepare method
javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse
Persistence Services - 2.0.0.v20090713-r4647):
org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [inherit] failed.
Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services -
2.0.0.v20090713-r4647): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The @JoinColumns on the annotated element [method
getAccounts] from the entity class [class ejb30.inherit.entity.Client] is
incomplete. When the source entity class uses a composite primary key, a
@JoinColumn must be specified for each join column using the @JoinColumns. Both
the name and the referenceColumnName elements must be specified in each such
@JoinColumn.
       at
org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:879)


More information:

The test source code are in the attachment of
https://glassfish.dev.java.net/issues/show_bug.cgi?id=8734
Comment 1 Mitesh Meswani CLA 2009-07-21 15:50:07 EDT
Adjusting priority to P2 for 2.0 release as per http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines
Comment 2 Michael OBrien CLA 2009-07-24 14:54:57 EDT
>synopsis: Metamodel processing (fake PK field for MappedSuperclass) is leaking into real Metadata processing where a PK field already exists - we end up with a composite field (one real, one fake) on a OneToMany
- fixing as part of bug# 266912
Comment 3 Michael OBrien CLA 2009-07-24 14:56:34 EDT
>There are several design issues I will need to answer as part of this fix
http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_49:_20090723:_OneToMany_on_MappedSuperclass_with_JoinTable_causes_Composite_PK_in_error
Comment 4 Michael OBrien CLA 2009-07-27 16:24:32 EDT
>All new JPA 2.0 code modifications are categorized as enhancement until the JPA 2.0 RI is released.
Comment 5 Michael OBrien CLA 2009-08-10 15:58:45 EDT
>Starting reproduction
Comment 6 Michael OBrien CLA 2009-08-11 17:02:51 EDT
>Status: Failure reproduced, Solution fixes issue, patch and review pending

http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_49:_20090723:_OneToMany_on_MappedSuperclass_with_JoinTable_causes_Composite_PK_in_error

DI 49: 20090723: OneToMany on MappedSuperclass with JoinTable causes Composite PK in error 

Here, because of the fake RelationalDescriptor PK name for MappedSuperclasses added a month ago, there is a small issue arising for @OneToMany mappings on a MappedSuperclass with a JoinTable where the Fake PK is being added on top of the existing PK in the PK fields list. 
The result of this is that some of the Metamodel processing changes PK is leaking into the real world Metamodel processing code. 

> Reproduction:
Adding the following unidirectional @OneToMany to a MappedSuperclass that defines an @Id like Person (inherited by the MappedSuperclass Corporation which is inherited by the Entity Manufacturer) 

@MappedSuperclass
public abstract class Person {
    @Id
    @GeneratedValue(strategy=TABLE, generator="PERSON_MM_TABLE_GENERATOR")
    @TableGenerator(
        name="PERSON_MM_TABLE_GENERATOR", 
        table="CMP3_MM_PERSON_SEQ", 
        pkColumnName="SEQ_MM_NAME", 
        valueColumnName="SEQ_MM_COUNT",
        pkColumnValue="CUST_MM_SEQ"
    )    
    // InstanceVariableAttributeAccessor testing
    @Column(name="PERSON_ID")    
    private Integer id;
 
 
    // Verify special handling for PK for OneToMany (custom descriptor with fake PK name)
    // If a JoinTable with a JoinColumn is used - then we need a mappedBy on the inverse side here
    // However, bidirectional relationships are not allowed to MappedSuperclasses - as they have no identity
    // This @OneToMany implements internally as a @ManyToMany
    @OneToMany(fetch=EAGER, cascade=ALL)
    @JoinTable(name="CMP3_MM_HIST_EMPLOY", 
                joinColumns = @JoinColumn(name="PERSON_ID", referencedColumnName="PERSON_ID"),
                inverseJoinColumns = @JoinColumn(name="PERSON_ID", referencedColumnName="PERSON_ID"))   
    private Collection<Manufacturer> historicalEmployers = new HashSet<Manufacturer>();

>You will see the following exception 

Caused by: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.0.0.qualifier): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The @JoinColumns on the annotated element [field historicalEmployers] from the entity class [class org.eclipse.persistence.testing.models.jpa.metamodel.Person] is incomplete. When the source entity class uses a composite primary key, a @JoinColumn must be specified for each join column using the @JoinColumns. Both the name and the referencedColumnName elements must be specified in each such @JoinColumn.
	at org.eclipse.persistence.exceptions.ValidationException.incompleteJoinColumnsSpecified(ValidationException.java:1789)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.getJoinColumnsAndValidate(MappingAccessor.java:483)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.CollectionAccessor.processJoinTable(CollectionAccessor.java:578)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.OneToManyAccessor.processManyToManyMapping(OneToManyAccessor.java:153)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.OneToManyAccessor.process(OneToManyAccessor.java:108)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.RelationshipAccessor.processRelationship(RelationshipAccessor.java:459)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processRelationshipAccessors(MetadataProject.java:1046)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage3(MetadataProject.java:1325)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor.processORMMetadata(MetadataProcessor.java:460)
	at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processORMetadata(PersistenceUnitProcessor.java:297)
	at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:849)

>This validation exception is caused by 2 defined primary keys (one defined and the other added for MappedSuperclass processing). Because this particular MappedSuperclass already defines its' own @Id we should not add the fake proceessing Id here in this use case. 

descriptor	MetadataDescriptor  (id=75)	
	m_descriptor	RelationalDescriptor  (id=74)	
		primaryKeyFields	ArrayList<E>  (id=182)	
			elementData	Object[2]  (id=195)	
				[0]	DatabaseField  (id=197)	
					name	"__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=199)	
					qualifiedName	"__PK_METAMODEL_RESERVED_IN_MEM_ONLY_FIELD_NAME" (id=199)	
					table	DatabaseTable  (id=200)	
					type	null	
					typeName	null	
				[1]	DatabaseField  (id=186)	
					name	"PERSON_ID" (id=202)	
					qualifiedName	"__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME.PERSON_ID" (id=204)	
					table	DatabaseTable  (id=151)	
					type	null	
					typeName	null	
			modCount	2	
			size	2	
	m_primaryTable	DatabaseTable  (id=151)	
		name	"__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME" (id=157)	
		qualifiedName	"__METAMODEL_RESERVED_IN_MEM_ONLY_TABLE_NAME" (id=157)
	
>The 1st (Fake> key is added in stage 1 of metadata processing 
>Note: at the time this is a valid operation becuase the PK List is empty 

Thread [main] (Suspended (breakpoint at line 465 in MetadataProject))	
	MetadataProject.addMappedSuperclassAccessor(MetadataClass, MappedSuperclassAccessor) line: 465	
	EntityAccessor.addPotentialMappedSuperclass(MetadataClass) line: 199	
	EntityAccessor.discoverMappedSuperclassesAndInheritanceParents(boolean) line: 298	
	EntityAccessor.preProcess(boolean) line: 597	
	EntityAccessor.preProcess() line: 580	
	MetadataProject.processStage1() line: 1257	
	MetadataProcessor.processORMMetadata() line: 458	
	PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 297	
	EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 849	

>The 2nd (Real) key is added in stage 2 of metadata processing 

Thread [main] (Suspended (breakpoint at line 458 in ClassDescriptor))	
	RelationalDescriptor(ClassDescriptor).addPrimaryKeyField(DatabaseField) line: 458	
	MetadataDescriptor.addPrimaryKeyField(DatabaseField, MappingAccessor) line: 331	
	IdAccessor.process() line: 82	
	MetadataDescriptor.processAccessors(MetadataDescriptor) line: 1242	
	MappedSuperclassAccessor(ClassAccessor).processAccessors() line: 787	
	MappedSuperclassAccessor.processMetamodelDescriptor() line: 883	
	MetadataProject.processStage2() line: 1285	
	MetadataProcessor.processORMMetadata() line: 459	
	PersistenceUnitProcessor.processORMetadata(MetadataProcessor, boolean) line: 297	
	EntityManagerSetupImpl.predeploy(PersistenceUnitInfo, Map) line: 849	

> Q1) Do we need the inverseJoinColumns annotation
It would seem not, because bidirectional (using mappedBy) is not allowed on a mappedSuperclass 

> Q2) How do we define the join table columns
All the concrete entities must then define the @OneToMany fully with both joinColumns and inverseJoinColums 

>Solution: 
The simple solution would be - when there is an existing key, do not add another key. 
However, the real key is added after we have customized the descriptor by adding a fake key to allow MappedSuperclass descriptors to process. 

-----------------------------
>The full solution is to check the PK List for our custom MappedSuperclass fake key and remove it before adding the real one. 

>An even better solution would be to not add the "fake" key by checking that we "will" be adding a PK in the future - by possibly parsing the mappings in the stage 1 call

Note: The following use cases are covered by this operation. 
  UC1: The 2nd key is another fake key - we end up with a single one which is ok 
  UC2: The 2nd key is a real key - we delete the fake one only 
  UC3: The nth key is part of a composite key - we do not affect any existing composite keys already in the PK List 
Note: The following use cases may not be covered 
  UCA: Two threads attempt to modify the PK List in an un-Synchronized state 
This UC may not be valid 

>Code: Rough POC
MetadataDescriptor.java
    public void addPrimaryKeyField(DatabaseField field, MappingAccessor accessor) {
// New code start
        /**
         *  266912: For MappedSuperclass processing, if a fake PK already exists,
         *  delete the existing transient key uses solely by non-relational metamodel processing
         *  before adding the real one.
         *  In the case that this key is part of a real composite key, 
         *  the pk name check will still allow multiple keys to be added.
         */
        // Iterate the existing fields and remove the first fake PK used for metamodel processing.
        List<DatabaseField> pkList = m_descriptor.getPrimaryKeyFields();
        // This iteration is self modifying so keep it out of an Iterator
        for(int i=0; i<pkList.size(); i++) {
            if(pkList.get(i).getName().equals(MetadataConstants.MAPPED_SUPERCLASS_RESERVED_PK_NAME)) {
                pkList.remove(i);
                // exit loop
                break;
                //i=pkList.size() + 1;
            }
        }
// New code end
        m_descriptor.addPrimaryKeyField(field);
 
        // Store the primary primary key mappings based on their field name.
        m_primaryKeyAccessors.put(field.getName(), accessor);
    }


>I will post a patch shortly after cleaning up the metadata code and running regression tests as well as adding a test that fails on a non-existent compositePK before the change.
Comment 7 Michael OBrien CLA 2009-08-12 12:25:25 EDT
Created attachment 144256 [details]
Handle PK Field for MappedSuperclass - Option 1 - add in stage 1 and delete later in stage 2 - for reference only

>See option 1 (not recommended)
http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#Implementation_Option_1:_Remove_pseudo_PK_field_from_relational_descriptor_later_in_stage_2
Comment 8 Michael OBrien CLA 2009-08-12 12:26:59 EDT
Created attachment 144257 [details]
Handle PK Field for MappedSuperclass - Option 3 - search for future IdAccessor and add only if not found in stage 1 - recommended

>See option 3 (recommended)
http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#Implementation_Option_3:_Determine_if_there_will_be_1_or_more_pk_fields_before_adding_pseudo_PK_field_in_stage_1
Comment 9 Michael OBrien CLA 2009-08-12 14:02:38 EDT
Created attachment 144275 [details]
Handle PK Field for MappedSuperclass - Option 3b - call hasIdAccessor to check for future IdAccessor and add only if not found in stage 1 - no Iteration - reviewed by Guy - recommended
Comment 10 Michael OBrien CLA 2009-08-12 14:45:47 EDT
>See rev# 4845
Reviewed by: Guy (with refactor assistance for option 3)
http://fisheye2.atlassian.com/changelog/eclipselink/?cs=4845
Comment 11 Peter Krogh CLA 2009-08-26 09:57:30 EDT
Mass update to change fixed in target.
Comment 12 Peter Krogh CLA 2009-08-26 10:00:10 EDT
Mass update to change fixed in target.
Comment 13 Peter Krogh CLA 2009-08-26 10:05:28 EDT
Mass update to change fixed in target.
Comment 14 Peter Krogh CLA 2009-08-26 10:07:20 EDT
Mass update to change fixed in target.
Comment 15 Eclipse Webmaster CLA 2022-06-09 10:28:50 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink