我在连接到 Apache DERBY 嵌入式数据库的 JAVA 中的 SWING 应用程序上使用 JPA。我使用 Netbeans 作为我的 IDE,并使用了许多“据说”有用的模板。我的问题很简单,但是我很难解释,所以我将相关代码贴在这里,并尝试在底部进行解释。
@Entity
public class AnioLectivo implements Serializable, Comparable
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
@OneToMany(mappedBy = "anioLectivo", cascade=CascadeType.ALL)
private List<Compensatorio> compensatorios;
...
}
@Entity
public class Compensatorio implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
@ManyToOne
private AnioLectivo anioLectivo;
...
}
这两个是我想要坚持的实体。
public class AnioLectivoJpaController
{
public void edit(AnioLectivo anioLectivo) throws NonexistentEntityException,
Exception
{
EntityManager em = null;
try {
em = getEntityManager();
em.getTransaction().begin();
AnioLectivo persistentAnioLectivo = em.find(AnioLectivo.class,
anioLectivo.getId());
...
List<Compensatorio> compensatoriosOld =
persistentAnioLectivo.getCompensatorios();
List<Compensatorio> compensatoriosNew = anioLectivo.getCompensatorios();
...
List<Compensatorio> attachedCompensatoriosNew = new ArrayList<Compensatorio>();
for (Compensatorio compensatoriosNewCompensatorioToAttach : compensatoriosNew) {
compensatoriosNewCompensatorioToAttach =
em.getReference(compensatoriosNewCompensatorioToAttach.getClass(),
compensatoriosNewCompensatorioToAttach.getId());
attachedCompensatoriosNew.add(compensatoriosNewCompensatorioToAttach);
}
compensatoriosNew = attachedCompensatoriosNew;
anioLectivo.setCompensatorios(compensatoriosNew);
...
}
这是 netbeans 使用我之前粘贴的实体 AnioLectivo 的注释生成的一个类。正如你所看到的,我只粘贴了与问题相关的代码以保持简单,因为我知道多亏了 netbeans 的调试工具,问题就在这里。现在我将尝试解释究竟发生了什么。
我在程序的一部分中创建了 AnioLectivo 的实例并将它们持久化。然后在另一部分中,我必须创建 Compensatorio 实例并将其添加到 AnioLectivo 实例中的 Compensatorio 列表中。现在我想保存这个修改,我假设它是使用类 AnioLectivoJpaController 中的编辑方法进行的,我发现了这个错误:
java.lang.IllegalArgumentException: An instance of a null PK has been incorrectly provided for this find operation.
at oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerImpl.findInternal(EntityManagerImpl.java:309)
at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerImpl.getReference(EntityManagerImpl.java:176)
at org.sigeb.local.service.dao.jpa.AnioLectivoJpaController.edit(AnioLectivoJpaController.java:113)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.guardarCambios(AdministrarCursosPopUp.java:574)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.jBGuardarCambiosActionPerformed(AdministrarCursosPopUp.java:394)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp.access$1000(AdministrarCursosPopUp.java:44)
at org.sigeb.local.views.datosIniciales.AdministrarCursosPopUp$11.actionPerformed(AdministrarCursosPopUp.java:204)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
...
如我所见,问题出现在 AnioLectivoJpaController 的编辑方法中的这行代码中:
em.getReference(compensatoriosNewCompensatorioToAttach.getClass(),
compensatoriosNewCompensatorioToAttach.getId());
为什么?好吧,如果您看到实体,我已经定义了所有实体的 id 都将由持久化单元生成,但这仅在实体本身被告知要持久化时才会发生。当我创建 Compensatorio 的实例时,我从未明确设置 id,当它到达我引用的那一行时,compensatoriosNewCompensatorioToAttach.getId() 返回 null。
据我了解,ORM 之类的 JPA 具有 Persistence by Reachability,这允许如果对象 A 与对象 B 相关,则持久 A 也持久 B。但在这种情况下,它似乎以一种非常不方便的方式实现(至少对于我),因为它迫使我明确地持久化我的集合中的每个对象,而持久化拥有该集合的对象然后自动持久化该集合中的对象会更有用
是不是我做错了什么?,也许我应该从另一个角度面对这个问题,但我不知道如何,或者如果有的话,什么角度?为什么 netbeans 的人用这种方式制作模板,为什么执行该方法来尝试搜索数据库中的对象并将其带到持久性上下文中很有用,我是否需要自己持久化每个对象?如果是这样的话,如果持久性只能在一个方向上进行,为什么他们声称通过可达性实现持久性。
我显然错了,我正在寻求的是一个连贯的解释,说明如何明确这些实体之间的关系(如果我在创建它们的方式上确实犯了一个错误,因为在每本书和教程中我阅读它是这样做的)以使其工作,因此我不需要保留该集合的每个对象,或者,如果我需要从 netbeans 中删除该模板并自己为所有 CRUD 操作编写代码,我会喜欢听取有关在这种情况下如何方便进行的建议。