Jump to: navigation, search

Difference between revisions of "EclipseLink/Development/JPA 2.0/mappedbyid"

(Internal processing)
(Example 2)
Line 146: Line 146:
  
 
=== Example 2 ===
 
=== Example 2 ===
 +
The parent entity uses IdClass and the dependent entity uses EmbeddedId. The type of the pk attribute is that same as that of the primary key of the parent entity.
  
 
==== Model ====
 
==== Model ====
 
<code><pre>
 
<code><pre>
 +
@Entity
 +
@Table(name="JPA_MAJOR")
 +
@IdClass(MajorId.class)
 +
public class Major {
 +
    @Id
 +
    @Column(name="F_NAME")
 +
    protected String firstName;
  
 +
    @Id
 +
    @Column(name="L_NAME")
 +
    protected String lastName;
 +
   
 +
    public String getFirstName() {
 +
        return firstName;
 +
    }
 +
 +
    public void setFirstName(String firstName) {
 +
        this.firstName = firstName;
 +
    }
 +
 +
    public void setLastName(String lastName) {
 +
        this.lastName = lastName;
 +
    }
 +
   
 +
    public String getLastName() {
 +
        return lastName;
 +
    }
 +
   
 +
    public MajorId getPK() {
 +
        return new MajorId(firstName, lastName);
 +
    }
 +
}
 +
 +
public class MajorId {
 +
    public String firstName;
 +
    public String lastName;
 +
   
 +
    public MajorId() {}
 +
   
 +
    public MajorId(String firstName, String lastName) {
 +
        this.firstName = firstName;
 +
        this.lastName = lastName;
 +
    }
 +
 +
    public boolean equals(Object other) {
 +
        if (other instanceof MajorId) {
 +
            final MajorId otherMajorId = (MajorId) other;
 +
            return (otherMajorId.firstName.equals(firstName) && otherMajorId.lastName.equals(lastName));
 +
        }
 +
       
 +
        return false;
 +
    }
 +
}
 +
 +
@Entity
 +
@Table(name="JPA_CAPTAIN")
 +
public class Captain {
 +
    @EmbeddedId
 +
    CaptainId id;
 +
   
 +
    @ManyToOne
 +
    @MappedById("majorPK")
 +
    Major major;
 +
   
 +
    public CaptainId getId() {
 +
        return id;
 +
    }
 +
   
 +
    public Major getMajor() {
 +
        return major;
 +
    }
 +
   
 +
    public void setId(CaptainId id) {
 +
        this.id = id;
 +
    }
 +
 +
    public void setMajor(Major major) {
 +
        this.major = major;
 +
        id.setMajorPK(major.getPK());
 +
    }
 +
}
 +
 +
@Embeddable
 +
public class CaptainId {
 +
    @Column(name="NAME")
 +
    String name;
 +
    MajorId majorPK;
 +
   
 +
    public String getName() {
 +
        return name;
 +
    }
 +
   
 +
    public MajorId getMajorPK() {
 +
        return majorPK;
 +
    }
 +
   
 +
    public void setName(String name) {
 +
        this.name = name;
 +
    }
 +
   
 +
    public void setMajorPK(MajorId majorPK) {
 +
        this.majorPK = majorPK;
 +
    }
 +
}
 
</pre></code>
 
</pre></code>
  
 
==== Test ====
 
==== Test ====
 
<code><pre>
 
<code><pre>
 
+
Major major = new Major();
 +
MajorId majorId;
 +
Captain captain = new Captain();
 +
CaptainId captainId = new CaptainId();
 +
       
 +
major.setFirstName("Mr.");
 +
major.setLastName("Major");
 +
majorId = major.getPK();
 +
em.persist(major);
 +
           
 +
captainId.setName("Captain Sparrow");
 +
captain.setId(captainId);
 +
captain.setMajor(major);
 +
em.persist(captain);
 +
em.commit()
 +
em.close();           
 
</pre></code>
 
</pre></code>
  

Revision as of 12:25, 24 April 2009

MappedById Support

JPA 2.0 Root | Enhancement Request

Issue Summary

In JPA 1.0 primary key columns could only be mapped to @Basic attributes. Primary Keys that were derived from Foreign Keys had to be mapped twice, once as the relationship mapping and again as an @Basic. JPA 2.0 adds the functionality to designate a Relationship as providing the primary key for an Entity, see | DerivedId . When using an embeddedid, a @MappedById annotation can be used on a relationship mapping to indicate that it uses the same Id field that is also defined by the target ID field in the Embeddedid.

See JPA 2.0 section 2.4.1 for details.

General Solution

The EmbeddedID will be difficult to support as values in the EmbeddedID will need to be populated by EclipseLink on persist(), merge() calls even though they are not the sources of Identity for the Entity. Even more complicated if the derived ID is an IdClass then an instance of that class must be stored in the EmbeddedId which will be new functionality in EclipseLink. Transformation Mapping may be leveraged in combination with the CMP3Policy to support this functionality.

Metadata processing

The metadata processing will need a number of changes to support the notion of id classes and embedded id classes within their own embedded id's. The spec provided 6 examples, so we'll work through each and discuss the metadata processing behevior and any changes needed to it.

Example 1

The parent entity has a simple primary key and the dependent entity uses EmbeddedId to represent a composite key.

Model

