JPA @OneToMany and composite primary key

To implement an parent object in one-to-many relation with a child object that has a composite primary key (i.e. multiple columns as primary key) is not quite straightforward.

We need to use a mix of JPA annotations along with creating some embeddable classes.

Suppose we have the following representation in our database:

1
2
3
4
5
6
7
+=======+          +=============+          +=======+
|cat    |          |cat_toy_usage|          |toy    |
|-------|1        N|-------------|N        1|-------|
|cat_id*|----------|cat_id*      |----------|toy_id*|
|name   |          |toy_id*      |          |name   |
+=======+          |usage_status |          +=======+
                   +=============+

We have:

  • cats
  • toys for cats
    • a toy can be shared between cats
  • an usage status (either USED or UNUSED)

Using JPA, we will have the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Entity
public class Cat implements Serializable {
    @Id
    @GeneratedValue
    private Long catId;

    private String name;

    @OneToMany(mappedBy="id.cat", fetched = FechType.LAZY, cascade = CascadeType.ALL)
    private Set<CatToyUsage> toyUsages;

    // Getters + setters
}

@Entity
public class CatToyUsage implements Serializable {
    @Embeddable
    static class Pk implements Serializable {
        @ManyToOne
        @JoinColumn(name = "cat_id")
        private Cat cat;

        @Column(nullable = false, updatable = false)
        private Long toyId;

        // Getters + setters + equals + hashCode
    }

    @EmbeddedId
    private Pk id;

    @Enumerated(EnumType.STRING)
    private UsageStatus usageStatus;

    // Getters + setters
}

Sources: