Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2013_1 / КСТ / Разработка веб-приложений.pdf
Скачиваний:
160
Добавлен:
23.02.2015
Размер:
2.74 Mб
Скачать

11. Введение в Java Persistence API

Java Persistence API обеспечивает разработчикам программ Java объектно-

реляционное отображение для манипулирования реляционными данными. Java Persistence состоит из четырех частей:

Java Persistence API — библиотеки классов и методов;

Query language — язык запросов, близкий SQL;

Java Persistence Criteria API — средства запросов без SQL;

Object-relational mapping metadata — средства отображения объектов в БД.

Сущности являются облегченными объектами домена хранения. Обычно сущ-

ность представлена таблицей в реляционной базе данных и каждый экземпляр сущно-

сти соответствует строке таблицы. Первичным понятием в технологии является класс

сущности, хотя сущности могут использовать вспомогательные классы.

Хранимое состояние сущности представлено полями или свойствами. Поля или

свойства используются аннотациями для задания объектно-реляционного соответствия при отображении сущностей и отношений в реляционные таблицы в хранилище

данных.

11.1. Требования к классам сущностей

Классы сущности должны удовлетворять следующим требованиям.

1.Класс должен иметь аннотацию класса javax.persistence.Entity.

2.Класс должен среди прочих иметь конструктор public или protected без пара-

метров.

3.Класс не должен иметь декларацию final. Не должно быть декларации final у методов или переменных экземпляра.

4.Если экземпляр сущности передается как несвязанный объект, как сессионный бин удаленного бизнес-экземпляра, то класс должен реализовывать ин-

терфейс Serializable.

5.Сущности могут расширять как сущностные, так и несущностные классы. Несущностные классы могут расширять сущностные классы.

6.Хранимые переменные экземпляров должны объявляться как private, protected или package-private и могут напрямую быть доступны только из методов класса. Клиенты могут получить доступ к состоянию сущности через

бизнес-методы (get/set) или средства доступа (accessor).

11.2.Xранимые поля и свойства

вклассах сущностей

Xранимое (persistent) состояние объекта может быть доступно через перемен-

ные экземпляра сущности или через свойства JavaBean. Поля или свойства должны быть следующего класса:

1.Java primitive types — примитивные типы;

2.java.lang.String — строки;

3.Другие сериализуемые классы:

Wrappers of Java primitive types — обертки простых типов;

111

java.math.BigInteger

— длинные целые;

java.math.BigDecimal

— длинные десятичные;

java.util.Date

— даты;

java.util.Calendar

— календарный тип;

java.sql.Date

— даты в СУБД;

java.sql.Time

— время в СУБД;

java.sql.TimeStamp

— время и дата в СУБД;

User-defined serializable types — пользовательские хранимые типы;

byte[]

— массив байт;

Byte[]

— массив байт;

char[]

— массив символов;

Character[]

— массив символов;

Enumerated types

— перечисления;

4.Other entities and/or collections of entities — другие сущности и/или их коллекции;

5.Embeddable classes — встраиваемые классы.

Сущности могут использовать хранимые поля, хранимые свойства или их ком-

бинацию. Если аннотации отображения применены к переменным экземпляра сущности, то объект использует хранимые поля. Если аннотации отображения применены к get-методам для свойств JavaBeans, то сущность использует хранимые свойства.

11.2.1. Хранимые поля

Для работы с хранимыми полями аннотации объектно-реляционного отображе-

ния должны быть применены к переменным экземпляра. Если класс объекта использует хранимые поля, получатель во время исполнения имеет непосредственный доступ к переменным экземпляра класса сущности. Все поля, не аннотированные javax. persistence.Transient или не помеченные как Java transient, будут передаваться в хра-

нилище данных.

11.2.2. Хранимые свойства

Для работы со свойствами объектов аннотации объектно-реляционных отобра-

жений для хранимых свойств должны быть применены к методам get... Аннотации отображений не могут быть применены к полям или свойствам, аннотированным как @Transient или помеченным как transient (транзитные, не хранимые данные). Если сущность использует хранимые свойства, она должна следовать соглашениям для методов компонентов JavaBeans. Свойства в стиле JavaBeans используют методы get

иset, обращение к которым обычно записывается после имени экземпляра сущно-

