【问题标题】:JPA: hashmap from entity to entity gets wrong primary keys in join tableJPA:从实体到实体的哈希图在连接表中获取错误的主键
【发布时间】:2016-05-24 16:06:43
【问题描述】:

我正在尝试对以下内容进行建模:
学生可以参加考试并获得成绩。
应该可以将同一个成绩实体分配给多个学生。
成绩存储在地图中(在“考试”类中),其注释如下:

@ManyToMany
@MapKeyClass(value = Person.class)
@JoinTable(name = "t1")
private Map<Person, Grade> grades;

JPA 创建以下连接表:

T1:  
EXAM_ID: stores the id of the exam, is part of the primary key  
GRADES_ID: stores the id of the grade, is part of the primary key  
GRADES_KEY: stores the id of the student, is not part of the primary key 

但是,我期望的表会有一个包含学生和考试的主键。
我现在面临的问题是当我尝试以下操作时:

grades.put(student1, grade1);
grades.put(student2, grade1);

...我会收到一个异常,告诉我我违反了数据库约束。
如何正确持久化这张地图?

我使用 eclipselink 2.6.0 作为 JPA 提供程序。该应用程序在带有 derby 数据库的 glassfish 服务器上运行。

感谢您的阅读,祝您有愉快的一天:)

更新:
这些是用于创建数据库的 SQL 查询、eclipselink 调用:

...
CREATE TABLE EXAM_GRADE (Exam_ID BIGINT NOT NULL, grades_ID BIGINT NOT NULL, grades_KEY BIGINT, PRIMARY KEY (Exam_ID, grades_ID))
...
ALTER TABLE EXAM_GRADE ADD CONSTRAINT EXAMGRADEgrades_ID FOREIGN KEY (grades_ID) REFERENCES GRADE (ID)
ALTER TABLE EXAM_GRADE ADD CONSTRAINT EXAMGRADEgradesKEY FOREIGN KEY (grades_KEY) REFERENCES PERSON (ID)
ALTER TABLE EXAM_GRADE ADD CONSTRAINT EXAM_GRADE_Exam_ID FOREIGN KEY (Exam_ID) REFERENCES EXAM (ID)
...

【问题讨论】:

  • 你有没有设法解决你的问题?我在地图/主键/等方面遇到了完全相同的问题......
  • 嗨,Thomas,我不记得具体是怎么做的了,但我通过重组数据解决了这个问题。
  • 嗨 Niko,感谢您的回复,但不是我想听到的 :)

标签: java sql jpa dictionary eclipselink


【解决方案1】:

好吧,我们在这里遇到了几个问题:我在 Hibernate 上进行了尝试,并从您那里得到了不同的结果。我认为 Hibernate 做得正确,因此我对 UPDATE 的第一个回答。

首先,您显示的代码不应产生您所获得的结果。当您声称GRADES_IDjoin tableprimary key 的一部分时,这不应该是真的。当我运行示例时,我得到primary key (Exam_id, grades_KEY)

其次,您的grades 字段中应该有OneToMany 关系,而不是ManyToMany 关系。当您为两个学生插入相同的成绩时,ManyToMany 关系不应导致 constraint violation。当您有 OneToMany 关系时,将为 unique (grades_id) 创建一个 constraint,这确实会阻止您为两个学生插入相同的成绩:

grades.put(student1, grade1);
grades.put(student2, grade1);

最好使用constraint,因为它可以防止一个学生的成绩也分配给另一个学生的编码错误。

当您考虑关系时,请考虑Entities 之间的关系。在这种情况下,您有一个 Exam 和多个 Students,因此您应该有一个 OneToMany

最后,MapKeyClass 是多余的,没有充分的理由自己命名join table,尤其是t1

你们的关系应该很简单:

@OneToMany(fetch=FetchType.EAGER)
private Map<Student, Grade> grades;

创建Exam 时,请确保为每个学生保存唯一的成绩:

public void testCreateExam() {
    Student student1 = new Student("Karl");
    Grade grade1 = new Grade(85);
    Student student2 = new Student("Debbie");
    Grade grade2 = new Grade(90);
    Map<Student, Grade> grades = new HashMap<Student, Grade>();
    grades.put(student1, grade1);
    grades.put(student2, grade2);
    examService.createExam("Biology", grades);
}

并确保首先坚持每个StudentGrade(或制定适当的逻辑):

public Exam createExam(String name, Map<Student, Grade> grades) {
    for(Student student: grades.keySet()) {
        em.persist(student);
        Grade grade = grades.get(student);
        em.persist(grade);
    }
    Exam exam = new Exam(name);
    exam.setGrades(grades);
    em.persist(exam);
    return exam;
}

更新:

我查看了 EclipseLink 2.6。哇,完全FUBAR。首先,它甚至不允许我用OneToMany 注释地图,这是完全错误的。其次,它处理ManyToMany 的方式与我描述Hibernate 处理OneToMany 的方式完全相同。所以,我得到了Eclipselink 版本,以像HibernateManyToMany 一样工作,但是我必须生成模式脚本,手动更改它们,然后使用它们来创建数据库。几乎不是开发代码的好方法。

// Change the primary key to be the Exam_Id and the grades_KEY
CREATE TABLE EXAM_GRADE (Exam_ID BIGINT NOT NULL, grades_ID BIGINT NOT NULL, grades_KEY BIGINT, PRIMARY KEY (Exam_ID, grades_KEY))
// removed the constraint for grades_ID references GRADE (ID)
ALTER TABLE EXAM_GRADE ADD CONSTRAINT EXAMGRADEgradesKEY FOREIGN KEY (grades_KEY) REFERENCES PERSON (ID)
ALTER TABLE EXAM_GRADE ADD CONSTRAINT EXAM_GRADE_Exam_ID FOREIGN KEY (Exam_ID) REFERENCES EXAM (ID)

在进行这些更改后,我能够为不同的Student 编写和读取相同的Grade。这些模式脚本现在正是 Hibernate 生成它们的方式。我认为 Eclipselink 发生了一些有趣的事情,因此提交一个错误是个好主意。另外,请注意,虽然您可能想为不同的学生分配相同的成绩,但这在问题领域没有意义,但这当然是我的拙见。我意识到一个Java Map 将能够有不同的keys 引用同一个value,所以对于某些问题,这当然是一个可以想象的要求,Ecpliselink 似乎无法处理。

【讨论】:

  • 我希望能够为多个学生分配相同的成绩,这就是我使用 ManyToMany 的原因。这就是奇怪的主键约束导致问题的原因。请问您使用的是哪个 JPA Provider 和哪个版本?
  • 我使用的是 wildfly 9,所以是 Hibernate Core {4.3.10.Final}。
  • 查看我的更新。我淡化了我的第一个答案并在 EclipseLink 上尝试了所有方法,所以我明白你为什么遇到麻烦了。
  • 您是如何设法让它为 EclipseLink 工作的?我和OP有同样的问题。我还手动修改了我的模式,虽然我在插入时没有遇到问题,但在获取或更新数据时我仍然遇到问题(基本上,在获取和更新时地图总是空的,只是添加新记录)。
猜你喜欢
  • 1970-01-01
  • 2011-03-20
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
  • 1970-01-01
  • 2021-12-21
  • 2016-06-30
相关资源
最近更新 更多