2011-12-27 64 views
3

我確信這個問題之前已經被問過了,但我似乎無法找到答案。我正在使用Hibernate 4.0做一個基本的關係。我有一個Person對象可以包含可能的地址(是的,從另一個網站得到的代碼認爲它會工作,但事實並非如此)。我想要使​​用連接表。我覺得我已經嘗試了所有可能的組合,但不斷提出相同的錯誤:「對於鍵1,重複輸入」1-3「。基本上,當我在設置雙方關係(人員/地址)後嘗試提交事務時發生Hibernate錯誤。很明顯,它試圖插入相同的記錄兩次,這是連接表上不允許的,因爲personId/addressId組合應該只出現一次。任何幫助將不勝感激,一個有效的Junit測試也許。在下面我手動插入一個人和三個地址,前兩個已經與該人有關。休眠一對多連接表 - 重複插入

1)我是否必須明確設置雙方的關係(人員和地址)?似乎不正確,因爲其他對象之一會不同步。 2)爲什麼這個工作正常,如果我使用List而不是Set?

單元測試:

@Test 
public void tryAgain(){ 
Person p = em.find(Person.class, 1);<br> 
Address a = em.find(Address.class, 1);<br> 
Address b= em.find(Address.class,2);<br> 
Address c= em.find(Address.class, 3);<br> 
em.getTransaction().begin();<br> 
p.addresses.add(c);<br> 
c.person=p;<br> 
em.getTransaction().commit();<br> 
assertTrue(p.addresses.size()==3);<br> 
} 


JUnit的堆棧跟蹤

Hibernate: select person0_.personId as personId3_0_ from PERSON person0_ where person0_.personId=? 
<br>Hibernate: select address0_.addressId as addressId6_1_, address0_1_.personId as personId7_1_, person1_.personId as personId3_0_ from ADDRESS address0_ left outer join PersonAddress address0_1_ on address0_.addressId=address0_1_.addressId left outer join PERSON person1_ on address0_1_.personId=person1_.personId where address0_.addressId=? 
<br>Hibernate: select address0_.addressId as addressId6_1_, address0_1_.personId as personId7_1_, person1_.personId as personId3_0_ from ADDRESS address0_ left outer join PersonAddress address0_1_ on address0_.addressId=address0_1_.addressId left outer join PERSON person1_ on address0_1_.personId=person1_.personId where address0_.addressId=? 
<br>Hibernate: select address0_.addressId as addressId6_1_, address0_1_.personId as personId7_1_, person1_.personId as personId3_0_ from ADDRESS address0_ left outer join PersonAddress address0_1_ on address0_.addressId=address0_1_.addressId left outer join PERSON person1_ on address0_1_.personId=person1_.personId where address0_.addressId=? 
<br>Hibernate: select addresses0_.personId as personId3_2_, addresses0_.addressId as addressId2_, address1_.addressId as addressId6_0_, address1_1_.personId as personId7_0_, person2_.personId as personId3_1_ from PersonAddress addresses0_ inner join ADDRESS address1_ on addresses0_.addressId=address1_.addressId left outer join PersonAddress address1_1_ on address1_.addressId=address1_1_.addressId left outer join PERSON person2_ on address1_1_.personId=person2_.personId where addresses0_.personId=? 
<b>Hibernate: insert into PersonAddress (personId, addressId) values (?, ?) 
<br>Hibernate: insert into PersonAddress (personId, addressId) values (?, ?)</b> 
<br>Dec 26, 2011 10:40:27 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions 

警告:SQL錯誤:1062 SQLSTATE:23000
2011年12月26日10點四十分27秒PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions 錯誤:對於密鑰1重複條目'1-3'

代碼

@Entity<br> 
@Table(name="PERSON")<br> 
public class Person {<br> 
@Id<br> 
@GeneratedValue(strategy = GenerationType.AUTO)<br> 
@Column(name = "personId")<br> 
public int id;<br> 
@OneToMany()<br> 
@JoinTable(name = "PersonAddress", 
joinColumns = { 
@JoinColumn(name="personId", unique = true)}, 
inverseJoinColumns = { 
@JoinColumn(name="addressId")})<br> 
public Set<Address> addresses = new HashSet<Address>();<br> 
} 


@Entity 
@Table(name = "ADDRESS") 
public class Address { 
@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
@Column(name = "addressId") 
public int id; 

@ManyToOne(optional=true,cascade={CascadeType.ALL}) 
@JoinTable(name = "PersonAddress", 
joinColumns = {@JoinColumn(name="addressId",insertable=false,updatable=true)}, 
inverseJoinColumns = { 
@JoinColumn(name="personId") 
}) 
public Person person; 
} 

回答

6

一個在發展中的關鍵原則是幹:不要重複自己。 JPA將其應用於此,因此,當您有雙向關聯時,您應該只在關聯的一側聲明該關聯的映射一次。另一方面應該說:我使用mappedBy屬性映射爲在另一端聲明。

由於您將雙向關聯映射了兩次,Hibernate認爲您有兩個不同的關聯,因此會生成兩個插入。

下面是如何設定的地址應該聲明:

@OneToMany(mappedBy = "person") 
private Set<Address> addresses = new HashSet<Address>(); 

其他說明:

  • OO的另一個重要原則是封裝。您的字段應該是私人的
  • 在toOne關係中使用級聯= ALL始終是錯誤的:您真的希望在刪除某個地址時刪除某個人嗎?
  • 如果關聯是雙向的,爲什麼要使用連接表?爲什麼不直接在地址上使用外鍵?
+0

這種關係的存在方式是一個人可以有多個地址,並且一個地址可以在沒有人的情況下存在(地址可以是空的)。爲了使用mappedBy =「person」屬性,我必須在Address表中有parentId鍵。我不想要這個,因爲地址只能屬於一個人。連接表允許多個人與地址相關聯。此外,這些字段爲了簡化而不公開,不準確。 – user1116536 2011-12-27 13:12:38

+0

如果一個人有幾個地址,並且一個地址可能屬於多個人,那麼您沒有一對多關聯,而是多對多關聯。 Address實體應該有一個人員集合,而不是Person字段。無論您是否使用連接表,mappedBy都可以使用。它只是意味着:另一方有映射信息。但是,嘿,如果您不相信回答者(並且在hibernate標記中有538個選票,在JPA標記中有258個),爲什麼要問問題? – 2011-12-27 13:27:11