JPA级联更新错误。我这样做的方式不对吗?

发布于 2024-08-17 22:42:31 字数 4286 浏览 8 评论 0原文

我在 JAVA 中的 SWING 应用程序上使用 JPA,该应用程序连接到 Apache DERBY 嵌入式数据库。我使用 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 一样具有按可达性进行持久化的功能,这允许如果对象 A 与对象 B 相关,则持久化 A 也会持久化 B。但在这种情况下,它似乎以一种非常不方便的方式实现(至少对于我),因为它迫使我显式地持久化集合中的每个对象,而持久化拥有该集合的对象会更有用,然后自动持久化该集合中的对象

是我做错了什么吗?我应该从另一个角度来面对这个问题,但我不知道如何,或者如果有的话,从什么角度?为什么 netbeans 的人会这样制作该模板,为什么执行该方法来尝试搜索数据库中的对象并将其带入持久化上下文很有用,我需要自己持久化每个对象吗?如果是这样的话,如果持久性只能在一个方向上进行,为什么他们声称具有可达性持久性。

我在这一点上显然是错误的,我所寻求的是一个连贯的解释,说明如何必须明确这些实体之间的关系(如果我实际上在创建它们的方式上犯了错误,因为在每本书和教程中我阅读它是这样完成的)使其工作,这样我就不需要保留该集合的每个对象,或者,如果我需要从 netbeans 中删除该模板并自己为所有 CRUD 操作编写代码,我会喜欢听取关于在这种情况下如何方便地进行的建议。

I'm using JPA on a SWING application in JAVA that connects to an Apache DERBY embedded database. I use Netbeans as my IDE and use many of the "supposedly" helpful templates. My problem is simple, but it's difficult for me to explain so I will paste the relevant code here and try to explain at the bottom.

@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;  
...  
}  

These two are the entities that i want to persist.

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);
        ...
}

This is a class that netbeans generates using the annotations of the entity AnioLectivo that i pasted before. As you can see, i only pasted the code relevant to the problem to keep it simple because i know thanks to the debug tool of netbeans that the problem is here.
Now I'll try to explain exactly what happens.

I create instances of AnioLectivo in one part of the program and persist them ok. Then in another part i must create and add instances of Compensatorio to the Compensatorio's List in an instance of AnioLectivo. Now I want to save this modification, which I assume is made using the edit method in the class AnioLectivoJpaController and I found this error:

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)
...

the problem, as I see it, occurs in this line of code in the edit method of AnioLectivoJpaController:

em.getReference(compensatoriosNewCompensatorioToAttach.getClass(),
                compensatoriosNewCompensatorioToAttach.getId());

Why? Well if you see the entities, I have defined that the id of all entities are to be generated by the persistence unit, but this only happens when the entity itself is told to persist. As I create the Compensatorio's instances I never set the id explicitly and when it arrives to that line I quoted up there, compensatoriosNewCompensatorioToAttach.getId() returns null.

It's my understanding that ORM's like JPA have Persistence by Reachability, that allows that if an object A is related to an object B, persisting A also persists B. But in this case it seems like it's implemented in a very inconvenient way(at least for me), because it forces me to persist every object of my collection explicitly when it would be more usefull to persist the object that owns that collection and then the objects in that collection be persisted automatically

Is there something I'm doing wrong?, maybe I should face this problem from another angle, but I don't know how, or if any, what angle?. Why does the people of netbeans make that template that way, why is it useful to execute that method to try to search the objects in the DB and bring it to the persistence context, do i need to persist every object myself? if that's so why do they claim to have Persistence by Reachability if the persistence can only be made in one direction only.

I'm clearly wrong in this, what I'm seeking it's a coherent explanation of how would have to be explicited the relationship between those entities(if i actually did a mistake in the way i created them, because in every book and tutorial i read it's done like that) to make it work so i don't need to persist every object of that collection, or, if i need to drop that template from netbeans and make the code for all the CRUD operations myself, i will like to hear advice on how is convenient to proceed in this case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

时光是把杀猪刀 2024-08-24 22:42:32

看来你可以调用 em.merge(anioLectivo) 来代替所有这些代码。

It seems to be you can call em.merge(anioLectivo) insted of all this code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文