【问题标题】:JPA throws no EntityExistsException but generates a duplicate row (auto generated PK)JPA 不抛出 EntityExistsException 但生成重复行(自动生成的 PK)
【发布时间】:2014-04-22 11:20:07
【问题描述】:

我是 JPA 的新手,正在做一些实验,希望能弄清楚实体生命周期模型。我预计下面的代码会抛出 EntityExistsException。然而,它并没有抛出预期的异常,而是在数据库表中创建了一个新行,带有一个新的主键(加一)。

谁能解释一下?我怀疑这可能与我的 ID 字段上的 @GeneratedValue 注释以及相应表中的自动递增主键有关。

您可以在下面找到 3 个 sn-ps:我尝试运行的代码、我的实体定义和我的表格设计。

我要提前感谢您为我的误解开光。

亲切的问候

ps:我使用 eclipselink 作为 jpa 实现和一个 Derby 数据库

脚本:

    Teacher teacher = new Teacher("John","Doe","English");

    //Persist John Doe
    EntityManager em = Persistence.createEntityManagerFactory("jpatest").createEntityManager(); 
    em.getTransaction().begin();
    em.persist(teacher);
    em.getTransaction().commit();
    em.close();

    //Try to persist John Doe a second time
    em = Persistence.createEntityManagerFactory("jpatest").createEntityManager();
    em.getTransaction().begin();
    em.persist(teacher);
    em.getTransaction().commit(); //I Expect a throw here
    em.close();

表格设计:

CREATE TABLE teachers (id INT GENERATED ALWAYS AS IDENTITY, firstname VARCHAR(20) ,lastname VARCHAR(40), PRIMARY KEY(id))

实体定义:

package testpackage;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity
@Table(name="teachers", schema="USERNAME")
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String firstName;

    private String lastName;

    @Transient
    private String course;




    /**
     * Zero argument constructor for JPA
     */
    Teacher(){
    }

    public Teacher(String firstName, String lastName, String course){
        this.firstName = firstName;
        this.lastName = lastName;
        this.course = course;
    }




    public int getId(){
        return id;
    }
    public String getFirstName(){
        return firstName;
    }
    public String getLastname(){
        return lastName;
    }
    public String getCourse(){
        return course;
    }



    public void setFirstName(String firstName){
        this.firstName = firstName;
    }
    public void setLastName(String lastName){
        this.lastName = lastName;
    }
}

【问题讨论】:

  • 对我来说这看起来像是一个 eclipselink 错误。 EclipseLink 应该将实体视为分离的实体,并且规范说如果将分离的实体传递给 persist(),则必须抛出异常。
  • 问题将是教师实例在第一次提交后没有分配 id。如果没有 id,第二次持久调用应该会成功,因为它没有身份,因此必须是新的。打开对所有或最好的日志记录,看看日志中是否有任何问题的迹象,例如使用了错误的数据库平台,或者 EclipseLink 没有正确发出插入。数据库中为 id 使用了哪些值?如果您切换到使用 Integer 而不是 int 或不同的生成策略,它是否有效?

标签: java jpa eclipselink derby


【解决方案1】:

EntityExistsException 只会在您持久化与 DB 关联的同一实体时引发。

但是在这里您创建了一个具有一些值的对象,然后使用主键为每个持久性自动生成的主键持久化 2 次。

因为

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

每次使用实体管理器调用persist 方法时都会生成新的ID。因此,实体在 DB 方面变得不同。因为数据库关心主键是唯一的以区分每条记录。

最好你这样做以获得该异常

首先从数据库中获取实体并更改它并尝试对其调用保存

EntityManager em = Persistence.createEntityManagerFactory("jpatest").createEntityManager(); 
    em.getTransaction().begin();
Teacher teacher = em.find(Teacher.class,primary key)
    em.persist(teacher);
    em.getTransaction().commit();
    em.close();

或者删除主键的自动生成并手动为其分配相同的 id。 如有其他问题,请告诉我

【讨论】:

    【解决方案2】:

    很明显,实体已经存在表单异常,如果已经存在,则在调用持久操作时可能会抛出 EntityExistsException。

    EclipseLink 没有在 Derby IDENTITY 的 INSERT 中包含 Id,因此您遇到了一些奇怪的事情。

    您之前是否使用了另一种生成器策略并且没有正确重新编译/部署您的代码?

    还可以尝试使用 persistence.xml 中的“eclipselink.target-database”="Derby" 设置您的平台。

    稍微改变一下

    id INT GENERATED ALWAYS AS IDENTITY
    

    使用 START WITH 1, INCREMENT BY 1 进行 PK。

    id INT GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)
    

    【讨论】:

      猜你喜欢
      • 2012-07-24
      • 1970-01-01
      • 1970-01-01
      • 2010-12-21
      • 1970-01-01
      • 2018-11-28
      • 2012-02-12
      • 2016-10-01
      • 1970-01-01
      相关资源
      最近更新 更多