Java: saving entities with ManyToMany association
Asked Answered
G

2

1

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.

Goldagoldarina answered 21/9, 2020 at 11:13 Comment(0)
L
2

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);
}
Lax answered 21/9, 2020 at 11:58 Comment(1)
That worked, and I think I got it. I have to create the RawMaterial entity first but can not persist it through RawMaterialRepository, instead, I have to create a Product (or get an existing one), add that raw material to the list of products rawMaterials and, then, persist only the Product. It's easier than I thought. Thank you.Goldagoldarina
D
0

In your design the owner of the association is RawMaterial because of you used mappedBy attribute as RawMaterial. That's fine but when you want to persist RawMaterial it tries to execute what is responsible for (creating product_raw_material and inserting row with FKs of association sides) but there is no Product record which can be used. That's why you get the error you describe.

Dexamyl answered 21/9, 2020 at 12:26 Comment(1)
Yes, I understood that and already tested that implementation but then I had another problem as described on the post. Thanks for your response.Goldagoldarina

© 2022 - 2025 — McMap. All rights reserved.