Interestingly, I can't find any solution for a seemingly common scenario! So I'm asking here to learn from experienced professionals in Spring Data JPA. I'll consider using Lombok to make the sample codes more concise.
Consider a simple IMDB example web application. I've defined two simple entities as below:
@Data
@Entity
public class Movie {
    @Id
    @GeneratedValue
    private long id;
    private String title;
    private int year;
    private int rating;
}
@Data
@Entity
public class Actor {
    
    @Id
    @GeneratedValue
    private long id;
    private String firstName;
    private String lastName;
    private Date birthday;
    private String gender;
}
Now we need a join-table to link these two entities; but this is not just a simple join-table. Other than the actor and movie columns, this table has some additional attributes. We didn't want to waste storage by adding an ID column here, instead we used a composite-key consisting of actor and movie:
@Data
@Embeddable
public class MovieActorId implements Serializable {
    
    private Actor actor;
    private Movie movie;
}
@Data
@Entity
public class MovieActor {
    
    @EmbeddedId
    private MovieActorId id;
    private int salary;
    private String characterName;
}
There are two Many-to-One relations here: MovieActor >-- Actor and MovieActor >-- Movie.
Now my main question is: "Assuming the above design, how should I define the @ManyToOne relationships in this design?"
NOTE: I believe if we add an additional ID column to the MovieActor join-table instead of the composite/embedded MovieActorId, the JPA code will become fairly straight-forward. But suppose we have some sort of limitation, and we need to stick to this design as much as possible.
You need to use @MapsId which provides the mapping for an EmbeddedId primary key in @ManyToOne relation
@Data
@Embeddable
public class MovieActorId implements Serializable {
    
    private long actorId;
    private long movieId; 
    // constructor, setter, etc
}
@Data
@Entity
public class MovieActor {
    
    @EmbeddedId
    private MovieActorId id;
    @ManyToOne(cascade = CascadeType.ALL)
    @MapsId("actorId")
    private Actor actor;
    
    @ManyToOne(cascade = CascadeType.ALL)
    @MapsId("movieId")
    private Movie movie;
    ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With