2016-02-13 43 views
1

我想模擬以下內容:
學生可以參加考試並獲得成績。
應該可以將同一年級實體分配給多個學生。
的等級存儲在地圖(在「考試」類),這是註釋像這裏面:JPA:從實體到實體的hashmap在連接表中獲取錯誤的主鍵

@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 

但表我預計會有一個主要關鍵的學生和考試。
問題IM現在面臨的是,當我嘗試以下方法:

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

...我會得到一個異常告訴我,即時通訊違反數據庫約束。
我該如何正確堅持這張地圖?

我使用eclipselink 2.6.0作爲JPA提供程序。該應用程序在具有derby數據庫的glassfish服務器上運行。

謝謝您的閱讀,並有一個愉快的一天:)

UPDATE:
這些都是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) 
... 

回答

0

好了,我們已經有一對夫婦這裏的問題:我在Hibernate上試過了,並從中得到了不同的結果。我認爲Hibernate是正確的,因此我的第一個答案是UPDATE。

首先,你顯示的代碼不應該產生你得到的結果。當您聲稱GRADES_IDjoin table中的primary 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; 
} 

UPDATE:

我看着的EclipseLink 2.6。哇,完全是FUBAR。首先,它甚至不會讓我註釋一個OneToMany的地圖,這顯然是錯誤的。其次,它處理ManyToMany正如我描述Hibernate一樣處理OneToMany。所以,我得到了Eclipselink版本的工作方式Hibernate確實與ManyToMany,但我不得不生成模式腳本,手動更改它們,使用它們來創建數據庫向前發展。幾乎不是開發代碼的好方法。

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

我能夠寫入和讀取同一Grade爲不同Student後,我做了這些改變。這些模式腳本現在完全是Hibernate生成它們的方式。我認爲在Eclipselink有一些有趣的事情,並且提交一個錯誤是個好主意。另外,請注意,儘管您可能希望爲不同的學生分配相同的成績,但在問題領域沒有任何意義,但這當然是我的愚見。我認識到,一個Java Map就能有不同的keys指同一value,所以它肯定是針對一些問題,似乎這Ecpliselink不能夠處理一個可以想象的要求。

+0

我希望能夠分配同檔次多個學生,這就是爲什麼使用多對多IM。這就是奇怪的主鍵約束導致問題的原因。我可以問哪個JPA提供程序和您使用的是哪個版本? – Niko

+0

我正在使用wildfly 9,所以這是Hibernate Core {4.3.10.Final}。 –

+0

查看我的更新。我淡化了我的第一個答案,並嘗試了EclipseLink上的所有內容,所以我明白了爲什麼會遇到麻煩。 –