сти. Для каждого хранимого свойства Property типа Type есть метод getProperty и метод setProperty. Если свойство логическое, вы можете использовать isProperty вместо getProperty. Например, если объект Customer использует хранимые свойства и имеет

переменнуюprivate экземпляраfirstName,вклассеопределяютсяметодыgetFirstName

иsetFirstName для запроса и установки состояния свойства экземпляра firstName.

Сигнатура метода для хранимых свойств с простым значением выглядит сле-

дующим образом:

Type getProperty()

void setProperty(Type value)

112

11.2.3. Использование коллекций в полях и свойствах сущностей

Хранимые поля и свойства со значениями коллекций должны использовать ин-

терфейсы коллекций Java независимо от того, что объект использует хранимые поля

или свойства. Могут быть использованы следующие интерфейсы коллекций:

java.util.Collection,

java.util.Set,

java.util.List,

java.util.Map.

Если класс объекта использует хранимые поля, тип в вышеуказанных сигнату-

рах метода должен быть одним из этих типов коллекций. Варианты коллекций этих

типов также могут использоваться. Например, если объект Customer имеет хранимое

свойство, которое содержит множество номеров телефонов, у него должны быть сле-

дующие методы:

Set<PhoneNumber> getPhoneNumbers() { ... } void setPhoneNumbers(Set<PhoneNumber>) { ... }

Если поле или свойство объекта состоит из коллекции основных типов или

встраиваемых (embeddable) классов, в поле или свойстве используйте аннотацию javax.persistence.ElementCollection.

Аннотация @ElementCollection имеет два атрибута: targetClass и fetch. Атрибут targetClass определяет имя базового или встроенного класса и является необязательным, если поле или свойство определены с использованием языка программирования

Java. Дополнительный атрибут fetch используется для определения: должна ли кол-

лекция извлекаться способом lazily или eagerly, используя соответственно константы

LAZY или EAGER класса javax.persistence.FetchType. По умолчанию коллекция выби-

рается в режиме lazily.

Следующая сущность, Person, имеет хранимое поле nicknames — коллекция

класса String, которая выбирается в режиме eagerly. Элемент targetClass не потребо-

вался, поскольку используется наследование имени класса, чтобы определить поле.

@Entity

public class Person {

...

@ElementCollection(fetch=EAGER)

protected Set<String> nickname = new HashSet();

...

}

Использование коллекций Map в сущностях

Коллекции элементов сущностей и связей могут быть представлены классом java.util.Map. Карта включает ключ и значение.

При использовании элементов Map или связей следует применять следующие

правила:

Ключ или значение может быть основным типом языка программирования

Java, встроенного (embeddable) класса или сущностью.

Когда применяется значение встроенного класса или основного типа, исполь-

зуется аннотация @ElementCollection.

Когда значение является сущностью, используется аннотация @OneToMany.

Можно использовать тип Map на одной стороне двунаправленного отношения.

113

Если тип ключа является основным типом языка программирования Java, используется аннотация javax.persistence.MapKeyColumn, чтобы задать столбец, отображаемый как ключ. По умолчанию, имя атрибута @MapKeyColumn имеет форму

RELATIONSHIPFIELD/PROPERTY NAME_KEY. Например, если имя поля — ссылка на отношение image, по умолчанию атрибут имени — IMAGE_KEY.

Если тип ключа является объектом, используется аннотация javax.persistence. MapKeyJoinColumn. Если многие столбцы нуждаются в отображении, используйте аннотацию javax.persistence.MapKeyJoinColumns, чтобы включать кратные аннотации

@MapKeyJoinColumn. Если @MapKeyJoinColumn отсутствует, отображение имени столбца по умолчанию установлено в RELATIONSHIP FIELD/PROPERTY NAME_KEY.

Например, если имя поля отношения — employee, то по умолчанию имя атрибута —

EMPLOYEE_KEY.

Если общие типы языка программирования Java не использованы в поле или

свойстве отношения, класс ключа должен быть явно установлен с использованием аннотации javax.persistence.MapKeyClass.

Если ключ карты — первичный ключ, или хранимое поле, или значение карты – свойство объекта, используют аннотацию javax.persistence.MapKey. Аннотации @MapKeyClass и @MapKey не могут быть использованы для одинакового поля или

свойства.

