【问题标题】:JPA: Duplicate entry for primary keyJPA:主键的重复条目
【发布时间】:2015-07-13 00:46:45
【问题描述】:

过去 4 小时我一直在处理这个 JPA 问题。最后我放弃了,所以我请求你的帮助。到目前为止,我几乎尝试了所有建议的解决方案。

我试过了,

1) 映射更改(@ManyToOne、@OneToOne、@OneToMany)

2) 级联选项(PERSIST、MERGE、ALL..)

3) 禁用缓存

3) 许多其他不特别的尝试,例如 1,2 和 3。只是为了希望。 :)

测试类

public class testClass {
public static void main(String[] args) {

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("CSE_482_Project_4_-_PersistencePU");
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();

    File parentFile = new File("cleardata");
    File[] allFiles = parentFile.listFiles();
    File myFile;
    int k = 1;

    for(int f =0; f<allFiles.length; f++){
    try {
        myFile=allFiles[f];
        BufferedReader br = new BufferedReader(new FileReader(myFile));

        String tempDate = br.readLine();
        tempDate = tempDate.substring(17);
        String[] tempDateArr = tempDate.split(" ");
        int day = Integer.parseInt(tempDateArr[0]);
        int month = Integer.parseInt(tempDateArr[1]);
        int year = Integer.parseInt(tempDateArr[2]);

        Date leavingDate = new Date(year, month, day);

        String studentNumber = br.readLine().substring(13);
        boolean minor = true;
        if (br.readLine().contains("false")) {
            minor = false;
        }
        Student stu = new Student(studentNumber, leavingDate);
        stu.setMinorDegree(minor);

        tx.begin();
        em.persist(stu);
        tx.commit();

        String currentLine;
        Slot s;
        Course c;
        SlotAndCourse sc;
        int semester = 0;
        String courseCode = "";
        String slotName = "";
        int credit = 0;
        String termTaken = "";
        int yearTaken = 0;
        String grade = "";
        boolean semesterSet = false;
        boolean courseSet = false;
        boolean gradesSet = false;

        int count = 0;


        while ((currentLine = br.readLine()) != null) {
            String[] arr = currentLine.split(" ");

            if (arr[0].equals("semester")) {
                semester = Integer.parseInt(arr[1]);
                semesterSet = true;
            } else if (arr[0].matches("^([0-9]+[a-zA-Z]+|[a-zA-Z]+[0-9]+)[0-9a-zA-Z]*$")) { // contains both latters and digits CS112
                courseCode = arr[0];
                slotName = arr[1];
                credit = Integer.parseInt(arr[2]);
                courseSet = true;
            } else if (arr[0].equals("numberofattempts")) {
                int n = Integer.parseInt(arr[1]);
                for (int i = 0; i < n; i++) {
                    currentLine = br.readLine();
                    System.out.println(currentLine);
                    arr = currentLine.split(" ");
                    yearTaken = Integer.parseInt(arr[0]);
                    termTaken = arr[1];
                    grade = arr[2];
                }
                gradesSet = true;
            }

            if (gradesSet && courseSet && semesterSet) {
                s = new Slot();
                c = new Course(courseCode);

                s.setCredit(credit);
                s.setSemester(semester);
                s.setSlotName(slotName);
                s.setSlotCode("" + k);

                c.setCourseCode(courseCode);

                sc = new SlotAndCourse(s,c,yearTaken,termTaken);
                sc.setCourse(c);
                sc.setSlot(s);
                sc.setGrade(grade);

                tx.begin();

                em.clear();    // just a try, but didn't work
                em.persist(sc);
                tx.commit();                 

                courseSet = false;
                semesterSet = false;
                gradesSet = false;
                k++;
            }

        }

    } catch (Exception ex) {
        System.out.println(ex.toString());
        ex.printStackTrace();
    }
    }
     em.close();
}

}

学生班

@Entity
@Cacheable(false)
public class Student implements Serializable {

@Id
private String studentNumber;

@Temporal(TemporalType.DATE)
private Date leavIngDate;
private boolean mInorDegree;

public Student() {
}

public Student(String studentNumber, Date leavingDate) {
    this.studentNumber = studentNumber;
    this.leavIngDate = leavingDate;
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Slot)) {
        return false;
    }
    Student other = (Student) obj;
    if ((this.studentNumber == null &&  other.studentNumber != null) || (this.studentNumber != null && !this.studentNumber.equals(other.studentNumber))) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
   int hash = 0;
    hash += (studentNumber != null ? studentNumber.hashCode() : 0);
    return hash;
}

  // setters and getters

