1.19.2015

8. Ассоциации сущностей. One-to-one. Hibernate



В мире реляционных баз данных существуют четыре типа связей,  которые  так же имеют место быть  и  в hibernate.  Перечислим их:

1. One-to-one  -  Один к одному
2. Many-to-one  - Многие к одному.
3. One-to-many  - Один ко многим.
4. Many-to-many  -  Многие ко многим.

Если связь представлена в одном направлении между двумя сущностями, т. е.  только из одной сущности можно получить  зависимые объекты,  то такая связь называется,  однонаправленной  ассоциацией (Unidirectional Association).  Если из любой сущности можно получить зависимые объекты, то такая связь называется двунаправленной  (Bidirectional Association).  Эта и последующие главы будут представлены как  описание  кратких и законченных шаблонов примеров,   результаты  выполнения этих примеров  будут представлять или схему базы данных
которая подходит под описание конкретного примера или
sql   запросы которые генерирует hibernate.  И так давайте приступим  к рассмотрению самой простой связи  один к одному.  Пусть у нас   есть сущность Event, дата рождения, событие которое может произойти один раз в жизни у каждого человека,  описанного сущностью Person.  Связь Person(one) -> Event(one) однонаправленная,  описывается через аннотацию @OneToOne() в сущности Person,   связываем через  foreign-key две таблицы,  поможет нам в  этом аннотация @JoinColumn(name = "idF",referencedColumnName = "idE1"),  где name = "idF" – имя поля в таблице Prersons, referencedColumnName = "idE1" – имя поля на которое ссылаемся в таблице Events:

@Entity @Table(name="Events")
public class Event {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long idE;
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date birthDate;
   
}

@Entity @Table(name = "Prersons")
public class Person implements Serializable {
    @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idP;  
    @OneToOne() @JoinColumn(name = "idF",referencedColumnName = "idE1")
    private Event event;
}

Доступ к объекту  происходит посредством кода person.getEvent().getBirthDate().toString(), при этом генерируется  такой sql  код:

create table Events (idE1 bigint identity not null, birthDate datetime, primary key (idE1));
create table Prersons (idP bigint identity not null, MyName varchar(255), idF bigint, primary key (idP));
alter table Prersons add constraint FKB7BA4BF0A99263A0 foreign key (idF) references Events;

полный код  можно посмотреть в примере С8.  Хотелось бы поговорить еще о двух вещах, которые нужно вам знать. Первое есть такая аннотация @Fetch(value = FetchMode.SELECT) которая задает стратегию sql  запроса выборки, т.е. как выбрать данные, одним  запросом через join  или используя подзапросы выборки select для каждого отдельной сущности,  FetchMode.SELECT  генерирует  два sql:

select person0_.idP as idP0_0_, person0_.idF as idF0_0_, person0_.MyName as MyName0_0_ from Prersons person0_ where person0_.idP=?
select event0_.idE1 as idE1_1_0_, event0_.birthDate as birthDate1_0_ from Events event0_ where event0_.idE1=?

FetchMode.JOIN  генерирует один  sql запрос через outer join:

select person0_.idP as idP0_1_, person0_.idF as idF0_1_, person0_.MyName as MyName0_1_, event1_.idE1 as idE1_1_0_, event1_.birthDate as birthDate1_0_ from Prersons person0_ left outer join Events event1_ on person0_.idF=event1_.idE1 where person0_.idP=?

И вторая вещь, о которой нужно упомянуть, все ассоциации можно сделать “ленивыми”, если к полю применена аннотация OneToOne с выставленным атрибутом  в fetch = FetchType.LAZY, и стратегия выборки FetchMode.SELECT:

@OneToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "idF",referencedColumnName = "idE1")
 @Fetch(value = FetchMode. SELECT)
 private Event event;

Приступим к следующему  примеру, рассмотрим  ту же  связь Person1(one) -> Event(one) однонаправленную,  которая так же описывается через аннотацию @OneToOne() в сущности Person1.   Связываем через  primary key две таблицы,  поможет нам в  этом аннотация  @PrimaryKeyJoinColumn(name = "idP",referencedColumnName = "idE1"),  где name = "idP" – имя primary key поля в таблице Prersons1,  referencedColumnName = "idE1"    имя primary key поля на которое ссылаемся в таблице Events:

@Entity @Table(name = "Persons1")
public class Person1 {
    @Id   @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idP;
  
    @OneToOne(fetch = FetchType.LAZY) @Fetch(value = FetchMode.SELECT)
    @PrimaryKeyJoinColumn(name = "idP",referencedColumnName = "idE1")
    private Event event;

при этом генерируется  такая схема:

create table Events (idE1 bigint identity not null, birthDate datetime, primary key (idE1));
create table Prersons1 (idP bigint identity not null, MyName varchar(255), primary key (idP));

Следующий пример, рассматриваем  ту же связь one-to-one, но связаны  сущности  через таблицу связи (join table)  Persons2Events ,  описать такую связь помогает нам  аннотация @JoinTable( name = "Persons2Events" , joinColumns= @JoinColumn(name = "idP2"), inverseJoinColumns = @JoinColumn(name = "idE2") ) , где  имя таблицы описывается в атрибуте name = "Persons2Events",  и два поля связи "idP2" и "idE2"  посредством которых связываются две сущности:

@Entity @Table(name = "Persons2")
public class Person2 implements Serializable{
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idP;

    @OneToOne(fetch = FetchType.LAZY) @Fetch(value = FetchMode.SELECT)
    @JoinTable(name = "Persons2Events"
    ,joinColumns= @JoinColumn(name = "idP2")
    ,inverseJoinColumns = @JoinColumn(name = "idE2"))
    private Event event;

При этом формируются такие три таблицы и два ключа  foreign key:

create table Events (idE1 bigint identity not null, birthDate datetime, primary key (idE1);
create table Persons2 (idP bigint identity not null, MyName varchar(255), primary key (idP));
create table Persons2Events (idE2 bigint, idP2 bigint not null, primary key (idP2));

alter table Persons2Events add constraint FK1BA3CA8DA9C20BBD foreign key (idE2) references Events;
alter table Persons2Events add constraint FK1BA3CA8DB25E3F35 foreign key (idP2) references Persons2;

Двунаправленная связь (Bidirectional Association),  имеет место быть искусственно создано  только в ORM, и Hibernate  не исключение, он поддерживает такие связи  но,  в реляционных базах данных,   стандартом такое не предусмотрено. Рассмотрим следующий пример, связь Person(one) <-> Event(one)  через foreign-key, сущность Person3 осталось неизменной приведем её:

@Entity @Table(name = "Persons3")
public class Person3 implements Serializable {
    @Id  @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long idP;
  
    @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "idF",referencedColumnName = "idE1")
    @Fetch(value = FetchMode.SELECT)
    private Event3  event3;

Для получения обратной связи нужно в сущности Event3 объявить  переменную типа Person3  с  аннотацией @OneToOne(mappedBy = "event3"), где атрибутом mappedBy описываем обратную связь  к полю  "event3" сущности Person3:

@Entity @Table(name="Events3")
public class Event3 {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "idE1")
    private long idE;

    @OneToOne(mappedBy = "event3")
    private Person3  person3;
….

При этом генерируются похожие sql  схемы как  у однонаправленной  ассоциации, и мы получаем возможность из сущности Event3, получить сущность Person3,  и наоборот из Person3, сущность Event3 . Можно подобным образом  преобразовать однонаправленные  ассоциации связей one-to-one(primary key)  и one-to-one (join table)  добавив к ним сущность  и аннотацию @OneToOne(mappedBy = …).

Комментариев нет:

Отправить комментарий