Spring Data JPA Many-To-Many Explained Simply

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;
}

Leave a Reply

Your email address will not be published. Required fields are marked *