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

Bug 345839

Summary: Extend the policy to support relationships to non-primary key columns in derived identies
Product: z_Archived Reporter: Karsten Wutzke <kwutzke>
Component: EclipselinkAssignee: Nobody - feel free to take it <nobody>
Status: NEW --- QA Contact:
Severity: enhancement    
Priority: P3    
Version: unspecified   
Target Milestone: ---   
Hardware: All   
OS: All   
URL: http://www.eclipse.org/forums/index.php/m/669746/
Whiteboard:
Attachments:
Description Flags
Test case: JavaSE, EclipseLink 2.3.0 M7, HSQLDB 2 none

Description Karsten Wutzke CLA 2011-05-15 07:07:44 EDT
Build Identifier: EclipseLink 2.3.0 M7 (R9282)

Here's the design:

http://www.kawoolutions.com/media/countries-zips.png

Here's the DDL used (HSQLDB ISO/ANSI):

CREATE TABLE Countries
(
  id INTEGER NOT NULL,
  iso_code CHAR(2) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE (iso_code)
);

CREATE TABLE Zips
(
  country_code CHAR(2) NOT NULL,
  code VARCHAR(10) NOT NULL,
  PRIMARY KEY (country_code, code),
  CONSTRAINT zips_countries_fk
    FOREIGN KEY (country_code)
    REFERENCES Countries (iso_code)
    ON DELETE NO ACTION
    ON UPDATE CASCADE
);

Here are the mappings:

@Entity
@Table(name = "Countries")
public class Country implements Serializable
{
	@Id
	@Column(name = "id")
	private Integer id;

	@Column(name = "iso_code")
	private String isoCode;

	...
}

@Entity
@Table(name = "Zips")
public class Zip implements Serializable
{
	@EmbeddedId
	private ZipId embeddedId;

	@MapsId(value = "countryCode")
	@ManyToOne
	@JoinColumn(name = "country_code", referencedColumnName = "iso_code")
	private Country country;

	...
}

@Embeddable
public class ZipId implements Serializable
{
	@Column(name = "country_code", insertable = false, updatable = false)
	private String countryCode;

	@Column(name = "code")
	private String code;

	...
}

Problem:

The @MapsId represents a single-column relationship to another entity's non-primary key column (JPA 2.0). The JPA (1.0 + 2.0) only supports relationships to primary key columns in derived identies.

However, also due to the requirement of inheritance usually forcing a common ID onto the sub entities, other entities referencing the (sub) entities that use surrogate/artificial IDs can't define primary keys constructed from (single- or multi-column) non-primary key columns. The current policy - essentially a weakness of JPA - forces developers to continue to use the meaningless ID, even though a good natural key exists. This really limits DB design.

There should be a mechanism to "return" to the non-primary key attributes for referencing entities. EL should allow relationships to non-PK columns in derived identifiers - @IdClass and @EmbeddedId implementations alike.

Analysis:

Note that referencing multiple non-primary key columns would mean a reference to an ID class of an alternative/natural key that the class doesn't necessarily have (JPA 2.0 may nest ID classes). As the referenced entity class will already have either an @Id or an @IdClass/@EmbeddedId representing the PK, multi-column relationships to alternative keys would require another ID class, which may collide with existing ID classes.

The restriction to be put upon here would probably be to only allow references to multiple non-PK columns in entities that have a single-column PK. I don't know the details here, or whether that's possible at all.

Maybe this has to be limited to single-column relationships to non-PK columns.

Deeper analsysis is definitely required.

Reproducible: Always

Steps to Reproduce:
1. See test case
2.
3.
Comment 1 Karsten Wutzke CLA 2011-05-15 07:50:25 EDT
The stack trace when using

		EntityManagerFactory emf = Persistence.createEntityManagerFactory("geoareas");
		em = emf.createEntityManager();
		
		System.out.println();
		System.out.println("Entity manager created!");
		System.out.println();
		
		Country co = em.find(Country.class, 1);
		System.out.println("Loaded country = " + co);
		
		Zip zi = em.find(Zip.class, new ZipId("DE", "64846"));
		System.out.println("Loaded zip = " + zi);

is:

Loaded country = tld.geoareas.model.Country@60cbf9bd[id=1,isoCode=AF]
Exception in thread "main" java.lang.IllegalArgumentException: You have provided an instance of an incorrect PK class for this find operation.  Class expected : class java.lang.Integer, Class received : class java.lang.String.
    at org.eclipse.persistence.internal.jpa.CMP3Policy.createPrimaryKeyFromId(CMP3Policy.java:239)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.findInternal(EntityManagerImpl.java:699)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:621)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:500)
    at tld.geoareas.Main.main(Main.java:38)
Comment 2 Karsten Wutzke CLA 2011-05-15 07:53:30 EDT
Tested with EL 2.2.0 nightly (R8626) and EL 2.3.0 M7 (R9282) with the same exception. (2.3.0 not yet selectable from dropdown)
Comment 3 Karsten Wutzke CLA 2011-05-15 08:03:08 EDT
Created attachment 195661 [details]
Test case: JavaSE, EclipseLink 2.3.0 M7, HSQLDB 2

eclipselink.jar and javax.persistence_2.0.3.v201010191057.jar must be copied to the project's lib dir manually.
Comment 4 Karsten Wutzke CLA 2011-05-16 12:58:22 EDT
For more information about relationships to non-PK columns see:
http://www.kawoolutions.com/Technology/JPA,_Hibernate,_and_Co./Relationships_to_Non-Primary_Key_Columns
Comment 5 Eclipse Webmaster CLA 2022-06-09 10:34:15 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink