@Entity
public class Artist {
··@Id @GeneratedValue
··private Long id;
··private String firstName;
··private String lastName;
··@ManyToMany
··@JoinTable(name = "jnd_art_cd",
····joinColumns = @JoinColumn(name = "artist_fk"),
····inverseJoinColumns = @JoinColumn(name = "cd_fk"))
··private List
··// Конструкторы, геттеры, сеттеры
}
Как вы можете видеть в листинге 5.48, таблица соединения между Artist и CD переименовывается в JND_ART_CD, как и каждый столбец соединения (благодаря аннотации @JoinTable). Элемент joinColumns ссылается на владеющую сторону (Artist), а inverseJoinColumns — на противоположную владеющую сторону (CD). Соответствующая структура базы данных показана на рис. 5.18.
Рис. 5.18. ARTIST, CD и таблица соединения
Следует отметить, что при двунаправленной связи «многие ко многим» и «один к одному» любая из сторон может быть обозначена как владеющая. Независимо от того, какая из сторон будет обозначена как владелец, владеющая сторона должна включать элемент mappedBy. В противном случае поставщик будет считать, что обе стороны являются владельцами, и воспринимать все это как две отдельные однонаправленные связи «один ко многим». В результате этого могло бы получиться четыре таблицы: ARTIST и CD плюс две таблицы соединения с именами ARTIST_CD и CD_ARTIST. И недопустимым было бы наличие mappedBy на обеих сторонах.
Выборка связей
Все аннотации, которые вы видели ранее (@OneToOne, @OneToMany, @ManyToOne и @ManyToMany), определяют атрибут выборки, указывающий, что загрузка ассоциированных объектов должна быть незамедлительной или отложенной с результирующим влиянием на производительность. В зависимости от вашего приложения доступ к одним связям осуществляется чаще, чем к другим. В таких ситуациях вы можете оптимизировать производительность, загружая информацию из базы данных, когда сущность подвергается первоначальному чтению (незамедлительная загрузка) или когда к ней осуществляется доступ (отложенная загрузка). В качестве примера взглянем на некоторые крайние случаи.
Представим себе четыре сущности, которые все связаны между собой и имеют разные отношения («один к одному», «один ко многим»). В первом случае (рис. 5.19) между всеми сущностями будут связи EAGER. Это означает, что, как только вы загрузите Class1 (произведя поиск по идентификатору или выполнив запрос), все зависимые объекты будут автоматически загружены в память. Это может отразиться на производительности вашей системы.
Рис. 5.19. Четыре сущности со связями EAGER
Если взять противоположный сценарий, то все связи будут задействовать режим fetch, обеспечивающий отложенную выборку (рис. 5.20). При загрузке Class1 ничего больше загружаться не будет (за исключением, конечно же, непосредственных атрибутов Class1). Вам потребуется явным образом получить доступ к Class2 (например, с помощью метода-геттера), чтобы дать команду поставщику постоянства на загрузку информации из базы данных и т. д. Если вы захотите управлять всем графом объекта, то вам потребуется явным образом вызывать каждую сущность.
Рис. 5.20. Четыре сущности со связями LAZY
Однако не следует думать, что EAGER — это плохо, а LAZY — хорошо. EAGER поместит все данные в память с помощью небольшого количества операций доступа к базе данных (поставщик постоянства, вероятно, будет использовать запросы с соединением для связи таблиц и извлечения данных). В случае с LAZY вы не рискуете заполнить всю используемую вами память, поскольку будете контролировать, какой объект будет загружаться. Однако вам придется каждый раз осуществлять доступ к базе данных.
Параметр fetch очень важен, поскольку, если его неправильно использовать, это может привести к проблемам с производительностью. У каждой аннотации есть значение fetch по умолчанию, которое вам необходимо знать, и, если оно окажется неподходящим, изменить его (табл. 5.2).
Аннотация | Стратегия выборки по умолчанию |
---|---|
@OneToOne | EAGER |
@ManyToOne | EAGER |
@OneToMany | LAZY |
@ManyToMany | LAZY |
Если при разгрузке заказа вам постоянно будет нужен доступ к его строкам в вашем приложении, то, возможно, будет целесообразно изменить режим fetch по умолчанию аннотации @OneToMany на EAGER (листинг 5.49).
@Entity
public class Order {
··@Id @GeneratedValue
··private Long id;