user1116536
user1116536

Reputation: 375

Hibernate One-To-Many with Join Table - Duplicate Inserts

I'm sure this question has been asked before but I can't seem to find the answer to it. I am using Hibernate 4.0 to do a basic relationship. I have a Person object that can contain may Addresses (yes, got the code from another site thinking it would work, but it's not). I want to use a join table. I feel like i've tried every possible combination but keep coming up with the same error: "Duplicate entry for '1-3' for key 1". Basically a Hibernate error that occurs when I try to commit the transaction after setting the relationship on both sides (Person/Address). Obviously it's trying to insert the same record twice which isn't allowed on the join table, because the personId/addressId combination should only appear once. any help would be appreciated, a working Junit test maybe. In the same below I manually inserted one person and three addresses, with the first two already related to the person.

1) Do I have to explicitly set the relationship on both sides (Person and Address)? Doesn't seem right because then one of other objects would be out of synch. 2) Why does this work fine if I use List instead of a Set?

Unit Test:

@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 Stack Trace

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

WARN: SQL Error: 1062, SQLState: 23000
Dec 26, 2011 10:40:27 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions ERROR: Duplicate entry '1-3' for key 1

Code

@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;   
}

Upvotes: 3

Views: 7129

Answers (1)

JB Nizet
JB Nizet

Reputation: 692281

One of the key principles in development is DRY: don't repeat yourself. JPA applies it, and thus when you have a bidirectional association, you should declare the mapping of this association only once, on one side of the association. The other side should just say: I'm mapped as declared on the other side, using the mappedBy attribute.

Since you mapped the bidirectional association twice, Hibernate considers you have two distinct associations, and thus make two inserts.

Here's how the set of addresses should be declared:

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

Other remarks:

  • another key principle of OO is encapsulation. Your fields should be private
  • using a cascade = ALL on a toOne relationship is always wrong: do you really want a person to be deleted when one of its addresses is deleted?
  • why use a join table if the association is bidirectional? Why not just use a foreign key to person in address?

Upvotes: 8

Related Questions