Fork me on GitHub

1/01/2014

[Hibernate] Composite primary key maps composite foreign key

The blog records how to map two entities by the composite keys by using hibernate. Let's say we have 2 entities PrizeItem and PrizeWinnerInfo. We want to map them as OneToOne relation.

The PrizeWinnerInfo (pzwininfo_pzitem_pz_id, pzwininfo_pzitem_id) refer to PrizeItem (pzitem_pz_id, pzitem_id) with the composite foreign key.

Step 0.

Database schema of PrizeItem, table name: ix_prize_item

+----------------------------+--------------+------+-----+---------+-------+
| Field                      | Type         | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+-------+
| pzitem_pz_id               | bigint(20)   | NO   | PRI | NULL    |       |
| pzitem_id                  | int(11)      | NO   | PRI | NULL    |       |
| pzitem_expiration          | datetime     | YES  |     | NULL    |       |
| pzitem_won                 | tinyint(1)   | NO   |     | 0       |       |
| pzitem_won_time            | datetime     | YES  |     | NULL    |       |
| pzitem_winner_info_applied | tinyint(1)   | NO   |     | 0       |       |
| pzitem_pzcha_id            | int(11)      | YES  | MUL | NULL    |       |
| pzitem_serial              | varchar(255) | YES  |     | NULL    |       |
+----------------------------+--------------+------+-----+---------+-------+

Database schema of PrizeWinnerInfo, table name: ix_prize_winner_info

+------------------------+--------------+------+-----+---------+-------+
| Field                  | Type         | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+-------+
| pzwininfo_pzcha_id     | int(11)      | NO   | MUL | 0       |       |
| pzwininfo_pzitem_pz_id | bigint(20)   | NO   | PRI | 0       |       |
| pzwininfo_pzitem_id    | int(11)      | NO   | PRI | 0       |       |
| pzwininfo_name         | varchar(255) | NO   |     | NULL    |       |
| pzwininfo_email        | varchar(128) | NO   |     | NULL    |       |
| pzwininfo_phone        | varchar(32)  | YES  |     | NULL    |       |
| pzwininfo_detail       | longtext     | YES  |     | NULL    |       |
+------------------------+--------------+------+-----+---------+-------+

Step 1.

Tip: Use @Embeddable to define the composite key

The composite primary key PrizeItemPK of PrizeItem

@Embeddable
public class PrizeItemPK implements Serializable
{
    private final static long serialVersionUID = 1L;

    @Column(name="pzitem_pz_id")
    private Long prizeId;

    @Column(name="pzitem_id")
    private Integer prizeItemId;

    public PrizeItemPK() {}
    public PrizeItemPK(Long prizeId, Integer prizeItemId) {
        super();
        this.prizeId = prizeId;
        this.prizeItemId = prizeItemId;
    }

    @JsonGetter("prize_id")
    public Long getPrizeId() {
        return prizeId;
    }

    public void setPrizeId(Long prizeId) {
        this.prizeId = prizeId;
    }

    @JsonGetter("prize_item_id")
    public Integer getPrizeItemId() {
        return prizeItemId;
    }

    public void setPrizeItemId(Integer prizeItemId) {
        this.prizeItemId = prizeItemId;
    }

}

The composite primary key PrizeWinnerInfoPK of PrizeWinnerInfo

@Embeddable
public class PrizeWinnerInfoPK implements Serializable
{
    private final static long serialVersionUID = 1L;

    @Column(name="pzwininfo_pzitem_pz_id")
    private Long prizeId;

    @Column(name="pzwininfo_pzitem_id")
    private Integer prizeItemId;

    public PrizeWinnerInfoPK() {}
    public PrizeWinnerInfoPK(Long prizeId, Integer prizeItemId) {
        super();
        this.prizeId = prizeId;
        this.prizeItemId = prizeItemId;
    }

    @JsonGetter("prize_id")
    public Long getPrizeId() {
        return prizeId;
    }

    public void setPrizeId(Long prizeId) {
        this.prizeId = prizeId;
    }

    @JsonGetter("prize_item_id")
    public Integer getPrizeItemId() {
        return prizeItemId;
    }

    public void setPrizeItemId(Integer prizeItemId) {
        this.prizeItemId = prizeItemId;
    }

}

Step2.

Tip1: Use @OneToMany mapping, not @OneToOne
Tip2: Use @JsonManagedReference and @JsonBackReference to avoid cycling json serialization

ORM entity of PrizeItem

@Entity
@Table(name="ix_prize_item")
@JsonAutoDetect(getterVisibility=JsonAutoDetect.Visibility.NONE)
public class PrizeItem
{
    @EmbeddedId
    private PrizeItemPK pk;

    ...

    @OneToMany(mappedBy="prizeItem", fetch=FetchType.EAGER)
    @JsonBackReference
    private List winnerInfo;

}

ORM entity of PrizeWinnerInfo

@Entity
@Table(name="ix_prize_winner_info")
@JsonAutoDetect(getterVisibility=JsonAutoDetect.Visibility.NONE)
public class PrizeWinnerInfo extends AbstractExtensibleJsonAliasBean
{
    @EmbeddedId
    private PrizeWinnerInfoPK PK;

    ...

    @ManyToOne(fetch=FetchType.EAGER)
    @JoinColumns ({
        @JoinColumn(name="pzwininfo_pzitem_pz_id", referencedColumnName = "pzitem_pz_id", insertable = false, updatable = false),
        @JoinColumn(name="pzwininfo_pzitem_id", referencedColumnName = "pzitem_id", insertable = false, updatable = false)
    })
    @JsonManagedReference
    private PrizeItem prizeItem;

}

2014/01/16 Note:

I found the use of @JsonBackReference will make the serialization fail on that field. So, I took the below solution (http://stackoverflow.com/a/10111411/474002)

References:

  1. java - hibernate composite Primary key contains a composite foreign key, how to map this - Stack Overflow
  2. java - @OneToOne annotation within composite key class is not working - Stack Overflow
  3. @JoinColumn is part of the composite primary keys | dwuysan

No comments:

Post a Comment