简体   繁体   中英

JPA: How to implement a composite primary key with derived IdClass?

I am implementing the following table structure with JPA entities, where the shown attributes represent PK/FK columns. The protocolId is auto-generated and set as null in all entities before the call to persist() . The entire structure is stored at once.

表关系

With the help of a stackoverflow user and JPA spec , I resolved the derived identity in Position :

@Entity
@Table(name="...")
@IdClass(PositionID.class)
public class Position {

    @Id
    @Column(name = "POSITION_ID")
    private int positionId;

    @Id
    @ManyToOne(...)
    @JoinColumn(name="PROTOCOL_ID")
    private Protocol protocol;    


    @OneToMany(mappedBy="position")
    private Collection<Item>
}


public class PositionID implements Serializable{
    private int protocol;
    private int positionId;
}

The above relationship works, but I have trouble implementing the derived identity in Item .

This is the version, that I wrote according to the specification in chapter 2.4.1.3 Examples of Derived Identities :

@Entity
@Table(name="...")
@IdClass(ItemId.class)
public class Item {

    @Id
    @Column(name = "ITEM_NAME")
    private String itemName;

    @Id
    @ManyToOne(...)
    @JoinColumns({
        @JoinColumn(name="POSITION_ID", referencedColumnName="POSITION_ID"),
        @JoinColumn(name="PROTOCOL_ID", referencedColumnName="PROTOCOL_ID")
    })
    private Position position;
}

public class ItemId implements Serializable {
    private String itemName;
    private PositionId position;
}

Unfortunately, this results in the following error:

Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [null] and those of the entity bean class [class ......Item] must correspond and their types must be the same.

How can I implement the identity for Item ?

I think you can use a combined primary key!

In your main entity (Item) you use this:

@EmbeddedId
private CombinedPK id;

And in the CombinedPK you set this:

@Embeddable // Annotation on class level
public class CombinedPK {
    ...
    @Column(name = "positionId")
    private Integer positionId;

    @Column(name = "protocolId")
    private Integer protocolId;
}

The columns in the CombinedPK can then be mapped as you normally would, each for itself.

Additionally, remove the @Id annotation from your position field and add insertable=false, updatable=false to your @JoinColumn annotations.

The problem is that in your Java model you have (and need) two different representations of the same data from SQL: you need the primary key to be modeled and annotated, and you also need the foreign key relationship. JPA does not handle this very well, and JPA also forbids changing the primary key of a record. If you can change the database you will find it easier to add a 'SERIAL' column to your item table and use it as the primary key (and add a composite unique constraint on the foreign key columns).

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