Many-to-Many Relationship Spring Data Jpa

Many-to-Many Relationship Spring Data Jpa


3 min read

We have learned in previous articles there are mainly three types of relationships between entities:

  1. One-To-One
  2. One-To-Many
  3. Many-To-Many

In this article we will continue our learning and learn about implementing Many-to-Many relationship.

Many-to-Many Relationship

A Many-to-Many relationship is a type of cardinality that refers to the relationship between two entities, say, A and B, where A may contain a parent instance for which there are many children in B and vice versa.

Let's take an example of Merchants and service provided by them. A merchant could provide a variety of services at the same time the same service could be provided by various other merchants.

The entity classes Merchant and Service are using Lombok to reduce boilerplate code, I suggest you to go through the Lombok article .

@Table(name = "merchant")
@Getter @Setter @Builder
public class MerchantEntity {

    @Column(name = "merchant_id")
    private Integer merchantId;

    @Column(name = "merchant_name")
    private String merchantName;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "merchant_service_mapping",
                joinColumns = @JoinColumn(name = "merchant_id", referencedColumnName = "merchant_id"),
                inverseJoinColumns = @JoinColumn(name = "service_id", referencedColumnName = "service_id"))
    private  List<ServiceEntity> merchantService;

@Table(name = "service")
@Getter @Setter @Builder
public class ServiceEntity {

    @Column(name = "service_id")
    private Integer serviceId;

    @Column(name = "service_name")
    private String serviceName;

    @ManyToMany(mappedBy = "merchantService", fetch = FetchType.EAGER)
    private List<MerchantEntity> merchants;


The relationship in ER diagram is represented as follows:


The mapping is implemented in database using a mapping table which stores the primary key or identifying column from both the entities.

@JoinTable is used to specify the name of mapping table and joining column details.

name = "merchant_service_mapping"specifies the mapping table name where the primary keys or identifying keys will be stored.

joinColumns = @JoinColumn(name = "merchant_id", referencedColumnName = "merchant_id") specifies that from merchant table merchant_id column will be used to implement mapping.

inverseJoinColumns = @JoinColumn(name = "service_id", referencedColumnName = "service_id" specifies that from service table service_id column will be used to implement mapping.

referencedColumnName is an optional property which is used to specify the name of the column referenced by this foreign key column.

mappedBy = "merchantService" is used to specify the property in MerchantEntity which is used to store the mapping details. This is required if the mapping is bideirectional.


public class ManyToManyMappingTests {

    private MerchantRepository merchantRepository;

    private ServiceRepository serviceRepository;

    private ServiceEntity serviceEntity1 = null;
    private ServiceEntity serviceEntity2 = null;
    private ServiceEntity serviceEntity3 = null;
    private MerchantEntity merchantEntity1 = null;
    private MerchantEntity merchantEntity2 = null;

    public void setup() {

        serviceEntity1 = ServiceEntity.builder()
        serviceEntity2 = ServiceEntity.builder()
                                        .serviceName("Home Renovation")

        serviceEntity3 = ServiceEntity.builder()

        List<ServiceEntity> serviceList1 = new ArrayList<>();

        List<ServiceEntity> serviceList2 = new ArrayList<>();

        merchantEntity1 = MerchantEntity.builder()
                                            .merchantName("John's Builder")

        merchantEntity2 = MerchantEntity.builder()
                                            .merchantName("Jack Repairs")

        merchantEntity1 =;

        merchantEntity2 =;

        List<MerchantEntity> me1 = new ArrayList<>();

        List<MerchantEntity> me2 = new ArrayList<>();

        List<MerchantEntity> me3 = new ArrayList<>();


    public void testManyToMany() {

        MerchantEntity merchant1 = merchantRepository.findById(1).orElse(null);

        assertEquals("Plumbing", merchant1.getMerchantService().get(0).getServiceName());
        assertEquals("Home Renovation", merchant1.getMerchantService().get(1).getServiceName());

        MerchantEntity merchant2 = merchantRepository.findById(2).orElse(null);

        assertEquals("Plumbing", merchant2.getMerchantService().get(0).getServiceName());
        assertEquals("Carpenting", merchant2.getMerchantService().get(1).getServiceName());

        ServiceEntity serviceEntity1 = serviceRepository.findByServiceName("Plumbing");
        assertEquals("John's Builder", serviceEntity1.getMerchants().get(0).getMerchantName());
        assertEquals("Jack Repairs", serviceEntity1.getMerchants().get(1).getMerchantName());

        ServiceEntity serviceEntity2 = serviceRepository.findByServiceName("Carpenting");
        assertEquals("Jack Repairs", serviceEntity2.getMerchants().get(0).getMerchantName());

On running this test case, we will find all these test cases runs successfully and verifies that our implementation is correct.


In this article, we learnt about implementation of many-to-many mapping relationship in Spring Data JPA. And also we tested our implementation using JUnit test case.

You can find the source code of this post @ Github