Одна проблема, с которой я сталкиваюсь, - это хороший способ сопоставить отношения «один ко многим» с JPA / Hibernate, не жертвуя при этом принципами SOLID. Вот пример этой проблемы из текущего проекта, над которым я работаю:
Учитывая следующий код, который определяет однонаправленное отношение «один ко многим»:
@Entity
@Table(name = "users")
public class User extends AggregateEntity<User> implements HasRoles, HasContactInfo, HasContacts, HasDeals {
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
@JoinColumn(name = "fk_hasContacts")
private Set<Contact> contacts;}
Насколько я понимаю, это создает столбец "fk_hasContacts" в таблице "контакты", при этом класс "Контакт" не знает и не заботится о том, на какой объект он ссылается. Также обратите внимание на то, как «Пользователь» реализует интерфейс «hasContacts», который создает эффект разделения между двумя объектами. Если завтра я хочу добавить еще один класс; скажем, «BusinessEntity», который также имеет контакты, нет ничего, что нужно изменить в текущем коде. Однако после прочтения этой темы кажется, что этот подход неэффективен с точки зрения производительности базы данных.
Лучший способ отобразить связь @OneToMany - это полагаться на сторону @ManyToOne для распространения всех изменений состояния сущности - Как изложено здесь .
Кажется, преобладающая мудрость. Если бы я разработал его таким образом, класс «Пользователь» теперь выглядел бы так:
@Entity
@Table(name = "users")
public class User extends AggregateEntity<User> implements HasRoles, HasContactInfo, HasContacts, HasDeals {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Contact> contacts;
И (ранее отсоединенный) класс «Контакт» теперь должен выглядеть следующим образом:
@Entity
@Table(name = "contacts")
public class Contact extends StandardEntity<Contact> {
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "fk_user")
private User user;
Ключевая проблема в том, что из здесь кажется, что JPA не поддерживает определение интерфейса в качестве атрибута сущности. Итак, этот код:
@Entity
@Table(name = "contacts")
public class Contact extends StandardEntity<Contact> {
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "fk_user")
private HasContacts hasContacts;//some class which implements the "HasUser" interface
Не вариант. Кажется, нужно было бы выбирать между SOLID OOP и действительными объектами ORM. После долгих чтений я до сих пор не знаю, достаточно ли плох для производительности базы данных, чтобы оправдать написание того, что представляет собой тесно связанный, жесткий и в конечном итоге плохой код.
Есть ли обходные пути / решения этого кажущегося противоречия в принципах проектирования?