··@Id private String title;
··@Id private String language;
··private String content;
··// Конструкторы, геттеры, сеттеры
}
Оба варианта — @EmbeddedId и @IdClass — будут отображены в одну и ту же табличную структуру. Она определена в листинге 5.9 с использованием языка описания данных (Data Definition Language — DDL). Атрибуты сущности и первичный ключ в итоге окажутся в одной и той же таблице, а первичный ключ будет сформирован с использованием атрибутов составного класса (title и language).
create table NEWS (
····CONTENT VARCHAR(255),
····TITLE VARCHAR(255) not null,
····LANGUAGE VARCHAR(255) not null,
····primary key (TITLE, LANGUAGE)
);
Подход с использованием @IdClass более предрасположен к ошибкам, поскольку вам потребуется определить каждый атрибут первичного ключа как в @IdClass, так и в сущности, позаботившись об использовании одинакового имени и Java-типа. Преимущество заключается в том, что вам не придется изменять код класса первичного ключа (никаких аннотаций не потребуется). Например, вы могли бы использовать унаследованный класс, который по причинам правового характера вам не разрешено изменять, однако при этом вам предоставляется возможность его повторного использования.
Одно из видимых отличий заключается в способе, которым вы ссылаетесь на сущность при использовании JPQL. В случае с @IdClass вы сделали бы что-то вроде следующего:
select n.title from News n
А в случае с @EmbeddedId у вас получилось бы что-то вроде показанного далее:
select n.newsId.title from News n
Атрибуты
У сущности должен иметься первичный ключ (простой или составной), чтобы у нее был идентификатор в реляционной базе данных. Она также обладает всевозможными атрибутами, которые обуславливают ее состояние и должны быть отображены в таблицу. Это состояние способно включать почти любой Java-тип, который вам может потребоваться отобразить:
• примитивные Java-типы и классы-обертки (int, double, float и т. д.) (Integer, Double, Float и т. д.);
• массивы байтов и символов (byte[], Byte[], char[], Character[]);
• строковые, связанные с большими числами, и временные типы (java.lang.String, java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp);
• перечислимые типы, а также определяемые пользователем типы, которые реализуют интерфейс Serializable;
• коллекции базовых и встраиваемых типов.
Разумеется, у сущности также могут иметься атрибуты сущности, коллекции сущностей или встраиваемые классы. Это требует рассказа о связях между сущностями (которые будут рассмотрены в разделе «Отображение связей»).
Как вы уже видели ранее, при конфигурации в порядке исключения атрибуты отображаются с использованием правил отображения по умолчанию. Однако иногда возникает необходимость настроить детали этого отображения. Именно здесь в дело вступают аннотации JPA (или их XML-эквиваленты).
Опциональная аннотация @javax.persistence.Basic (листинг 5.10) является отображением в столбец базы данных, относящимся к самому простому типу, поскольку она переопределяет базовое постоянство.
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Basic {
··FetchType fetch() default EAGER;
··boolean optional() default true;
}
У этой аннотации есть два параметра: optional и fetch. Элемент optional подсказывает вам, может ли null быть значением атрибута. Однако он игнорируется для примитивных типов. Элемент fetch может принимать два значения: LAZY или EAGER. Он намекает на то, что во время выполнения поставщика постоянства выборка данных должна быть отложенной (только когда приложение запрашивает соответствующее свойство) или быстрой (когда сущность изначально загружается поставщиком).
Возьмем, к примеру, сущность Track, показанную в листинге 5.11. В CD-альбом входит несколько треков, имеющих название, описание и WAV-файл определенной длительности, который вы можете прослушать. WAV-файл представляет собой BLOB-объект, который может иметь объем несколько мегабайт. Вам не нужно, чтобы при доступе к сущности Track WAV-файл тут же быстро загружался; вы можете снабдить атрибут аннотацией @Basic(fetch = FetchType.LAZY), и извлечение информации из базы данных будет отложенным (например, только при доступе к атрибуту wav с использованием его геттера).
@Entity