@Entity
@Table(name="JPA_SARGEANT")
public class Sargeant {
    @Id
    @Column(name="ID")
    @GeneratedValue(strategy=TABLE, generator="SARGEANT_TABLE_GENERATOR")
    @TableGenerator(
        name="SARGEANT_TABLE_GENERATOR", 
        table="JPA_SARGEANT_SEQ", 
        pkColumnName="SEQ_NAME", 
        valueColumnName="SEQ_COUNT",
        pkColumnValue="SARGEANT_SEQ",
        initialValue=50
    )
    long sargeantId;

    @Basic
    @Column(name="NAME")
    String name;
    
    public String getName() {
        return name;
    }
    
    public long getSargeantId() {
        return sargeantId;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void setSargeantId(long sargeantId) {
        this.sargeantId = sargeantId;
    }
}

@Entity
@Table(name="JPA_MASTER_CORPORAL")
public class MasterCorporal {
    @EmbeddedId 
    MasterCorporalId id;
    
    @ManyToOne 
    @MappedById("sargeantPK")
    Sargeant sargeant;
    
    public MasterCorporalId getId() {
        return id;
    }
    
    public Sargeant getSargeant() {
        return sargeant;
    }
    
    public void setId(MasterCorporalId id) {
        this.id = id;
    }

    public void setSargeant(Sargeant sargeant) {
        this.sargeant = sargeant;
        id.setSargeantPK(sargeant.getSargeantId());
    }
}

@Embeddable
public class MasterCorporalId {
    @Column(name="NAME")
    String name;
    
    @Column(name="SARGEANTPK")
    long sargeantPK;
    
    public String getName() {
        return name;
    }
    
    public long getSargeantPK() {
        return sargeantPK;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setSargeantPK(long sargeantPK) {
        this.sargeantPK = sargeantPK;
    }
}

Test

EntityManager em = createEntityManager();
beginTransaction(em);
        
Sargeant sargeant = new Sargeant();
MasterCorporal masterCorporal = new MasterCorporal();
MasterCorporalId masterCorporalId = new MasterCorporalId();
sargeant.setName("Sarge");
em.persist(sargeant);
            
masterCorporalId.setName("Corpie");
masterCorporal.setId(masterCorporalId);
masterCorporal.setSargeant(sargeant);
em.persist(masterCorporal);
            
commitTransaction(em);         
closeEntityManager(em);

Internal processing

When processing the @ManyToOne mapping, the mapping for sargeantPK will be retrieved from the embeddable descriptor for MasterCorporalId and the following keys added:

DatabaseField dependentField = sargeantPKMapping.getField(); // The mapping from the embeddable class
DatabaseField parentField = getReferenceDescriptor().getPrimaryKeyField(); // The primary key mapping from the Sargeant entity
manyToOneMapping.addForeignKeyField(dependentField, parentField);

// The example above will yield.
// manyToOneMapping.addForeignKeyField("SARGEANTPK", "ID");

// And to ensure the primary key class is properly initialized from the CMP3Policy
manyToOneMapping.setIsDerivedIdMapping(true);
manyToOneMapping.setMappedByIdValue("sargeantPK");

Example 2

The parent entity uses IdClass and the dependent entity uses EmbeddedId. The type of the pk attribute is that same as that of the primary key of the parent entity.

Model

@Entity
@Table(name="JPA_MAJOR")
@IdClass(MajorId.class)
public class Major {
    @Id
    @Column(name="F_NAME")
    protected String firstName;

    @Id
    @Column(name="L_NAME")
    protected String lastName;
    
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    
    public String getLastName() {
        return lastName;
    }
    
    public MajorId getPK() {
        return new MajorId(firstName, lastName);
    }
}

public class MajorId {
    public String firstName;
    public String lastName;
    
    public MajorId() {}
    
    public MajorId(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public boolean equals(Object other) {
        if (other instanceof MajorId) {
            final MajorId otherMajorId = (MajorId) other;
            return (otherMajorId.firstName.equals(firstName) && otherMajorId.lastName.equals(lastName));
        }
        
        return false;
    }
}

@Entity
@Table(name="JPA_CAPTAIN")
public class Captain {
    @EmbeddedId 
    CaptainId id;
    
    @ManyToOne 
    @MappedById("majorPK")
    Major major;
    
    public CaptainId getId() {
        return id;
    }
    
    public Major getMajor() {
        return major;
    }
    
    public void setId(CaptainId id) {
        this.id = id;
    }

    public void setMajor(Major major) {
        this.major = major;
        id.setMajorPK(major.getPK());
    }
}

@Embeddable
public class CaptainId {
    @Column(name="NAME")
    String name;
    MajorId majorPK;
    
    public String getName() {
        return name;
    }
    
    public MajorId getMajorPK() {
        return majorPK;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public void setMajorPK(MajorId majorPK) {
        this.majorPK = majorPK;
    }
}

Test

Major major = new Major();
MajorId majorId;
Captain captain = new Captain();
CaptainId captainId = new CaptainId();
        
major.setFirstName("Mr.");
major.setLastName("Major");
majorId = major.getPK();
em.persist(major);
            
captainId.setName("Captain Sparrow");
captain.setId(captainId);
captain.setMajor(major);
em.persist(captain);
em.commit()
em.close();            

Internal processing


Example 3

Model


Test


Internal processing


Example 4

Model


Test


Internal processing


Example 5

Model


Test


Internal processing


Example 6

Model


Test


Internal processing


Work Required

  1. Develop model for testing access type settings
    approx ? days
  2. Update Processing
    approx ? days