2011-12-27 39 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