hibernate jpa
在旧版本的Hibernate中,我可以看到手册中指示的一键式删除 。 但是较新的版本不再包含此部分。 我不知道为什么。 因此,在这篇文章中,我来看看它是否仍然有效。
一键式删除部分显示:
有时一个接一个地删除收集元素可能效率极低。 对于新的空集合(例如,如果您调用
list.clear()
,Hibernate不会这样做。 在这种情况下,Hibernate将发出一个DELETE
。假设您将一个元素添加到大小为20的集合中,然后删除了两个元素。 除非集合是一个袋子,否则Hibernate将发出一个
INSERT
语句和两个DELETE
语句。 这当然是可取的。但是,假设我们删除了18个元素,剩下两个,然后添加了新元素。 有两种可能的方式进行
- 一一删除十八行,然后再插入三行
- 在一个SQL
DELETE
删除整个集合,并一一插入所有五个当前元素Hibernate不知道第二个选项可能更快。 Hibernate如此直观可能会不受欢迎,因为这种行为可能会使数据库触发器混乱,等等。
幸运的是,您可以通过丢弃(即取消引用)原始集合并返回带有所有当前元素的新实例化集合,来随时强制执行此行为(即第二种策略)。
一击删除不适用于映射为
inverse="true"
集合。
inverse="true"
适用于(Hibernate映射)XML。 但是在这篇文章中,我们将看到JPA (以Hibernate为提供者)如何“一键删除”。
我们将尝试不同的方法,看看哪种方法会导致一次删除。
- 双向一对多
- 单向一对多(带连接表)
- 单向一对多(无连接表)
- 单向一对多(使用
ElementCollection
)
我们将使用具有多个CartItem
的Cart
实体。
双向一对多
对于这一点,我们从双方的引用。
@Entity
public class Cart { ...@OneToMany(mappedBy="cart", cascade=ALL, orphanRemoval=true)Collection<OrderItem> items;
}@Entity
public class CartItem { ...@ManyToOne Cart cart;
}
为了测试这一点,我们为Cart
的表插入一行,为CartItem
的表插入三行或更多行。 然后,我们运行测试。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);for (CartItem item : cart.items) {item.cart = null; // remove reference to cart}cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen}
}
所示SQL命令将每个项目分别删除(而不是一次性删除)。
delete from CartItem where id=?
delete from CartItem where id=?
delete from CartItem where id=?
丢弃原始集合也不起作用。 它甚至引起了异常。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);// remove reference to cartcart.items = new LinkedList<CartItem>(); // discard, and use new collectionentityManager.flush(); // just so SQL commands can be seen}
}
javax.persistence.PersistenceException:org.hibernate.HibernateException:A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: ….Cart.items
我用Hibernate 4.3.11和HSQL 2.3.2对此进行了测试。 如果您的结果有所不同,请点击评论 。
单向一对多(带连接表)
为此,我们对映射进行了更改。 这将导致创建一个联接表。
@Entity
public class Cart { ...@OneToMany(cascade=ALL)Collection<OrderItem> items;
}@Entity
public class CartItem { ...// no @ManyToOne Cart cart;
}
同样,对于Cart
,我们在表中插入一行,对于CartItem
,我们在表中插入三行或更多行。 我们还必须在连接表( Cart_CartItem
)中插入适当的记录。 然后,我们运行测试。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen}
}
显示SQL命令已删除联接表中的关联行(使用一个命令)。 但是表中CartItem
的行仍然存在(并且没有被删除)。
delete from Cart_CartItem where cart_id=?
// no delete commands for CartItem
嗯,不完全是我们想要的,因为CartItem
表中的行仍然存在。
单向一对多(无联接表)
从JPA 2.0开始,通过指定@JoinColumn
可以避免单向一对多的连接表。
@Entity
public class Cart { ...@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)@JoinColumn(name="cart_id", updatable=false, nullable=false)Collection<OrderItem> items;
}@Entity
public class CartItem { ...// no @ManyToOne Cart cart;
}
同样,对于Cart
,我们在表中插入一行,对于CartItem
,我们在表中插入三行或更多行。 然后,我们运行测试。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen}
}
丢弃原始收藏集也无效。 这也导致了相同的异常(与双向一对多)。
javax.persistence.PersistenceException:org.hibernate.HibernateException:A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: ….Cart.items
单向一对多(带有
JPA 2.0引入了@ElementCollection
。 这允许与许多侧之中任建立一个一对多关系@Basic
或@Embeddable
(即不是@Entity
)。
@Entity
public class Cart { ...@ElementCollection // @OneToMany for basic and embeddables@CollectionTable(name="CartItem") // defaults to "Cart_items" if not overriddenCollection<OrderItem> items;
}@Embeddable // not an entity!
public class CartItem {// no @Id// no @ManyToOne Cart cart;private String data; // just so that there are columns we can set
}
同样,对于Cart
,我们在表中插入一行,对于CartItem
,我们在表中插入三行或更多行。 然后,我们运行测试。
public class CartTests { ...@Testpublic void testOneShotDelete() throws Exception {Cart cart = entityManager.find(Cart.class, 53L);cart.items.clear(); // as indicated in Hibernate manualentityManager.flush(); // just so SQL commands can be seen}
}
是的 CartItem
的关联行在CartItem
被删除。
delete from CartItem where Cart_id=?
总结思想
使用ElementCollection
以单向一对多的方式进行一次删除( ElementCollection
是嵌入式的,而不是实体)。
在单向一对多联接表方案中,删除联接表中的条目不会增加太多价值。
我不确定为什么一键式删除在Hibernate中起作用(或为什么这样起作用)。 但是我确实有一个猜测。 这就是底层的JPA提供程序无法执行一次删除操作,因为它无法确保多端实体不会被其他实体引用。 与ElementCollection
不同,多面不是实体,其他实体也不能引用。
现在,这并不意味着您必须一直使用ElementCollection
。 一次性删除可能仅适用于聚合根。 在这些情况下,使用Embeddable
和ElementCollection
可能适合于组成聚合的值对象的集合。 当删除了聚合根之后,最好也删除“子”对象(并以一种有效的方式)。
我希望JPA中有一种方法可以指示子实体是私有的,并且在删除父实体时可以安全地删除它们(例如,类似于EclipseLink中的@PrivateOwned
)。 让我们看看它是否将包含在API的将来版本中。
希望这可以帮助。
翻译自: https://www.javacodegeeks.com/2016/07/one-shot-delete-hibernate-jpa.html
hibernate jpa