Если значение Map является основным типом Java или встроенного класса, оно будет отображено в коллекцию таблицы в основной базе данных. Если общие типы не

использованы, аннотация @ElementCollection атрибута targetClass должна быть уста-

новлена в тип значения Map.

ЕслизначениеMap —сущностьичастьоднонаправленногоотношения«многие-

ко-многим» или «один-ко-многим», то оно будет отображено как присоединенная таблица в основной базе данных. Однонаправленное отношение «один-ко-многим», которое использует Map, может также быть отображено с использованием аннотации

@JoinColumn.

Если объект является частью двунаправленного отношения «один-ко-

многим»/«многие-к-одному», он будет отображен в таблицу сущности, которая представляет значение Map. Если общие типы не использованы, атрибут targetEntity аннотации @OneToMany и @ManyToMany должен быть установлен в тип значения Map.

11.2.4. Первичные ключи сущности

Каждый объект имеет уникальный идентификатор. Объект «клиент», например,

может идентифицироваться номером клиента. Уникальный идентификатор, или первичный ключ, позволяет локализовать конкретный экземпляр объекта. Каждый объект

должен иметь первичный ключ. Сущность может иметь простой или составной первичный ключ.

Простые первичные ключи используют аннотацию javax.persistence.Id, чтобы

задавать свойство или поле.

Составные первичные клавиши используются, когда первичный ключ состоит из более чем одного атрибута, которые соответствуют набору уникальных хранимых свойств или полей. Составные первичные ключи должны быть определены в классе первичного ключа. Составные первичные ключи описываются с использованием анно-

тации javax.persistence.EmbeddedId и javax.persistence.IdClass.

114

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

Java primitive types

— простой тип Java;

Java primitive wrapper types — простой тип оболочки Java;

java.lang.String

— строки;

java.util.Date

— даты пакета util;

java.sql.Date

— даты языка SQL;

java.math.BigDecimal

— большие десятичные числа;

java.math.BigInteger

— большие целые числа.

Типы с плавающей точкой никогда не должны использоваться в первичных клю-

чах. Если вы используете автоматически генерируемый первичный ключ, то наиболее приемлемыми будут только целые типы.

Классы первичных ключей

Классы первичных ключей должны удовлетворять требованиям:

Модификатор доступа класса должен быть public.

Свойства первичного ключевого класса должны быть public или protected, если используется доступ на основе свойств.

Класс должен иметь конструктор public по умолчанию.

Класс должен реализовывать методы hashCode() и equals(Object other).

Класс должен быть сериализуемым.

Составной первичный ключ должен быть представлен и отображен в набор полей или свойств объекта класса или должен быть представлен и отображен

как встроенный класс.

Если классы отображаются в набор полей или свойств объекта класса, имена и типы первичных ключевых полей или свойств в первичном ключевом классе должны соответствовать им в классе объекта.

Следующий класс первичного ключа является составным полем: orderId и itemId

вместе однозначно идентифицируют сущность.

public final class LineItemKey implements Serializable { public Integer orderId;

public int itemId;

public LineItemKey() {}

public LineItemKey(Integer orderId, int itemId) { this.orderId = orderId;

this.itemId = itemId;

}

public boolean equals(Object otherOb) { if (this == otherOb) {

return true;

}

if (!(otherOb instanceof LineItemKey)) { return false;

}

LineItemKey other = (LineItemKey) otherOb; return (

115

(orderId==null?

other.orderId==null:orderId.equals

(other.orderId)

)

&&

(itemId == other.itemId)

);

}

public int hashCode() {

return ( (orderId==null?0:orderId.hashCode()) ^ ((int) itemId) );

}

public String toString() {

return "" + orderId + "-" + itemId;

}

}

11.2.5. Множественность отношений сущностей

Есть четыре типа множественных отношений: взаимно-однозначное, «один-ко- многим», «многие-к-одному» и «многие-ко-многим». Обычно такие отношения исполь-

зуют внешние ключи в БД.

Взаимно-однозначное отношение: каждый экземпляр сущности обусловливается единственным экземпляром другой сущности. Например, чтобы моделировать физический склад, в котором каждая ячейка содержит единственный предмет, поля StorageBin и Widget должны иметь взаимно-однозначное отношение. Взаимно-

однозначные связи используют аннотацию javax.persistence.OneToOne в соответству-

