Дублирование строк при использовании связи ManyToMany и Spring Data JPA
есть сущности Person и Address, между ними связь ManyToMany
но при сохранении используя репозитории spring data jpa, в таблице persons_addresses почему-то все строки связей сохраняются дважды
Собственно вопрос, почему так происходит, что я делаю неправильно?
@Entity
@Table(name = "persons")
@Data
@NoArgsConstructor
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String fullName;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "persons_addresses",
joinColumns = @JoinColumn(name = "person_id"),
inverseJoinColumns = @JoinColumn(name = "address_id"))
private List<Address> addresses;
}
@Entity
@Table(name = "addresses")
@Data
@NoArgsConstructor
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "address")
private String addressName;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "persons_addresses",
joinColumns = @JoinColumn(name = "address_id"),
inverseJoinColumns = @JoinColumn(name = "person_id"))
private List<Person> persons;
}
// use in service with transactional
@Transactional
public PersonDto create(PersonDto newPerson) {
Person person = new Person();
person.setFullName(newPerson.getFullName());
person.setAddresses(new ArrayList<>());
newPerson.getAddresses().forEach(a -> {
Address address = addressRepository.findByAddressName(a);
if (address == null) {
address = new Address();
address.setAddressName(a);
address.setPersons(new ArrayList<>());
}
address.getPersons().add(person);
addressRepository.save(address);
person.getAddresses().add(address);
});
return new PersonDto(personRepository.save(person));
}
Ответы (1 шт):
@JoinTable нужно указывать только на одном конце ассоциации. На том, который будет ею управлять. В вашем случаее это имеет смысл сделать в Person.
Сейчас из-за того, что @JoinTable и там и там запись в таблицу ассоциации сохраняется и при сохранении Person и при сохранении Address. Правильно так:
public class Person {
...
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "persons_addresses",
joinColumns = @JoinColumn(name = "person_id"),
inverseJoinColumns = @JoinColumn(name = "address_id"))
private List<Address> addresses;
}
public class Address {
...
@ManyToMany(mappedBy="addresses")
private List<Person> persons;
}
mappedBy указывает, что другой конец (т.е. Person) является владельцем и управляет ассоциацией (т.е. создает записи при сохранении и т.д.).
Для упрощения работы с ассоциацией и поддержания консистентности объектов в памяти часто еще создают метод:
class Person {
public void addAddress(Address address) {
this.addresses.add(address);
address.getPersons().add(this);
}
}
