В предыдущем посте я упоминал, что я решил ссылаться на другие агрегаты по их первичному ключу, а не по типу. Я обычно использую этот подход (он же модель автономного домена) при работе с моделями больших или сложных доменов. В этой статье я попытаюсь объяснить, как это можно сделать в JPA . Обратите внимание, что результирующие сценарии DDL не будут создавать ограничение внешнего ключа (в отличие от того, что показано в предыдущем посте ).
Ссылка по идентичности
В большинстве примеров JPA каждый объект ссылается на другой объект или на него ссылается другой объект. В результате получается объектная модель, которая позволяет переходить от одного объекта к любому другому объекту. Это может вызвать нежелательные обходы (и нежелательный каскад операций сохранения). Поэтому было бы хорошо предотвратить это, ссылаясь на другие объекты по идентификатору (а не по типу).
Приведенный ниже код показывает, как OrderItem
ссылается на сущность Product
по ее первичному ключу (а не по типу).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Entity public class Product { @Id private Long id; // ... } @Entity public class Order { // ... @OneToMany (mappedBy= "order" ) private Collection<OrderItem> items; } @Entity public class OrderItem { // ... @ManyToOne private Order order; // @ManyToOne // private Product product; private Long productId; // ... } |
Есть несколько способов получить связанные объекты Product
. Одним из способов является использование хранилища для поиска товаров по заданным идентификаторам ( ProductRepository
с findByIdIn(List<Long> ids)
). Как упоминалось в предыдущих комментариях, пожалуйста, будьте осторожны, чтобы не закончить с проблемой выбора N + 1 .
Пользовательские типы идентичности также могут быть использованы. В приведенном ниже примере используется ProductId
. Это объект стоимости. И из-за JPA нам нужно было добавить конструктор без аргументов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
@Embeddable public class ProductId { private Long id; public ProductId( long id) { this .id = id; } public long getValue() { return id; } // equals and hashCode protected ProductId() { /* as required by JPA */ } } @Entity public class Product { @EmbeddedId private ProductId id; // ... } @Entity public class Order { // ... @OneToMany (mappedBy= "order" ) private Collection<OrderItem> items; } @Entity public class OrderItem { // ... @ManyToOne private Order order; // @ManyToOne // private Product product; @Embedded private ProductId productId; // ... } |
Но это не будет работать, если вы используете сгенерированные значения для идентификаторов. К счастью, начиная с JPA 2.0, есть несколько хитростей, о которых я расскажу в следующем разделе.
Сгенерированные идентификаторы
В JPA при использовании не @Basic
типов в качестве @Id
мы больше не можем использовать @GeneratedValue
. Но используя сочетание доступа к свойствам и полям, мы все равно можем использовать сгенерированное значение и ProductId
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
@Embeddable @Access (AccessType.FIELD) public class ProductId {...} @Entity @Access (AccessType.FIELD) public class Product { @Transient private ProductId id; public ProductId getId() { return id; } // ... private Long id_; @Id @GeneratedValue (strategy=...) @Access (AccessType.PROPERTY) protected Long getId_() { return id_; } protected void setId_(Long id_) { this .id_ = id_; this .id = new ProductId( this .id_); } } @Entity public class Order { // ... @OneToMany (mappedBy= "order" ) private Collection<OrderItem> items; } @Entity public class OrderItem { // ... @ManyToOne private Order order; // @ManyToOne // private Product product; @Embedded private ProductId productId; // ... } |
Хитрость заключается в использовании доступа к свойству для сгенерированного значения идентификатора (при сохранении остальных с доступом к полю). Это заставляет JPA использовать метод установки. И в этом мы инициализируем поле ProductId
. Обратите внимание, что поле ProductId
не сохраняется (помечено как @Transient
).
Надеюсь это поможет.
Ссылка: | Ссылка от Identity в JPA от нашего партнера по JCG Лоренцо Ди в блоге « Адаптация и обучение» . |