At a certain point in your Java career, it is inevitable to arrive at a point where you have models that are related to each other but cannot be solved with foreign keys.
A good example of a many-to-many relationships is the relationship between rice and sauces. I love ALL types of different rice with different types of sauces. Sometimes I like Jasmine rice with spicy curry, while other times I like sticky rice with soy sauce. In a pinch, I’ve even ate Jasmine rice with soy sauce! *crowd gasps*
Overall, many-to-many is a relationship that has infinite amounts of different combinations. Try to apply the rice analogy to a one-to-many relationship and it will not work.
Many-To-Many Relationships
To maintain a many-to-many relationship between two tables in a database, the only way is to have a third table which has a reference to both sides of the table. This table is called a “join” table and each entry in this table will connect the source table and target table.
Many-To-Many in Spring Data JPA
Modeling a many-to-many relationship in Spring Data is easy and the best way to remember them conceptually is we should include a collection in both models.
@Entity
public class Rice {
@Id
public Long id
@ManyToMany
public Set<Sauce> sauces;
}
@Entity
public class Sauce {
@Id
public Long id
@ManyToMany
public Set<Rice> rice;
}
This is a good start, but we must configure the model with the correct annotations.
We can do this with the @JoinTable annotation in the Rice
class. You must also provide the name of the join table, foreign keys with the @JoinColumn and @InverseJoinColumn.
@ManyToMany
@JoinTable(
name = "rice_sauces",
joinColunms = @JoinColumn(name = "rice_id"),
inverseJoinColumns = @JoinColumn(name = "sauce_id"))
Set<Sauces> sauces;
@JoinTable and @JoinColumn are not required, but it is best practice to name them explicitly since database table naming is a big deal.
On the otherside, we only have to set the mappedBy attribute of the @ManyToMany annotation in the course class:
@ManyToMany(mappedBy = "rice")
Set<Rice> rice;
Many-to-many Using a Composite Key
Our current rice and sauce model is a bit…. bland. Let’s sauce things up a bit more with the ability to put which culture our sauce combination is from!
Because every JPA entity needs a primary key. In this case a primary key is actually a composite key.
Composite keys require us to create a new class that holds different parts of the key.
@Embeddable
public class CultureTypeKey implements Serializable {
@Columns(name = "rice_id")
public Long riceId;
@Column(name = "sauce_id")
public Long sauceId;
}
But what is @Embeddable? As our program grows, you could get to a point where you have database tables that are too big. @Embeddable allows us to “split up” entities into seperate objects.
@Entity
public class Culture {
@EmbeddedId
CultureKey id;
@ManyToOne
@MapsId("riceId")
@JoinColumn(name = "rice_id")
Rice rice;
@ManyToOne
@MapsId("sauceId")
@JoinColumn(name = "sauce_id")
Sauce sauce;
string culture;
}