Search code examples
javaspring-boothibernatespring-data-jpamany-to-many

How to insert data inside @ManyToMany relationship with extra field in join table in Spring Boot application


I have two entities, Product and Attribute. I must to create a Many To Many relationship between Product Attribute, using another separate entity called ProductAttribute which should have a value field. I can succesfully add and delete Products and Attributes to the database, but i cannot create any entry in ProductAttribute table since in the service class ( last code snippet ) the repository see's my new ProductAttribute as null. Here are my Entities:

@Table
@Entity
@Data
public class Product{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    @OneToMany(mappedBy = "product")
    private List<ProductAttribute> productAttributes;
  // other fields -------
@Table
@Entity
@Data
public class Attribute{
    @Id
    @GeneratedValue
    @Column(name = "id",nullable = false)
    private Long id;
    @Column(nullable = false)
    private String name;

    @OneToMany(mappedBy = "attribute")
    private List<ProductAttribute> productAttributes;
}

ProductAttribute entity and the Key class

@Entity
@Table(name = "product_attribute")
@Data
public class ProductAttribute {
    @EmbeddedId
    private ProductAttributeKey id;

    @ManyToOne
    @MapsId("productId")
    private  Product product;

    @ManyToOne
    @MapsId("attributeId")
    private Attribute attribute;

    @Column
    String value;
}
@Embeddable                                                     
@Data                                                           
@AllArgsConstructor                                             
@NoArgsConstructor                                              
public class ProductAttributeKey implements Serializable {      
    @Column(name = "product_id")                                
    private Long productId;                                     
                                                                
    @Column(name = "attribute_id")                              
    private Long attributeId;                                   
}

I implemented the JpaRepository for every Relationship except for ProductAttributeKey. This is the method from the ProductAttributeService class:

@Transactional
    public ProductAttribute createProductAttribute(CreateProductAttributeModel createProductAttributeModel) {
        Product product = productRepository.findById(createProductAttributeModel.getProductId())
                .orElseThrow(() -> new EntityNotFoundException("Product with id " + createProductAttributeModel.getProductId() + " not found!"));
        Attribute attribute = attributeRepository.findById(createProductAttributeModel.getAttributeId())
                .orElseThrow(() -> new EntityNotFoundException("Attribute with id " + createProductAttributeModel.getAttributeId() + " not found!"));
        String value = createProductAttributeModel.getValue();

//create new productAttribute
        ProductAttribute productAttribute = new ProductAttribute();

        productAttribute.setProduct(product);
        productAttribute.setAttribute(attribute);
        productAttribute.setValue(value);


     return productAttributeRepository.save(productAttribute);
    }

Solution

  • When you try to save this ProductAttribute object using the productAttributeRepository, the repository is trying to generate an ID for the object, but since the id property is null, it fails to do so and returns a null object.

    This should fix your Problem:

    //create new productAttribute
    ProductAttribute productAttribute = new ProductAttribute();
    
    ProductAttributeKey id = new ProductAttributeKey();
    id.setProductId(createProductAttributeModel.getProductId());
    id.setAttributeId(createProductAttributeModel.getAttributeId());
    
    productAttribute.setId(id);
    productAttribute.setProduct(product);
    productAttribute.setAttribute(attribute);
    productAttribute.setValue(value);
    
    return productAttributeRepository.save(productAttribute);