Type getProperty()
void setProperty(Type type)
Java Platform, Enterprise Edition (Java EE) 8 Учебник по Java EE |
Назад | Вперёд | Содержание |
Сущность — это легковесный объект персистентной области. Как правило, сущность представляет таблицу в реляционной базе данных, и каждый объект сущности соответствует строке в этой таблице. Основным программным артефактом сущности является класс сущности, хотя сущности могут использовать вспомогательные классы.
Персистентное состояние сущности проявляется через персистентные поля и персистентные свойства. Эти поля или свойства используют аннотации объектно-реляционного отображения для отображения сущностей и отношений сущностей с реляционными данными в хранилище данных.
Класс сущности должен соответствовать следующим требованиям.
Класс должен быть аннотирован javax.persistence.Entity
.
Класс должен иметь открытый или защищённый (protected) конструктор без аргументов. Класс может иметь другие конструкторы.
Класс не должен быть объявлен final
. Методы и персистентные переменные объекта не должны быть объявлены final
.
Если объект сущности передаётся по значению как отдельный объект, например, через удалённый бизнес-интерфейс сессионного компонента, класс должен реализовать интерфейс Serializable
.
Сущности могут расширять классы сущностей и не-сущностей, а классы не-сущностей могут расширять классы сущностей.
Персистентные переменные должны быть иметь приватный, защищённый (protected) или пакетный доступ, и доступ к ним можно получить только через методы класса сущности. Клиенты должны получить доступ к состоянию объекта с помощью методов доступа или бизнес-методов.
Доступ к персистентному состоянию объекта можно получить через переменные или свойства объекта. Поля или свойства должны иметь следующие типы Java:
Java примитивы
java.lang.String
Другие сериализуемые типы, в том числе:
Обёртки (wrapper) примитивов Java
java.math.BigInteger
java.math.BigDecimal
java.util.Date
java.util.Calendar
java.sql.Date
java.sql.Time
java.sql.TimeStamp
Пользовательские сериализуемые типы
byte[]
Byte[]
char[]
Character[]
Перечислимые типы
Другие объекты и/или коллекции объектов
Встраиваемые классы
Сущности могут использовать персистентные поля, персистентные свойства или их комбинацию. Если аннотации сопоставления применяются к переменным объекта сущности, сущность использует персистентные поля. Если аннотации сопоставления применяются к методам получения объекта для свойств стиля JavaBeans, объект использует персистентные свойства.
Если класс сущности использует персистентные поля, среда выполнения персистентности напрямую обращается к переменным объекта класса сущности. Все поля, не аннотированные javax.persistence.Transient
или не помеченные как Java transient
, будут сохранены в хранилище данных. Аннотации объектно-реляционного отображения должны применяться к переменным объекта.
Если объект использует персистентные свойства, он должен следовать соглашениям о методах компонентов JavaBeans. Свойства стиля JavaBeans используют get- и set- методы, которые обычно именуются в соответствии с именами переменных объекта класса сущности. Для каждого персистентного свойства типа Type объекта существует метод получения get
Property и метод установки set
Property. Если свойство логического типа, можно использовать is
Property вместо get
Property. Например, если сущность Customer
использует персистентные свойства и имеет приватную переменную firstName
, класс определяет методы getFirstName
и setFirstName
для чтения и установки состояния переменной firstName
.
Сигнатуры метода для однозначных персистентных свойств следующие:
Type getProperty()
void setProperty(Type type)
Аннотации объектно-реляционного отображения для персистентных свойств должны быть применимы к get-методам. Аннотации отображения нельзя применять к полям или свойствам, аннотированным @Transient
или помеченным transient
.
Персистентные поля и свойства со значением коллекции должны использовать поддерживаемые интерфейсы коллекции Java независимо от того, использует объект персистентные поля или свойства. Можно использовать следующие интерфейсы коллекций:
java.util.Collection
java.util.Set
java.util.List
java.util.Map
Если класс сущности использует персистентные поля, тип в сигнатурах предыдущего метода должен быть одним из этих типов коллекции. Универсальные варианты этих типов коллекций также могут быть использованы. Например, если у него есть персистентное свойство, содержащее набор телефонных номеров, сущность Customer
будет иметь следующие методы:
Set<PhoneNumber> getPhoneNumbers() { ... }
void setPhoneNumbers(Set<PhoneNumber>) { ... }
Если поле или свойство объекта состоит из коллекции базовых типов или встраиваемых классов, используйте аннотацию javax.persistence.ElementCollection
для поля или свойства.
Атрибуты targetClass
и fetch
аннотации @ElementCollection
. Атрибут targetClass
указывает имя класса базового или встраиваемого класса и является необязательным, если поле или свойство определяются с использованием обобщений (generics) Java. Необязательный атрибут fetch
используется, чтобы указать, следует использовать для коллекции отложенное (lazy) или раннее (eager) извлечение, используя соответственно константы LAZY
или EAGER
перечислимого типа javax.persistence.FetchType
. По умолчанию коллекция будет извлекаться отложенно (lazy).
Следующая сущность, Person
, имеет персистентное поле nicknames
, которое представляет собой коллекцию классов String
и для которой будет использоваться раннее (eager) извлечение. Элемент targetClass
не является обязательным, потому что он использует обобщённые значения (generics) при определении поля:
@Entity
public class Person {
...
@ElementCollection(fetch=EAGER)
protected Set<String> nickname = new HashSet();
...
}
Коллекции элементов сущностей и отношений могут быть представлены коллекциями java.util.Map
. Map
состоит из ключа и значения.
При использовании элементов или отношений Map
применяются следующие правила.
Ключ или значение Map
могут быть базовым типом Java, встраиваемым классом или сущностью.
Если значение Map
является встраиваемым классом или базовым типом, используйте аннотацию @ElementCollection
.
Если значение Map
является сущностью, используйте аннотацию @OneToMany
или @ManyToMany
.
Используйте тип Map
только для одной из сторон двунаправленных отношений.
Если тип ключа Map
является базовым типом Java, используйте аннотацию javax.persistence.MapKeyColumn
, чтобы установить mapping столбца для ключа. По умолчанию атрибут name
в @MapKeyColumn
имеет форму RELATIONSHIP-FIELD/PROPERTY-NAME`_KEY`. Например, если имя поля ссылочной связи — image
, атрибут name
по умолчанию — IMAGE_KEY
.
Если тип ключа Map
является сущностью, используйте аннотацию javax.persistence.MapKeyJoinColumn
. Если для отображения требуется несколько столбцов, используйте аннотацию javax.persistence.MapKeyJoinColumns
, чтобы включить несколько аннотаций @MapKeyJoinColumn
. Если @MapKeyJoinColumn
отсутствует, по умолчанию для имени столбца сопоставления устанавливается значение RELATIONSHIP-FIELD/PROPERTY-NAME`_KEY`. Например, если имя поля отношения employee
, атрибут name
по умолчанию — EMPLOYEE_KEY
.
Если обобщённые (generic) типы Java не используются в поле отношения или свойстве, класс ключа должен быть явно установлен с использованием аннотации javax.persistence.MapKeyClass
.
Если ключ Map
является первичным ключом или персистентным полем или свойством объекта, который является значением Map
, используйте аннотацию javax.persistence.MapKey
. Аннотации @MapKeyClass
и @MapKey
нельзя использовать для одного поля или свойства.
Если значение Map
является базовым типом Java или встраиваемым классом, оно будет отображено как таблица коллекции в базе данных. Если обобщённые (generic) типы не используются, для атрибута targetClass
аннотации @ElementCollection
должен быть указан тип значения Map
.
Если значение Map
является сущностью и частью отношения «многие ко многим» или однонаправленного отношения «один ко многим», оно будет отображено на промежуточную таблицу в базе данных. Однонаправленное отношение «один ко многим», в котором используется Map
, также может быть отображено с помощью аннотации @JoinColumn
.
Если объект является частью двунаправленного отношения «один ко многим/многие к одному», он будет отображён в таблице объекта, представляющего значение Map
. Если обобщённые (generic) типы не используются, атрибут targetEntity
в аннотациях @OneToMany
и @ManyToMany
должен быть установлен на тип значения Map
.
Java API для валидации бинов (Bean Validation) предоставляет механизм для валидации данных приложения. Bean Validation интегрирован в контейнеры Java EE, что позволяет использовать одну и ту же логику валидации в любом слое корпоративного приложения.
Ограничения Bean Validation могут применяться к классам постоянных сущностей, встраиваемым классам и отображаемым родительским классам. По умолчанию Persistence provider будет автоматически выполнять валидацию сущностей с персистентными полями или свойствами, аннотированными ограничениями Bean Validation сразу после событий PrePersist
, PreUpdate
и PreRemove
жизненного цикла.
Ограничения Bean Validation — это аннотации, применяемые к полям или свойствам классов Java. Bean Validation предоставляет набор ограничений, а также API для определения пользовательских ограничений. Пользовательские ограничения могут быть комбинациями ограничений по умолчанию или новыми ограничениями, которые не используют ограничения по умолчанию. Каждое ограничение связано по крайней мере с одним классом валидатора, который проверяет значение поля или свойства. Разработчики пользовательских ограничений также должны предоставить класс валидатора для ограничения.
Ограничения Bean Validation применяются к персистентным полям или свойствам персистентных классов. При добавлении ограничений Bean Validation используйте ту же стратегию доступа, что и для персистентного класса. То есть, если персистентный класс использует доступ к полям, примените аннотации ограничения Bean Validation к полям класса. Если класс использует доступ к свойству, примените ограничения на get-методы.
Таблица 22-1 перечисляет предустановленные ограничения Bean Validation, определённые в пакете javax.validation.constraints
.
Все предустановленные ограничения, перечисленные в таблице 22-1, имеют соответствующую аннотацию ConstraintName`.List` для группировки нескольких ограничений одного типа в одном поле или свойстве. Например, следующее персистентное поле имеет два ограничения @Pattern
:
@Pattern.List({
@Pattern(regexp="..."),
@Pattern(regexp="...")
})
Следующий класс сущности, Contact
, имеет ограничения Bean Validation, примененные к его персистентным полям:
@Entity
public class Contact implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
protected String firstName;
@NotNull
protected String lastName;
@Pattern(regexp = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\."
+ "[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@"
+ "(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9]"
+ "(?:[a-z0-9-]*[a-z0-9])?",
message = "{invalid.email}")
protected String email;
@Pattern(regexp = "^\\(?(\\d{3})\\)?[- ]?(\\d{3})[- ]?(\\d{4})$",
message = "{invalid.phonenumber}")
protected String mobilePhone;
@Pattern(regexp = "^\\(?(\\d{3})\\)?[- ]?(\\d{3})[- ]?(\\d{4})$",
message = "{invalid.phonenumber}")
protected String homePhone;
@Temporal(javax.persistence.TemporalType.DATE)
@Past
protected Date birthday;
...
}
Аннотация @NotNull
в полях firstName
и lastName
указывает, что эти поля теперь обязательны для заполнения. Если создаётся новый объект Contact
, где firstName
или lastName
не были инициализированы, Bean Validation выдаст ошибку валидации. Аналогично, если ранее созданный объект Contact
был изменён так, что firstName
или lastName
имеют значение null, будет выдана ошибка валидации.
К полю email
применено ограничение @Pattern
со сложным регулярным выражением, которое соответствует большинству допустимых адресов электронной почты. Если значение email
не соответствует этому регулярному выражению, будет выдана ошибка валидации.
Поля homePhone
и mobilePhone
имеют одинаковые ограничения @Pattern
. Регулярное выражение соответствует десятизначным телефонным номерам США и Канады в форме (`xxx
)` xxx`-`xxxx.
Поле birthday
аннотировано ограничением @Past
, которое гарантирует, что значение birthday
должно быть в прошлом.
Каждый объект имеет уникальный идентификатор объекта. Например, объект клиента может быть идентифицирован по номеру клиента. Уникальный идентификатор или первичный ключ позволяет клиентам находить конкретный объект. Каждый объект должен иметь первичный ключ. Сущность может иметь простой или составной первичный ключ.
Простые первичные ключи используют аннотацию javax.persistence.Id
для обозначения свойства или поля первичного ключа.
Составные первичные ключи используются, когда первичный ключ состоит из более чем одного атрибута, который соответствует набору отдельных персистентных свойств или полей. Составные первичные ключи должны быть определены в классе первичных ключей. Составные первичные ключи обозначаются с использованием аннотаций javax.persistence.EmbeddedId
и javax.persistence.IdClass
.
Первичный ключ или свойство или поле составного первичного ключа должны быть одного из следующих типов языка Java:
Java примитивы
Обёртки (wrapper) примитивов Java
java.lang.String
java.util.Date
(временной тип должен быть DATE
)
java.sql.Date
java.math.BigDecimal
java.math.BigInteger
Типы с плавающей точкой никогда не должны использоваться в первичных ключах. Если вы используете сгенерированный первичный ключ, учтите, что только целочисленные типы являются переносимыми.
Класс первичного ключа должен соответствовать следующим требованиям.
Модификатор доступа класса должен быть public
.
Свойства класса первичного ключа должны быть public
или protected
, если используется доступ на основе свойств.
Класс должен иметь публичный конструктор по умолчанию.
Класс должен реализовывать методы hashCode()
и equals(Object other)
.
Класс должен быть сериализуемым.
Составной первичный ключ должен включать несколько полей или свойств класса сущности или должен быть представлен как встраиваемый класс.
Если класс сопоставлен с несколькими полями или свойствами класса сущности, имена и типы полей или свойств первичного ключа в классе первичного ключа должны совпадать с именами класса сущности.
Следующий класс первичного ключа является составным ключом, и поля customerOrder
и itemId
вместе однозначно идентифицируют объект сущности:
public final class LineItemKey implements Serializable {
private Integer customerOrder;
private int itemId;
public LineItemKey() {}
public LineItemKey(Integer order, int itemId) {
this.setCustomerOrder(order);
this.setItemId(itemId);
}
@Override
public int hashCode() {
return ((this.getCustomerOrder() == null
? 0 : this.getCustomerOrder().hashCode())
^ ((int) this.getItemId()));
}
@Override
public boolean equals(Object otherOb) {
if (this == otherOb) {
return true;
}
if (!(otherOb instanceof LineItemKey)) {
return false;
}
LineItemKey other = (LineItemKey) otherOb;
return ((this.getCustomerOrder() == null
? other.getCustomerOrder() == null : this.getCustomerOrder()
.equals(other.getCustomerOrder()))
&& (this.getItemId() == other.getItemId()));
}
@Override
public String toString() {
return "" + getCustomerOrder() + "-" + getItemId();
}
/* get- и set- методы */
}
Отношения между сущностями бывают следующих типов.
Один-к-одному: каждый объект связан с одним другим объектом. Например, для моделирования физического хранилища, в котором каждая корзина содержит один виджет, StorageBin
и Widget
будут иметь отношение один к одному. Отношения «один к одному» используют аннотацию javax.persistence.OneToOne
для соответствующего персистентного свойства или поля.
Один-ко-многим: объект сущности может быть связан с несколькими объектами других сущностей. Например, заказ на продажу может содержать несколько позиций. В приложении заказа CustomerOrder
будет иметь отношение «один ко многим» с LineItem
. Отношения «один ко многим» используют аннотацию javax.persistence.OneToMany
для соответствующего персистентного свойства или поля.
Многие-к-одному: несколько объектов сущности могут быть связаны с одним объектом другой сущности. Эта множественность является противоположностью отношения «один ко многим». В только что упомянутом примере отношение к CustomerOrder
с точки зрения LineItem
является «многие к одному». Отношения «многие к одному» используют аннотацию javax.persistence.ManyToOne
для соответствующего персистентного свойства или поля.
Многие-ко-многим: объекты сущности могут быть связаны с несколькими объектами друг друга. Например, на каждом курсе колледжа есть много студентов, и каждый студент может пройти несколько курсов. Поэтому в заявке на зачисление Course
и Student
будут иметь отношение «многие ко многим». Отношения «многие ко многим» используют аннотацию javax.persistence.ManyToMany
для соответствующего персистентного свойства или поля.
Отношения могут быть двунаправленными или однонаправленными. Двунаправленное отношение имеет как собственную сторону, так и обратную сторону. Однонаправленные отношения имеют только свою сторону. Сторона-владелец связи определяет, как среда выполнения персистентности обновляет связь в базе данных.
В двунаправленном отношении каждый объект имеет поле отношения или свойство, которое ссылается на другой объект. Через поле или свойство отношения код класса сущности может получить доступ к связанному объекту. Если у сущности есть связанное поле, говорят, что сущность «знает» о своём родственном объекте. Например, если CustomerOrder
знает, какие у него есть объекты LineItem
и если LineItem
знает, к какому CustomerOrder
он принадлежит, то отношение этих сущностей двунаправленное.
Двунаправленные отношения должны следовать следующим правилам.
Обратная сторона двунаправленного отношения должна ссылаться на сторону владельца, используя элемент mappedBy
аннотаций @OneToOne
, @OneToMany
или @ManyToMany
. Элемент mappedBy
обозначает свойство или поле в объекте, который является владельцем отношения.
Двусторонняя связь «многие к одному» не должна определять элемент mappedBy
. Множественная сторона всегда является владельцем отношения.
Для двунаправленных отношений «один к одному» сторона-владелец соответствует стороне, которая содержит соответствующий внешний ключ.
Для двунаправленных отношений «многие ко многим» любая сторона может быть стороной-владельцем.
В однонаправленном отношении только один объект имеет поле или свойство отношения, которое относится к другому. Например, LineItem
будет иметь поле отношения, которое идентифицирует Product
, но Product
не будет иметь поле отношения или свойство для LineItem
. Другими словами, LineItem
знает о Product
, но Product
не знает, какие объекты LineItem
ссылаются на него.
Язык запросов персистентности Java и запросы Criteria API часто перемещаются по взаимосвязям. Направление отношения определяет, может ли запрос перемещаться от одного объекта к другому. Например, запрос может перейти от LineItem
к Product
, но не может перейти в противоположном направлении. Для CustomerOrder
и LineItem
запрос может перемещаться в обоих направлениях, поскольку эти два объекта имеют двунаправленное отношение.
Объекты, которые используют отношения, часто имеют зависимости от существования другого объекта в отношениях. Например, позиция является частью заказа. Если заказ удален, позиция также должна быть удалена. Это называется отношением каскадного удаления.
Перечислимый тип javax.persistence.CascadeType
определяет каскадные операции, которые применяются в элементе cascade
аннотаций отношений. Таблица 40-1 перечисляет каскадные операции для объектов.
Таблица 40-1 Каскадные операции для сущностей
Каскадная операция |
Описание |
|
Все каскадные операции будут применены к сущности, связанной с родительской сущностью. |
|
Если родительский объект отсоединён от контекста персистентности, связанный объект также будет отсоединён. |
|
Если родительский объект объединён с контекстом персистентности, связанный объект также будет объединён. |
|
Если родительский объект сохраняется в контексте персистентности, связанный объект также будет сохраняться. |
|
Если родительский объект обновляется в текущем контексте персистентности, связанный объект также будет обновляться. |
|
Если родительский объект удаляется из текущего контекста персистентности, связанный объект также будет удалён. |
Отношения каскадного удаления задаются с помощью указания cascade=REMOVE
для отношений @OneToOne
и @OneToMany
. Например:
@OneToMany(cascade=REMOVE, mappedBy="customer")
public Set<CustomerOrder> getOrders() { return orders; }
Когда целевой объект в отношении «один-к-одному» или «один-ко-многим» удаляется, часто желательно каскадно удалить и связанные с ним объекты. Такие связанные объекты считаются потерянными, а атрибут orphanRemoval
может использоваться для указания того, что такие потерянные объекты должны быть удалены. Например, если заказ содержит много позиций, и одна из них удалена из заказа, удалённая позиция считается потерянной. Если для orphanRemoval
установлено значение true
, сущность позиции будет удалена при удалении позиции из заказа.
Атрибут orphanRemoval
в @OneToMany
и @OneToOne
принимает логическое значение и по умолчанию имеет значение false.
В следующем примере каскадно удаляется потерянный объект order
при удалении объекта customer
:
@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<CustomerOrder> getOrders() { ... }
Встраиваемые классы используются для представления состояния объекта, но не имеют собственного персистентного идентификатора в отличие от классов объектов. Объекты встраиваемого класса используют идентификатор объекта, которому они принадлежат. Встраиваемые классы существуют только как состояние другого объекта. Сущность может иметь однозначные атрибуты или атрибуты-коллекции встраиваемых классов.
Встраиваемые классы имеют те же правила, что и классы сущностей, но снабжены аннотацией javax.persistence.Embeddable
вместо @Entity
.
Следующий встраиваемый класс, ZipCode
, имеет поля zip
и plusFour
:
@Embeddable
public class ZipCode {
String zip;
String plusFour;
...
}
Этот встраиваемый класс используется сущностью Address
:
@Entity
public class Address {
@Id
protected long id
String street1;
String street2;
String city;
String province;
@Embedded
ZipCode zipCode;
String country;
...
}
Объекты, имеющие встраиваемые классы частью своего персистентного состояния, могут аннотировать поле или свойство аннотацией javax.persistence.Embedded
, но не обязаны это делать.
Встраиваемые классы могут сами использовать другие встраиваемые классы для представления своего состояния. Они также могут содержать коллекции базовых типов Java или других встраиваемых классов. Встраиваемые классы также могут содержать отношения к другим объектам или коллекциям объектов. Если у встраиваемого класса есть такая связь, то связь идёт от целевой сущности или коллекции сущностей к сущности, которой принадлежит встраиваемый класс.
Назад | Вперёд | Содержание |