Hibernate один-ко-многим с таблицей соединений - дубликаты вставок - PullRequest
3 голосов
/ 27 декабря 2011

Я уверен, что этот вопрос задавался ранее, но я не могу найти ответ на него.Я использую Hibernate 4.0, чтобы сделать основные отношения.У меня есть объект Person, который может содержать майские адреса (да, я получил код с другого сайта, думая, что он будет работать, но это не так).Я хочу использовать таблицу соединений.Я чувствую, что перепробовал все возможные комбинации, но продолжаю появляться с одной и той же ошибкой: «Дублировать запись для« 1-3 »для ключа 1».В основном ошибка 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
26 декабря 2011 г. 22:40:27 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions ОШИБКА: Дублирующая запись '1-3' для ключа 1

Код

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

1 Ответ

6 голосов
/ 27 декабря 2011

Один из ключевых принципов развития - СУХОЙ: не повторяйте себя.JPA применяет его, и поэтому, когда у вас есть двунаправленная ассоциация, вы должны объявить сопоставление этой ассоциации только один раз, на одной стороне ассоциации.Другая сторона должна просто сказать: я сопоставлен как объявлено на другой стороне, используя атрибут mappedBy.

Поскольку вы дважды отобразили двунаправленную ассоциацию, Hibernate считает, что у вас есть две различные ассоциации, и, следовательно, делает две вставки.

Вот как должен быть объявлен набор адресов:

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

Другие замечания:

  • еще одним ключевым принципом ОО является инкапсуляция.Ваши поля должны быть приватными
  • с использованием каскада = ALL в отношении toOne всегда неправильно: вы действительно хотите, чтобы человек был удален при удалении одного из его адресов?
  • зачем использоватьприсоединиться к таблице, если связь двунаправленная?Почему бы просто не использовать внешний ключ к человеку в адресе?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...