ющем хранимом свойстве или поле.

Отношение «один-ко-многим»: экземпляр сущности может обуславливаться многими экземплярами других объектов. Бланк заказа продаж, например, может иметь несколько строк. В приложении поле Order должно иметь отношение

«один-ко-многим» с LineItem. Отношение «один-ко-многим» использует аннотацию javax.persistence.OneToMany в соответствующем хранимом свойстве или поле.

Отношение «многие-к-одному»: многие экземпляры сущности могут обуславливаться единственным экземпляром другого объекта. Эта множественность противо-

положна отношению «один-ко-многим». В только что упомянутом примере отношение

из LineItem в Order — «многие-к-одному». Использование связи «многие-к-одному» требует аннотации javax.persistence.ManyToOne в соответствующем хранимом свой-

стве или поле.

Отношение «многие-ко-многим»: экземпляры сущности могут обусловить друг другамногимиэкземплярами.Например,вколледжекаждуюдисциплинуизучаютмно-

го студентов, и каждый студент может изучать несколько дисциплин. Следовательно,

в приложении для учета студентов таблицы Course и Student должны иметь отно-

шение «многие-ко-многим». Отношение «Многие-ко-многим» использует аннотацию javax.persistence.ManyToMany в соответствующем хранимом свойстве или поле.

116

11.2.6. Направление связей сущностей

Направление отношения может быть или двунаправленным, или однонаправленным. Двунаправленное отношение имеет как собственную сторону, так и проти-

воположную. Однонаправленное отношение имеет только собственную сторону.

Собственная сторона отношения определяет, как во время исполнения приложения

хранилище делает коррекцию отношений в базе данных.

Двунаправленное отношение

В двунаправленном отношении каждый объект имеет поле или свойство, свя-

занное с другим объектом. Через поле или свойство отношения код объекта может

иметь доступ к своему связанному объекту. Если объект имеет связанное поле, тогда

он знает о своем связанном объекте. Например, если объект Order знает об экзем-

пляре LineItem и если LineItem знает, какой объект Order ему принадлежит, тогда у них есть двунаправленное отношение.

Двунаправленное отношение должно следовать правилам:

Противоположная сторона двунаправленного отношения должна ссы-

латься на сторону собственника, используя элемент mappedBy аннотации

@OneToOne, @OneToMany или @ManyToMany. Элемент mappedBy опреде-

ляет свойство или поле на объекте – владельце отношения.

Множественная сторона двунаправленной связи «много-к-одному» не должна определять элемент mappedBy. Эта сторона является всегда стороной собственника отношения.

Для взаимно-однозначной двунаправленной связи собственная сторона соот-

ветствует стороне, которая содержит внешний ключ (foreign key).

Для связи «многие-ко-многим» любая сторона двунаправленной связи может быть собственной стороной.

Однонаправленное отношение

В однонаправленном отношении только один объект имеет поле или свойство, имеющее ссылку на другой объект. Например, объект LineItem должен иметь поле,

которое идентифицирует Product, но Product не имеет поля или свойства для ссылки

на LineItem. Другими словами, LineItem знает о продукте, но продукт не знает, какие экземпляры LineItem ссылаются на него.

Запросы и направление отношения

Язык запросов Java Persistence и запросы Criteria API часто используют навига-

цию объектов по связям. Направление отношения определяет, может ли запрос перей-

ти от одного объекта к другому. Например, запрос может перейти от LineItem к Product, но не может переходить в противоположном направлении. Для Order и LineItem запрос мог бы переходить в обоих направлениях, поскольку эти два объекта имеют двуна-

правленное отношение.

Каскадные операции и отношения

Объекты, которые используют отношение, часто зависят от существования дру-

гого объекта в отношении. Например, пункт — часть заказа, и если заказ удален, тогда пункт должен быть также удален. Это называется каскадным удалением отношения.

Класс javax.persistence.CascadeType перечисляет типы каскадных операций, которые применимы в элементе аннотации cascade отношения (табл. 11.1).

Каскадное удаление отношения определяется операцией cascade=REMOVE

для отношения @OneToOne и @OneToMany. Например:

@OneToMany(cascade=REMOVE, mappedBy="customer") public Set<Order> getOrders() { return orders; }

117

Соседние файлы в папке КСТ