我有以下使用 Spring JPA 进行多对多关系持久化的代码。这似乎第一次有效,但在随后的保存中却失败了。
@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>();
}
和 rel 类(此处添加了一个新类,因为我需要在此表上添加其他列
@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;
}
和类别
@Entity
public class ProductAttribute {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
@NotNull
private String name;
}
以及要保存的代码
@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";
}
第一次调用保存似乎工作正常并按预期更新表。然而,当我尝试为 rel 表添加其他行时,第二次调用保存时会使用其他要保存的项目,似乎失败并出现错误
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'product_category_id' cannot be null
我在这里做错了什么
最佳答案
- 在我看来,该错误似乎是在 while 循环中第二次保存时引起的,在该循环中,您使用迭代器从
ProductCategory
中删除了已保存的ProductCategoryToProductAttributeRel
记录关联。 - 它将尝试将 null 设置为
ProductCategoryToProductAttributeRel.productCategory
,但由于是主键的一部分而无法设置。 - 要纠正此问题,您需要允许 null(即重新设计主键),或者在删除与
ProductCategory
的关联时对 Rel 实体进行删除。
如果您使用的是 JPA 2.0,请尝试使用 orphanRemoval=true
)
@OneToMany(mappedBy = "productCategory", orphanRemoval=true, cascade = CascadeType.ALL)
private Set<ProductCategoryToProductAttributeRel> productCategoryToProductAttributeRel = new HashSet<ProductCategoryToProductAttributeRel>();
下面可能是我注意到的另一个错误
<小时/>将您的 it.next()
放在 for 循环之外,因为我认为您不希望它每次 while 迭代都会增加多次。
即将 while block 替换为如下所示的内容。
// 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(); }
}
这可能是它第一次工作的原因(因为 while 循环第一次可能为空)。
关于java - Spring JPA 多对多持久性问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48733158/