我有以下对象:
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Entity(name="Group")
public class Group {
@Id
@GeneratedValue
@NotNull
@Column(name = "GROUP_ID")
private Long id;
@Column(name="NAME")
private String name;
@OneToMany(
targetEntity = Product.class,
mappedBy = "groupId",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
private List<Product> products = new ArrayList<>();
public Group(String name) {
this.name = name;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity(name="Product")
public class Product {
@Id
@GeneratedValue
@NotNull
@Column(name="PRODUCT_ID")
private Long id;
@Column(name="NAME")
private String name;
@Column(name="DESCRIPTION")
private String description;
@Column(name="PRICE")
private double price;
@ManyToMany
@JoinTable(
name = "JOIN_PRODUCT_CART",
joinColumns = {@JoinColumn(name = "PRODUCT_ID", referencedColumnName = "PRODUCT_ID")},
inverseJoinColumns = {@JoinColumn(name = "CART_ID", referencedColumnName = "CART_ID")}
)
private List<CartEntity> carts = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "GROUP_ID")
private Group groupId;
public Product(String name, String description, double price) {
this.name = name;
this.description = description;
this.price = price;
}
public Product(String name, String description, double price, Group groupId) {
this(name, description, price);
this.groupId = groupId;
}
public void addToCart(CartEntity cart) {
this.carts.add(cart);
cart.getProductsList().add(this);
}
public void addGroup(Group group) {
group.getProducts().add(this);
this.groupId = group;
}
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "cart")
public class CartEntity {
@Id
@NotNull
@GeneratedValue
@Column(name = "CART_ID")
private Long id;
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "carts")
private List<Product> productsList = new ArrayList<>();
public void addProduct(Product product) {
productsList.add(product);
product.getCarts().add(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CartEntity that = (CartEntity) o;
return id.equals(that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
现在,当我进行以下测试时:
public class ProductDaoTestSuite {
@Autowired
private ProductDao productDao;
@Autowired
private CartDaoStub cartDaoStub;
@Autowired
private GroupDao groupDao;
@Test
public void testDeleteProduct() {
// Given
Product product = new Product("test", "testProduct", 100.0);
Group group = new Group("group1");
CartEntity cart = new CartEntity();
product.addGroup(group);
cart.addProduct(product);
// When
groupDao.save(group);
productDao.save(product);
cartDaoStub.save(cart);
Long groupId = group.getId();
Long productId = product.getId();
Long cartId = cart.getId();
productDao.deleteById(productId);
// Then
Assert.assertTrue(cartDaoStub.findById(cartId).isPresent());
Assert.assertEquals(0, cartDaoStub.findById(cartId).get().getProductsList().size());
Assert.assertTrue(groupDao.findById(groupId).isPresent());
Assert.assertEquals(0, groupDao.findById(groupId).get().getProducts().size());
在产品删除之后,我希望在组和购物车中与它的关联消失(产品从它们的列表关系字段中消失)。然而,目前这种情况并没有发生。当我在产品删除后使用group/cart dao从db中拉入group&cart时,它们的列表中仍然有产品,而从db中拉入的产品返回为null。我曾尝试为@onetomany adnotation添加“orphanremoval=true”值,但对group entity似乎不起作用。
我做错什么了?
我已经开始尝试将所有类型的cascade(除了remove)添加到productclass上的@manytone中,但到目前为止还没有成功。
4条答案
按热度按时间ep6jt1vc1#
当你
remove
作为一个实体,这种状态转换应该从父级传播到子级,而不是相反。在这种情况下,您需要将其功能移动到
Group
实体,类似这样:如果你想的话
remove
一Product
,只需调用removeProduct
方法和save
父实体:另一方面,我们之间存在着多对多的关系
Product
以及CartEntity
.首先,如果您配置实体
CartEntity
与Cascade.ALL
就像你的例子:它可能会产生一种不希望的效果:如果您删除
CartEntity
,它将删除所有Product
也与实体关联,即使其他CartEntity
仍然与它们关联。vladmihalcea在本文中对此进行了详细的解释。为了避免这个问题,最好的选择是定义如下关系:
这将给我们一个
CartEntity
这样地:请注意
removeProduct
以及removeProducts
方法。使用此代码,如果需要删除
CartEntity
,只需执行以下操作:如果你需要的话
remove
一Product
从CartEntity
(仅删除关系):如果你需要传播
Product
remove
到CartEntity
,我认为最好的选择是创建一个处理整个流程的业务方法。你可以这样想:9ceoxa922#
对于1:n,你的应该只需要稍微调整就可以了。
失败原因:执行“groupdao.save(group);”此组现在位于持久性上下文中,调用“groupdao.findbyid(groupid).get().getproducts().size()”将返回持久性上下文中的副本。
要解决这个问题,只需添加:entitymanager.flush();和entitymanager.clear();在Assert之前
我想用这个集成测试来演示一下
如果我们要删除前2行,那么我们就不需要冲洗和清除。这样地。
对于n:m,由于将有另一个表引用产品,因此我们需要在删除产品之前首先从该表中删除记录。
n:m有点棘手,所以如果我能建议更改域,我会在这里做(集成测试在底部。)
我将添加一个单独的实体:cartitem,它与产品和购物车相关联
对于产品实体:添加与cartitem的双向关系
然后,检索产品(使用join fetch避免n+1,因为后者将循环遍历每个cartime)
在cartitemrepository中创建另一个查询,以按ID批量删除cartitems
最后,这里有一个集成测试来总结所有内容:
我在这个集成测试中使用了dbunit,所以我认为共享数据集也会很有帮助。
uplii1fm3#
我不知道我是否明白。hibernate不会自动为您维护反向关联。您可以使它对关联所属方的更改敏感,但仅此而已。
为什么你的测试失败了,
cartDaoStub.findById(cartId)
可能返回相同的CartEntity
已经加载到持久性上下文中的。试着打电话entityManager.flush()
然后entityManager.clear()
在做出Assert之前,问题很可能会消失。r6vfmomb4#
它将删除关联,您只需要做一些小的调整。
1:不适用。当你移除
Product
,您不必执行任何其他操作来删除它与的关联Group
,因为产品本身拥有关联(在db列中)product.group_id
). 您只需要提交事务。下一次当你从数据库加载一个组时,它肯定不会包含这个产品。护士:m。无法自动删除关联,因为它存储在一个单独的表中,并且没有单独的实体(你不应该使用
CascadeType.ALL
对于n:m关系)。您要做的是在删除产品之前删除关联。只需将另一个助手方法添加到Product
.所以最后,为了删除一个产品和它的所有关联。您需要执行以下操作:
**n:m很棘手。例如,你应该更好地使用
Set
而不是List
以避免意外的sql。同样,我建议您考虑将n:m拆分为两个n:1和1:m,并为链接表指定一个专用实体