Home Spring JPA many to many persistence issues
Reply: 1

Spring JPA many to many persistence issues

Raks
1#
Raks Published in 2018-02-11 15:33:28Z

I have the following code for many to many relationship persistence using Spring JPA. This seems to work the first time, but on subsequent saves it fails.

@Entity
public class ProductCategory {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;

    @NotNull
    @Column(unique = true)
    private String name;

    @OneToMany(mappedBy = "productCategory", cascade = CascadeType.ALL)
    private Set<ProductCategoryToProductAttributeRel> productCategoryToProductAttributeRel = new HashSet<ProductCategoryToProductAttributeRel>();
}

and rel class ( a new class is added here because I need additional columns on this table

@Entity
public class ProductCategoryToProductAttributeRel implements Serializable{
    private static final long serialVersionUID = 1L;
    private Integer id;
    @Id
    @ManyToOne
    @JoinColumn(name = "product_category_id")
    private ProductCategory productCategory;
    @Id
    @ManyToOne
    @JoinColumn(name = "product_attribute_id")
    private ProductAttribute productAttribute;
}

and category class

@Entity
public class ProductAttribute {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
    @NotNull
    private String name;
}

and code to save

@RequestMapping(value = "/saveProductCategory", method = RequestMethod.POST)
public String ProductCategorySave(ProductCategoryDTO productCategoryDTO, Model model) {
    ProductCategory pc = new ProductCategory(productCategoryDTO);
    if (productCategoryDTO.getProductAttributeIds() != null
            && productCategoryDTO.getProductAttributeIds().size() > 0) {
        Iterator<ProductCategoryToProductAttributeRel> it =
                pc.getProductCategoryToProductAttributeRel().iterator();
        // removing elements
        while (it.hasNext()) {
            boolean isPresent = false;
            for (Integer paId : productCategoryDTO.getProductAttributeIds()) {
                if (it.next().getProductAttribute().getId().equals(paId)) {
                    isPresent = true; break;
                }
            }
            if (!isPresent) { it.remove(); }
        }
        for (Integer paId : productCategoryDTO.getProductAttributeIds()) {
            ProductAttribute pa = productAttributeRepository.findOne(paId);

            boolean add = true;
            for (ProductCategoryToProductAttributeRel pcToPARel :
                pc.getProductCategoryToProductAttributeRel()) {
                if (pcToPARel.getProductAttribute().getId().equals(paId)) {
                    add = false;
                }
            }
            if (add) {
                ProductCategoryToProductAttributeRel pcToPARel = new ProductCategoryToProductAttributeRel();
                pcToPARel.setProductAttribute(pa);
                pcToPARel.setProductCategory(pc);
                pc.getProductCategoryToProductAttributeRel().add(pcToPARel);
            }
        }
    }
    productCateogryRepository.save(pc);    
    return "edit-product-category";
}

The first time save is called seems to work fine and updates the tables as expected. However the second time save is called with additional items to save, when I try to additional rows for rel table, seems to be failing with error

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'product_category_id' cannot be null

What am I doing wrong here

Ranjeet
2#
Ranjeet Reply to 2018-02-11 20:27:46Z
  • IMO the error seems to be caused on second save onwards in the while loop where you remove an already saved ProductCategoryToProductAttributeRel record association from ProductCategory using the iterator.
  • It will try to set null to ProductCategoryToProductAttributeRel.productCategory and it can't due to being part of primary key.
  • To rectify this you need to allow nulls(i.e. redesign the primary key) or also do a delete on the Rel entity when you are removing the association with ProductCategory.

If you are using JPA 2.0 try using orphanRemoval=true)

@OneToMany(mappedBy = "productCategory", orphanRemoval=true, cascade = CascadeType.ALL)
private Set<ProductCategoryToProductAttributeRel> productCategoryToProductAttributeRel = new HashSet<ProductCategoryToProductAttributeRel>();

The below could be another error which I noticed


Place your it.next() outside the for loop as I don't think you intended it to increment multiple times per single while iteration.

i.e. replace your while block with something like below.

// removing elements
while (it.hasNext()) {
    boolean isPresent = false;
    ProductCategoryToProductAttributeRel nextRel = it.next() // I think you wanted to increment once per while loop?
    for (Integer paId : productCategoryDTO.getProductAttributeIds()) {
        if (nextRel.getProductAttribute().getId().equals(paId)) {
            isPresent = true; break;
        }
    }
    if (!isPresent) { it.remove(); }
}

That could be the reason it works the first time (as the while loop could be empty the first time).

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.336579 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO