原文:JPA implementation patterns: Removing entities
作者:Vincent Partington
出处:http://blog.xebia.com/2009/04/09/jpa-implementation-patterns-removing-entities/
过去几周以来我一直在谈论自己在编写JPA应用时发现的实施模式,上两篇博客分别涉及了保存实体和检索实体方面的内容,不过在真正完全实现实体的时候,我猜你是希望也能够对它们进行删除操作的,所以,删除就是本篇博客的主题。
就像检索实体一样,删除一个实体是很简单的,实际上,所有需要做的就是把实体传给EntityManager.remove方法(当然实际上是调用了DAO的一个删除方法,该方法转而调用EntityManager.remove),以便在事务提交的时候从数据库中删除该实体。通常情况下,这一切就是这么回事,不过,事情会因为你正在使用关联(无论它们是双向的与否)而变得更有意思起来。
删除作为关联的组成部分的实体
考虑一下之前我们讨论过的Order和OrderLine类这一例子,比方说,我们要从Order中删除OrderLine,我们以这种简单的方式来完成这件事:
orderLineDao.remove(lineToDelete);
这一代码是有问题的,当你告诉实体管理器删除该实体的时候,实体管理器并不会自动地从指向该实体的所有关联中删除它,就像JPA不会自动地管理双向关联一样,就例子中的情况而言,关联指的应该是OrderLine.order属性指向的Order对象中的orderLines集合。假如我要把这一描述写成一个测试失败的JUnit用例的话,那它应该是这样的:
OrderLine orderLineToRemove = orderLineDao.findById(someOrderLineId);
Order parentOrder = orderLineToRemove.getOrder();
int sizeBeforeRemoval = parentOrder.getOrderLines().size();
orderLineDao.remove(orderLineToRemove);
assertEquals(sizeBeforeRemoval - 1, parentOrder.getOrderLines().size());
影响
这一测试用例的失败有两处不易察觉也因此是比较恶劣的影响:
在删除OrderLine之后,任何使用Order对象的代码相反仍然会看到被删除的OrderLine,只有在提交该事务,然后开始一个新的事务并在新事务中重新加载Order之后,被删除的OrderLine才不会在Order.orderLines集中出现。在简单的情况下我们不会遇到这一问题,但在事情越来越复杂的时候,我们可能会被这些“僵尸”OrderLines的出现吓一跳。
当持久操作从Order对象级联到Order.orderLines关联并且其包含的Order对象没有在同一事务中被删除时,我们将会收到诸如“已删除的实体被传给了持久方法”一类的错误。不同于我们在之前的博客中谈到的“游离实体被传给了持久方法”这一错误,当前的这一错误是由Order对象拥有到已被删除的OrderLine对象的引用这一事实导致的,JPA提供程序在把持久性上下文中的实体刷新到数据库中时发现了该引用,造成其企图持久已经被删除的实体,从而导致了错误的出现。
简单的解决方案
为了解决这一问题,我们还必须从Order.orderlines集中删除OrderLine,这听起来非常熟悉……实际上,在管理双向关联时,我们也需要确保关联的两端保持一致的状态,这意味着我们可以重用在这方面用过的模式,通过把对orderLineToRemove.setOrder(null)的调用加入到测试中来使其成功运行:
OrderLine orderLineToRemove = orderLineDao.findById(someOrderLineId);
Order parentOrder = orderLineToRemove.getOrder();
int sizeBeforeRemoval = parentOrder.getOrderLines().size();
orderLineToRemove.setOrder(null);
orderLineDao.remove(orderLineToRemove);
assertEquals(sizeBeforeRemoval - 1, parentOrder.getOrderLines().size());
模式
不过类似这样的做法会使代码变得脆弱,因为其依赖于领域对象的用户调用正确的方法,DAO应该承担这一工作。一个非常棒的解决这一问题的方法是像这样子来使用@PreRemove这一实体生命周期的钩子:
@Entity
public class OrderLine {
[...]
@PreRemove
public void preRemove() {
setOrder(null);
}
}
现在我们只要调用OrderLineDao.remove()来除去不想要的OrderLine对象就可以了。
本篇博客最初是建议引入由DAO来调用的带有preRemove方法的HasPreRemove接口,不过Sakuraba建议说,@PreRemove注解正好是我们这里要用到的东西,再次感谢你,Sakuraba!
删除孤儿
但是你会问,如果我们只是要从Order.orderLines集中删除OrderLine的话,那事情会怎么样呢?
Order parentOrder = orderLineToRemove.getOrder();
parentOrder.removeOrderLine(orderLineToRemove);
OrderLine确实已是从Order.orderlines集中删除,并且不仅仅只是在该事务中,如果我们在新的事务中再次检索该Order对象的话,被删除的OrderLine也不会出现。但是如果我们查看一下数据库,就会看到该OrderLine还在,它只是把OrderLine.order字段设置成null罢了,我们在这里看到的是一个“已成孤儿的”集合元素,解决这一问题有两种方法:
如我们之前讨论的那样,显式地删除OrderLine对象。
如果正在使用Hibernate作为JPA提供程序的话,可以让Hibernate自动地删除这些孤儿,对你希望Hinernate这样做的@OneToMany注解来说,在其下面添加值为org.hibernate.annotations.CascadeType.DELETE_ORPHAN的org.hibernate.annotations.Cascade注解,可参阅Hinernate文档中的例子。
虽然第二种解决方案是供应商特定的,但确实有它的好处在,那就是代码不需要每次从集合中删除实体的时候都要调用DAO的删除方法,不过为了明确地表明你是在使用供应商特定的扩展,你应该使用完整的包名称(既然Java Persistence with Hibernate一书也这样建议)来引用这些注解:
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<OrderLine> orderLines = new HashSet<OrderLine>();
需要注意的是,CascadeType.All并未包含DELETE_ORPHAN这一级联类型,这就是为什在这里的例子中要显式地设定它。
因此,我们可以得出结论,删除实体是件简单的工作,除非你正在处理关联,在这种情况下需要特别小心,要确保从所有指向实体的对象中删除该实体,并且同时要从数据库中删除它。我们下一篇博客中再见!在此期间,请在下面的评论部分中留下任何想说的话。
附:如果下周你会参加J-Spring的话,欢迎来参加我关于这一主题的谈论,时间是14:25到15:15(不过是在荷兰举行),或者可在举行会议的地方找我,一起来探讨一下JPA,我可能会在Xebia的展位附近出现。J
分享到:
相关推荐
JPA 实体状态:JPA 实体有四种状态:新建、已存在、已删除、已(detached)。 JPA 实体生命周期: 1. 新建实体 2. 持久化实体 3. 更新实体 4. 删除实体 JPA 实体管理器 实体管理器(EntityManager)是 JPA 中的...
此外,IDEA还提供了自动生成JPA实体类的功能,这对于创建与数据库表对应的实体模型非常方便。通过"File -> Project Structure -> Modules -> JPA"的路径设置JPA支持,选择Hibernate作为默认提供者。然后,在...
**JPA(Java Persistence API)**是Java平台上的一个标准,用于管理关系数据库中的数据,它简化了数据库操作,提供了一种面向对象的方式来处理数据库事务。JPA通过ORM(Object-Relational Mapping)映射机制将Java...
这些注解可以用来指定扫描的表映射实体 Entity 的目录、表 repository 目录、开启 JPA 审核能力等。 在 application.properties 文件中,可以配置一些数据库连接信息,例如数据库 URL、用户名、密码等。同时,也...
在处理关联关系时,Spring Data JPA提供了懒加载和急加载(Eager vs Lazy Fetching)机制,通过配置实体类的属性映射,可以在需要时按需加载关联的数据,避免了N+1查询问题。同时,它还支持级联操作(Cascade ...
一共有三个分卷。全部下载才能解压。 这本书不错,值得一看。
蓝图 JPA 图表 蓝图 API 在(1)。 我实现 blueprints-jpa-graph 的原因是我被 ObjectDB(2) 的性能 (3) 所吸引。 1:JPA: : 2:对象数据库: ://www.objectdb.com/ 3:JPA 性能基准: ://...
【标题】"notes_JPA_JSF:使用JSF和JPA实施来重建Notes项目"涉及的是在Java开发环境中,利用JavaServer Faces (JSF) 和 Java Persistence API (JPA) 技术重构建一个名为Notes的项目。JSF是Java EE平台上的一个用户...
### JPA实体对象状态 #### 一、实体对象的状态分类 在Java Persistence API (JPA) 中,实体对象的状态管理是实现数据持久化的基础之一。根据实体对象与实体管理器(EntityManager)之间的交互关系,实体对象可以...
春天数据jpa额外使用jpa的spring数据更舒适我爱spring-data-jpa,她放开我的双手,粗鲁的方法很无聊! 然而,尽管她为我们提供了规范解决方案,但她在动态本机查询上并不完美,而且她的返回类型必须是一个实体,但是...
Spring Data Jpa常用功能演示配套说明请查看:项目简介本项目采用当前最新版本的2.1.4.RELEASE做基础架构支撑,请参考本项目建议有一定的基础及经验。教程主要针对中文用户,如果您英文良好,建议直接阅读官网帮助...
2. **实体管理器(EntityManager)**: 是JPA的主要接口,负责处理数据库的CRUD(创建、读取、更新、删除)操作。开发者通过它来保存、查询和删除实体。 3. **实体管理工厂(EntityManagerFactory)**: 创建并管理...
3. **JPA实体生命周期**:实体从创建到销毁的过程中,状态会发生变化,JPA提供了对应的方法来管理这些状态。 ### 四、JPA实体管理器 实体管理器(EntityManager)是JPA的主要接口,负责管理实体的生命周期。 1. *...
- **OpenJPA/JPA**:实体映射主要通过注解来实现,如 `@Entity`、`@Table` 等,同时也支持使用 `persistence.xml` 文件来进行配置。 **6. 分离实体** - **Hibernate**:支持一级缓存和二级缓存,可以有效地提高...
- **jpa_07.rar**:可能涵盖了实体状态管理,如何处理对象的瞬时态、持久态、游离态和删除态。 - **jpa_08.rar**:可能讲解了JPA的缓存机制,包括一级缓存和二级缓存,以及如何自定义缓存策略。 - **jpa_09.rar**:...
例如,父实体删除时,其子实体也可以被一同删除。 8. **多态性(Polymorphism)**: JPA支持继承和多态性,使得你可以定义一个基类,然后多个子类继承它并有自己的数据库表。 在"apache-openjpa-1.2.0-source"这个...
本文是介绍Spring-data-jpa的PPT的学习笔记,整理了Spring Data JPA相关知识配置和实践源码. 本文介绍知识点有: JPA与Spring的相关配置 JPA 方法名常用查询 JPA 使用@Query注解实现JPQL和本地自定义查询 JPA API 条件...
别名转实体是JPA中的一个概念,它涉及到如何通过别名来查询数据库并映射到对应的实体类上。 在JPA中,我们经常需要处理SQL查询结果,这些结果可能包含别名,例如在HQL(Hibernate Query Language)或JPQL(Java ...