简体   繁体   中英

JPA Map Entity with PK to Entity with Composite PK

Note: I am new to JPA development, and learning trial by fire, so please excuse anything obvious I may be missing.

Here are the pertinent JPA Entities. Address isn't listed, but it's really simple entity with one @Id named id.

ShipTo.java

    package model;

    import java.io.Serializable;
    import javax.persistence.*;
    import java.math.BigDecimal;
    import java.sql.Timestamp;


    /**
     * The persistent class for the ship_to database table.
     * 
     */
    @Entity
    @Table(name="ship_to")
    @NamedQuery(name="ShipTo.findAll", query="SELECT s FROM ShipTo s")
    public class ShipTo implements Serializable {
        private static final long serialVersionUID = 1L;

    @EmbeddedId
    private ShipToPK id;

    //bi-directional many-to-one association to Customer
    @ManyToOne
    @JoinColumns({
        @JoinColumn(name="company_id", referencedColumnName="company_id"),
        @JoinColumn(name="customer_id", referencedColumnName="customer_id")
        })
    private Customer customer;

    //bi-directional one-to-one association to Address
    @OneToOne
    @JoinColumn(name="ship_to_id", referencedColumnName="id")
    private Address address;

ShipToPK.java

package model;

import java.io.Serializable;
import javax.persistence.*;

/**
 * The primary key class for the ship_to database table.
 * 
 */
@Embeddable
public class ShipToPK implements Serializable {
    //default serial version id, required for serializable classes.
    private static final long serialVersionUID = 1L;

    @Column(name="company_id", insertable=false, updatable=false)
    private String companyId;

    @Column(name="ship_to_id")
    private long shipToId;

    public ShipToPK() {
    }
    public String getCompanyId() {
        return this.companyId;
    }
    public void setCompanyId(String companyId) {
        this.companyId = companyId;
    }
    public long getShipToId() {
        return this.shipToId;
    }
    public void setShipToId(long shipToId) {
        this.shipToId = shipToId;
    }

    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof ShipToPK)) {
            return false;
        }
        ShipToPK castOther = (ShipToPK)other;
        return 
            this.companyId.equals(castOther.companyId)
            && (this.shipToId == castOther.shipToId);
    }

    public int hashCode() {
        final int prime = 31;
        int hash = 17;
        hash = hash * prime + this.companyId.hashCode();
        hash = hash * prime + ((int) (this.shipToId ^ (this.shipToId >>> 32)));

        return hash;
    }
}

When running the application I get the following error:

Descriptor Exceptions: 
---------------------------------------------------------

Exception [EclipseLink-48] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [ship_to.ship_to_id].  Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.OneToOneMapping[address]
Descriptor: RelationalDescriptor(model.ShipTo --> [DatabaseTable(ship_to)])

My ShipTo Entity needs to have a OneToOne relationship with Address based on ShipToPK.ship_to_id = Address.id

Similarly my Customer entity needs to have the same type of relationship based on CustomerPK.customerId = Address.id

Any help on this would be greatly appreciated. Thank you,

You have two mappings using the ship_to.ship_to_id field, both of which are writable. Normally for this to work, you will need to decide which mapping you want to use to control and set that field, and mark the other one as insertable=false, updatable=false.

In this case though, the field is an ID field that is also used within a foreign key mapping that you likely want to have set through the relationship. This is a common special case that JPA 2.0 introduced derived ID support, allowing relationships to be marked with the @ID annotation. Since you are using an embeddableID, you can also use the @MapsId annotation so that the EmbeddedID's shipToId attribute gets set by JPA with the value from the referenced entity.

Something like:

//bi-directional one-to-one association to Address
@OneToOne
@MapsId("shipToId")
@JoinColumn(name="ship_to_id", referencedColumnName="id")
private Address address;

The JoinColumn defines the field and the @MapsId forces it to override any column definition on the shipToId attribute.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM