Search code examples
javajpamany-to-manyhibernate-mapping

Java: saving entities with ManyToMany association


I'm having trouble saving entities with ManyToMany associations. I've read some posts regarding this but came to no conclusion. I have tow entities with ManyToMany association. I've read that the correct way to create this entities was creating the association between them and not create a third entity to hold the association, so I implemented it this way. When I run the code, it creates a third FK table as expected, but when I try to save new objects with this association it happens one of two things:

  • In the first example I create the RawMaterial entity first and, then, I create the Product entity. This action returns the error: org.hibernate.PersistentObjectException: detached entity passed to persist;
  • The second example, I create the Product entity first. Then I create the RawMaterial entity and save it. It successfully creates both lines on database but it doesn't create any association on the FK table.

My code is:

Product entity:

@Entity
@Table(name = "products")
public class Product {

    //Product attributes
    ...

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "product_raw_material",
        joinColumns = @JoinColumn(name = "raw_material_id"),
        inverseJoinColumns = @JoinColumn(name = "product_id"))
    private Set<RawMaterial> rawMaterialList = new HashSet<>();
    //Getters and setters
}

RawMaterial entity:

@Entity
@Table(name = "raw_materials",
    uniqueConstraints = {@UniqueConstraint(columnNames = 
    {"bar_code"}),
    @UniqueConstraint(columnNames = {"name"})})
public class RawMaterial {
    
     //RawMaterial attributes
     ...
    
    @ManyToMany(mappedBy = "rawMaterialList")
    private Set<Product> productsList = new HashSet<>();

    //Getters and setters

}

First attempt to create new Product and RawMaterial entities:

RawMaterial material = new RawMaterial("barcode", "name", 
    LocalDateTime.now(), 
    LocalDateTime.now());
Set<RawMaterial> materialsList = new HashSet<>();
RawMaterial savedMaterial = rawMaterialRepository.save(material);
materialsList.add(savedMaterial);

Product product = new Product("code", "name", materialsList, 
    LocalDateTime.now(), LocalDateTime.now());
Product savedProduct = productRepository.save(product);

This example returns the error when trying to persist the Product entity: org.hibernate.PersistentObjectException: detached entity passed to persist.

Second attempt:

Product product = new Product("code", "name", null, 
    LocalDateTime.now(), 
    LocalDateTime.now());
Product savedProduct = productRepository.save(product);

Set<Product> productsSet = new HashSet<>();
productsSet.add(savedProduct);
RawMaterial material = new RawMaterial("barcode", "name", 
    LocalDateTime.now(), 
    LocalDateTime.now());
material.getProductsList().add(productsSet.stream()
    .iterator().next());
rawMaterialRapository.save(material);

This creates a new entry on each table but creates nothing on the association table (the product_raw_material association table that was automatically generated).

I don't know if I should specify an entity to hold the association or if I'm missing something on repository. Thanks for your help.


Solution

  • Try this

    @Transactional
    public Product saveProduct() {
        RawMaterial material = new RawMaterial("barcode", "name", 
            LocalDateTime.now(), 
            LocalDateTime.now());
    
        Set<RawMaterial> materialsList = new HashSet<>();
        materialsList.add(material);
    
        Product product = new Product("code", "name", materialsList, 
            LocalDateTime.now(), LocalDateTime.now());
        material.getProductsList().add(product);
    
        return productRepository.save(product);
    }