课程班

  @Entity
  @Cacheable(false)
  public class Course implements Serializable {

  @Id
  private String courseCode;

public Course() {
}

public Course(String courseCode) {
    this.courseCode = courseCode;
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Course)) {
        return false;
    }
    Course other = (Course) obj;
    if ((this.courseCode == null && other.courseCode != null) || (this.courseCode != null && !this.courseCode.equals(other.courseCode))) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
    int hash = 0;
    hash += (courseCode != null ? courseCode.hashCode() : 0);
    return hash;
}

  //setters and getters

插槽类

  @Entity
  @Cacheable(false)
  public class Slot implements Serializable {
  @Id
private String slotCode;
private String slotName;
private int credIt;
private int semester;

public Slot() {
}

public Slot(String slotCode, String slotName) {
    this.slotCode = slotCode;
    this.slotName = slotName;
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof Slot)) {
        return false;
    }
    Slot other = (Slot) obj;
    if ((this.slotCode == null && other.slotCode != null) || (this.slotCode != null && !this.slotCode.equals(other.slotCode))) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
    int hash = 0;
    hash += (slotCode != null ? slotCode.hashCode() : 0);
    return hash;

     //setters getters

SlotAndCourse 类

@Entity
@Cacheable(false)
public class SlotAndCourse implements Serializable {
@EmbeddedId
protected SlotAndCoursePK slotAndCoursePK;

@JoinColumn(name = "SLOTCODE", referencedColumnName = "SLOTCODE", insertable = false, updatable = false)
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.ALL})
private Slot slot;

@JoinColumn(name = "COURSECODE", referencedColumnName = "COURSECODE", insertable = false, updatable = false)
@ManyToOne(cascade = CascadeType.PERSIST)
private Course course;

private String grade;

public SlotAndCourse() {
}

public SlotAndCourse(SlotAndCoursePK slotAndCoursePK) {
    this.slotAndCoursePK = slotAndCoursePK;
}

public SlotAndCourse(String slotCode, String courseCode, int yearTaken, String termTaken) {
    this.slotAndCoursePK = new SlotAndCoursePK(slotCode, courseCode, yearTaken, termTaken);
}
public SlotAndCourse(Slot s, Course c, int yearTaken, String termTaken) {
    this.slot = s;
    this.course = c;
    this.slotAndCoursePK = new SlotAndCoursePK(s.getSlotCode(), c.getCourseCode(),yearTaken,termTaken);
}

@Override
public boolean equals(Object obj) {
   if(obj instanceof SlotAndCourse){
        SlotAndCourse arg = (SlotAndCourse)obj;
        return this.slotAndCoursePK.equals(arg.slotAndCoursePK);
    }
    else if(obj instanceof SlotAndCoursePK){
        SlotAndCoursePK arg = (SlotAndCoursePK)obj;
        return this.slotAndCoursePK.equals(arg);
    }
    return false;
}

@Override
public int hashCode() {
    int hash = 0;
    hash += (slotAndCoursePK != null ? slotAndCoursePK.hashCode() : 0);
    return hash;
}

SlontAndCoursePK 类

 @Embeddable
 public class SlotAndCoursePK implements Serializable{

protected String slotCode;
protected String courseCode;
protected int yearTaken;
protected String termTaken;

public SlotAndCoursePK() {
}

public SlotAndCoursePK(String slotCode, String courseCode, int yearTaken, String termTaken) {
    this.slotCode = slotCode;
    this.courseCode = courseCode;
    this.yearTaken = yearTaken;
    this.termTaken = termTaken;
}

 @Override
public int hashCode() {
    int hash = 0;
    hash += (slotCode != null ? slotCode.hashCode() : 0);
    hash += (courseCode != null ? courseCode.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object) {
    // TODO: Warning - this method won't work in the case the id fields are not set
    if (!(object instanceof SlotAndCoursePK)) {
        return false;
    }
    SlotAndCoursePK other = (SlotAndCoursePK) object;
    if ((this.slotCode == null && other.slotCode != null) || (this.slotCode != null && !this.slotCode.equals(other.slotCode))) {
        return false;
    }
    if ((this.courseCode == null && other.courseCode != null) || (this.courseCode != null && !this.courseCode.equals(other.courseCode))) {
        return false;
    }
    return true;
}

 // setters and getters

我为在这里处理和粘贴所有代码而感到难过。我希望有人可以帮助我了解我所缺少的东西。 testClass 的第一部分是从现有的结构良好的文本文件中读取并填充相关的数据字段。

当我运行调试时会发生什么; 首先一切正常,它按预期将学生、课程、slotAndCourses 添加到 db,但是当创建一个带有 db 中现有 courseCode 的 slotAndCourse 实例并将其持久化(不确定它是否是正确的词)到数据库时,它给了我重复课程表中的主键条目。

错误如下所示:

javax.persistence.RollbackException: 异常 [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException javax.persistence.RollbackException: 异常 [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException 内部异常:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:键“PRIMARY”的重复条目“cse110” 内部异常:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:键“PRIMARY”的重复条目“cse110” 错误代码:1062 错误代码:1062 呼叫:插入课程(课程代码)值(?) 调用:INSERT INTO COURSE (COURSECODE) VALUES (?)

【问题讨论】:

  • 现在我更了解您在做什么,因此删除了我的答案。它与您的问题无关,因此我将发表评论,如果在使用变量的位置而不是在没有其他变量用途的更高范围内声明变量,那么阅读您的代码会更容易。在您拥有它的形式中,读者的工作是阅读每个变量的所有内容,以查看它是否在其他地方使用。更重要的是,您可能会这样发现自己的问题。
  • 开始前数据库是空的吗?
  • Persistence 设置设置为“Drop and Create”,因此每次它首先删除所有架构然后重新创建。这只是一个模板,我现在有点乱。我会尽快让阅读变得更简单:)
  • 下面彼得解决方案的要点是,您需要从数据库中查找 Course 实体(如果存在)并使用该实体。如果您不使用从数据库读取的托管实例,JPA 将假定它是新的,因此尝试再次插入它。因此,上面的代码试图找到该课程,并且仅在它不存在时才创建一个新课程。你有什么问题?
  • Peter 的解决方案对我帮助很大。在这里发布之前,我尝试过,但只检查数据库是否存在该课程并没有解决我的问题。下面我发布了我最后的更改,我认为它现在可以工作了!非常感谢大家。 @彼得

标签: java mysql hibernate jpa eclipselink


【解决方案1】:

我的猜测是cleardata 是一个包含文本文件的目录。 cleardata 目录中的每个文件都包含单个学生的数据。

我想一门课程可以被很多学生选修,所以你不应该每次在文本文件中找到它时都创建课程。

您应该搜索数据库而不是c = new Course(courseCode);,以查看是否有课程的保存版本。类似的事情可能会有所帮助(我没有测试此代码):

public Course findOrCreateCourse(String courseCode) {
    Course course = em.find(Course.class, courseCode);
    if (course == null)
        course = new Course(courseCode);
    }
    return course;
}

您可能对其他实体也有同样的问题。

【讨论】:

  • 尝试检查数据库是否存在该课程是否存在此类课程。我想知道它会起作用,但我无法让它起作用。我会再试一次,因为这很有意义。顺便说一句,你的猜测完全正确。我会尝试这个并尽快分享结果。谢谢。
  • 将代码更新为更简单 - 您可以使用find,因为courseCode 是主键。
  • 我试过你说的,没用,或者我不能让它工作。有一部分我不明白。根据这个解决方案,它说,如果您想要包含在 SlotAndCourse 对象中的课程存在于数据库中(在 Course 表中),请不要创建该对象。所以这意味着我不能在我的 SlotAndCourse 表中添加超过一行,其中 courseCode='CSE101'。我试图用一张图片来解释我想说什么。希望我能解释我想说的话。再次感谢您的考虑。图片链接:[链接]oi59.tinypic.com/vo7b83.jpg
【解决方案2】:

今天我终于找到了解决问题的方法。以下是任何需要它的人的更新代码。我发现的主要问题是我没有分别保存 Course 和 Slot 对象。我在想,持久组合的 SlotAndCourse 对象也涉及插入 Course 和 Slot 对象。我的观察是,如果将其中三个(Course、Slot、CourseAndSlot)坚持在一起,那么事情会更好。我认为这种关系设置正确。我可能对某些概念有误,但我的代码现在可以正常工作了!

谢谢大家。

我还尝试让我的代码更易于阅读:

测试类

public class testClass {

static EntityManagerFactory emf = Persistence.createEntityManagerFactory("CSE_482_Project_4_-_PersistencePU");
static EntityManager em = emf.createEntityManager();
static EntityTransaction tx = em.getTransaction();

public static void main(String[] args) {

    // File related variables
    File parentFile = new File("cleardata");
    File[] allFiles = parentFile.listFiles();
    File myFile;
    BufferedReader br;
    String currentLine;

    // Student object and its variables
    Student stu;
    Date leavingDate;
    String studentNumber;
    boolean minor;

    // Object declerations
    Slot s;
    Course c;
    SlotAndCourse sc;

    // datafields of objects
    int semester = 0;
    String courseCode = "";
    String slotName = "";
    int credit = 0;
    String termTaken = "";
    int yearTaken = 0;
    String grade = "";
    String slotCode = "";

    // to ensure if every related field is set or not
    boolean semesterSet = false;
    boolean courseSet = false;
    boolean gradesSet = false;

    for (int f = 0; f < allFiles.length; f++) {     // travesre through files in directory
        try {
            myFile = allFiles[f];
            br = new BufferedReader(new FileReader(myFile));

            String tempDate = br.readLine();
            tempDate = tempDate.substring(17);
            String[] tempDateArr = tempDate.split(" ");
            int day = Integer.parseInt(tempDateArr[0]);
            int month = Integer.parseInt(tempDateArr[1]);
            int year = Integer.parseInt(tempDateArr[2]);

            leavingDate = new Date(year, month, day);
            studentNumber = br.readLine().substring(13);

            minor = true;
            if (br.readLine().contains("false")) {
                minor = false;
            }
            stu = new Student(studentNumber, leavingDate);
            stu.setMinorDegree(minor);
            em.persist(stu);

            while ((currentLine = br.readLine()) != null) {
                String[] arr = currentLine.split(" ");

                if (arr[0].equals("semester")) {
                    semester = Integer.parseInt(arr[1]);
                    semesterSet = true;
                } else if (arr[0].matches("^([0-9]+[a-zA-Z]+|[a-zA-Z]+[0-9]+)[0-9a-zA-Z]*$")) { // contains both latters and digits CS112
                    slotCode = arr[0];
                    slotName = arr[1];
                    credit = Integer.parseInt(arr[2]);
                    courseSet = true;
                } else if (arr[0].equals("numberofattempts")) {
                    int n = Integer.parseInt(arr[1]);
                    for (int i = 0; i < n; i++) {
                        currentLine = br.readLine();
                        arr = currentLine.split(" ");
                        yearTaken = Integer.parseInt(arr[0]);
                        termTaken = arr[1];
                        grade = arr[2];
                        courseCode = arr[3];
                        gradesSet = true;

                        if (gradesSet && courseSet && semesterSet) {

                            tx.begin();

                            s = findOrCreateSlot(slotCode, slotName);
                            c = findOrCreateCourse(courseCode);

                            s.setCredit(credit);
                            s.setSemester(semester);
                            s.setSlotName(slotName);
                            s.setSlotCode(slotCode);

                            sc = findOrCreateSlotAndCourse(s, c, yearTaken, termTaken);
                            sc.setCourse(c);
                            sc.setSlot(s);
                            sc.setGrade(grade);

                            em.persist(s);
                            em.persist(c);
                            em.persist(sc);
                            tx.commit();

                            courseSet = false;
                            semesterSet = false;
                            gradesSet = false;
                        }
                    }
                }
            }
        } catch (Exception ex) {
            System.out.println(ex.toString());
            ex.printStackTrace();
        }
    }
    em.close();
}

public static SlotAndCourse findOrCreateSlotAndCourse(Slot s, Course c, int yearTaken, String termTaken) {
    SlotAndCoursePK pk = new SlotAndCoursePK(s.getSlotCode(), c.getCourseCode(), yearTaken, termTaken);
    SlotAndCourse slotandcourse = em.find(SlotAndCourse.class, pk);
    if (slotandcourse == null) {
        slotandcourse = new SlotAndCourse(s, c, yearTaken, termTaken);
    }
    return slotandcourse;
}

public static Course findOrCreateCourse(String courseCode) {
    Course course = em.find(Course.class, courseCode);
    if (course == null) {
        course = new Course(courseCode);
    }
    return course;
}

public static Slot findOrCreateSlot(String slotCode, String slotName) {
    Slot slot = em.find(Slot.class, slotCode);
    if (slot == null) {
        slot = new Slot(slotCode, slotName);
    }

    return slot;
}

}

【讨论】:

  • 这只是归结为问题代码总是创建一个新的课程/插槽并在其上调用persist,而此代码检查课程/插槽是否已经存在并使用现有实例.如果存在,则持久调用是无操作的,并且没有理由在插槽/课程实例上显式调用 em.persist 而不是仅调用 em.persist(slotandcourse) 并让持久调用级联。如果您切换到使用 em.merge 而不是 persist,问题代码可能也能正常工作,因为 JPA 会判断该实例是否存在。
猜你喜欢
  • 2016-10-25
  • 2019-04-04
  • 2012-08-18
  • 1970-01-01
  • 2011-09-21
  • 2018-05-02
  • 2014-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多