Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Распределенные сервис-ориентированные системы..pdf
Скачиваний:
16
Добавлен:
05.02.2023
Размер:
9.2 Mб
Скачать

3.2 Технология JPA

Технология JPA (Java Persistence API) является наиболее современной и развиваемой технологией программной платформы Java EE.

Проблема современного программирования приложений, работающих с базами данных, состоит в постоянном преобразовании данных, содержащихся в объектах, в представления табличных данных, хранящихся под управлением той или иной СУБД.

Идея технологии JPA состоит во взаимодействии с СУБД на уровне объектов (сущностей, entity), предоставляя программистам максимальное использование языка Java. Это достигается тремя теоретическими положениями, которые мы кратко рассмотрим в данном подразделе:

а) формализацией понятия сущности и обеспечение ее описания адекватными средствами Java EE;

б) отображением класса сущности на релляционное представление ORM (Object-Relational Mapping);

в) концепцией менеджера сущностей и языка запросов JPQL (Java Persistence Query Language).

Посколку тема технологии JPA — достаточно емкая и не является основной темой нашей дисциплины, то изложенный учебный материал демонстрирует только основные ее моменты, достаточные для понимания самой идеи и реализации тестового примера, заявленного в предыдущем подразделе.

3.2.1 Сущности

Сущности (Entity) — это специальным образом аннотированные JAVA-классы, воспринимаемые контейнерами платформы Java EE.

Формально, класс-сущность должен удовлетворять следующим ограничениям:

1.Класс должен быть снабжен аннотацией @javax.persistence.Entity или быть обозначен в XML-дескрипторе как сущность.

2.Для обозначения простого первичного ключа должна быть использована аннотация @javax.persistence.Id.

3.Класс должен располагать конструктором без аргументов, который должен быть public или protected, но также может иметь другие конструкторы с аргументами.

4.Должен быть классом верхнего уровня. Перечисление или интерфейс не

147

могут быть обозначены как сущность.

5.Не должен быть final и ни один из методов или постоянные переменные экземпляра класса-сущности тоже не могут быть final.

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

Фактически, класс-сущность — это сериализованный POJO-класс, который расширен бизнес-методами до потребностей приложений.

Чтобы не быть голословным, рассмотрим класс Letter, представленный ранее на листинге 3.1, и преобразуем его в сущность двумя аннотациями, как это показано на листинге 3.13.

Листинг 3.13 — Преобразование класса Letter.java проекта lab4 в сущность

package rsos.lab4;

import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date;

import javax.persistence.Entity; import javax.persistence.Id;

@Entity

public class Letter implements Serializable

{

// Идентификатор сериализации

private static final long serialVersionUID = 1L;

@Id

private int id;

private Date date; private String name; private String text;

/** * Конструкторы, геттеры, сеттеры и другие бизнес-методы */

Первая аннотация @Entity указывает, что класс Letter является сущностью и будет отображаться в таблицу базы данных с именем LETTER.

Вторая аннотация @Id указывает, что целочисленный атрибут id является уникальным ключем таблицы LETTER.

Дальнейшие модификации сущности Letter определяются многочисленными аннотациями объектно-реляционного отображения.

148

3.2.2 Объектно-реляционное отображение

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

На практике, возможности ORM ограничены возможностями провайдера JPA и особенностями конкретной СУБД.

Для СУБД Derby отображение простейших объектных типов представлено в таблице 3.1.

Таблица 3.1 — Отображение типов языка Java в типы СУБД Derby

Тип языка Java

Тип СУБД Derby

Boolean

SMALLINT

Integer

INTEGER

Long

BIGINT

Float

FLOAT

String

VARCHAR

Double

DOUBLE

Часто разработчику приложений требуется модифицировать уже существующие сущности к уже существующим таблицам базы данных. Например, SQL-сценарий create_lab4db.sql, представленный на рисунке 3.5, создает учебную таблицу t_letter. Чтобы полностью согласовать сущность Letter и существующую базу данных lab4db, следует использовать аннотации:

а) @Table(name = "t_letter") — изменяет имя используемой таблицы;

б) @GeneratedValue(strategy = GenerationType.IDENTITY) — задает стратегию генерации первичного ключа.

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

Листинг 3.14 — Полностью адаптированная сущность Letter.java

package rsos.lab4;

import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;

149

import javax.persistence.Table;

@Entity

@Table(name = "t_letter")

public class Letter implements Serializable

{

// Идентификатор сериализации

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id;

private Date date; private String name; private String text;

/** * Конструкторы, геттеры, сеттеры и другие бизнес-методы */

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

аннотаций @Embeddable/@EmbeddedId или одной аннотацией @IdClass, в зависимости от используемого метода.

Мы не будем подробно рассматривать тему первичных ключей, а интересующихся отправляем, например, к литературному источнику [17]. Мы также не будем рассматривать многие другие полезные аннотации, а оганичимся только тремя: @Column, @Temporal и @Transient.

@javax.persistence.Column — аннотация, позволяющая изменять свойства атрибутов сущности при отражении в столбцы таблицы базы данных. Ее формальное определение представлено на листинге 3.15.

Листинг 3.15 — Полностью адаптированная сущность Letter.java

@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Column {

String name() default "";

// имя столбца

boolean unique() default false;

// уникальность

boolean nullable() default true;

 

boolean insertable() default true;

 

boolean updatable() default true;

 

String columnDefinition() default "";

String table() default "";

 

int length() default 255;

// длина по умолчанию

int precision() default 0;

// десятичная точность

int scale() default 0;

// десятичная система счисления

}

Она позволяет изменять отражаемое имя столбца таблицы, делать его

150

уникальным, ненулевым, неадаптируемым и многое другое. Например, если мне необходимо, чтобы в таблице t_letter: столбец DATE не принимал значение null; атрибут сущности name отображался в столбец USER; столбец TEXT имел размер 4096 символов (не более 32.672 символа), тогда содержимое листинга 3.14 следует заменить на содержимое листинга 3.16.

Листинг 3.16 — Сущность Letter.java с аннотациями @Column

@Entity

@Table(name = "t_letter")

public class Letter implements Serializable

{

// Идентификатор сериализации

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id;

@Column(nullable = false)

private Date date;

@Column(name = "USER")

private String name;

@Column(length = 4096) private String text;

/** * Конструкторы, геттеры, сеттеры и другие бизнес-методы */

@javax.persistence.Temporal — аннотация, позволяющая правильно отображать JAVA-типы java.util.Date и java.util.Calendar в типы java.sql.Date

(только дата), java.sql.Time (только время) и java.sql.Timestamp (дата и время), которыми пользуется СУБД Apache Derby. Эта аннотация использует константы: TemporalType.DATE, TemporalType.TIME и TemporalType.TIMESTAMP. Например, если учесть, что столбец DATE таблицы t_letter определен как timestamp (см. рисунок 3.5), то листинг 3.16 следует переписать в виде, представленном листингом 3.17.

Листинг 3.17 — Сущность Letter.java с аннотацией @Temporal

@Entity

@Table(name = "t_letter")

public class Letter implements Serializable

{

// Идентификатор сериализации

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id;

151

@Column(nullable = false)

@Temporal(TemporalType.TIMESTAMP)

private Date date; private String name; @Column(length = 4096) private String text;

/** * Конструкторы, геттеры, сеттеры и другие бизнес-методы */

@javax.persistence.Transient — аннотация, позволяющая отмечать те атрибуты сущности, которые не должны отражаться в таблице базы данных.

Например, если предположить, что в таблице t_letter отсутствует столбец с именем NAME, то сущность Letter.java должна быть описана, как показано на листинге 3.18.

Листинг 3.18 — Сущность Letter.java с аннотацией @Transient

@Entity

@Table(name = "t_letter")

public class Letter implements Serializable

{

// Идентификатор сериализации

private static final long serialVersionUID = 1L;

@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id;

@Column(nullable = false) @Temporal(TemporalType.TIMESTAMP) private Date date;

@Transient private String name;

@Column(length = 4096) private String text;

/** * Конструкторы, геттеры, сеттеры и другие бизнес-методы */

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

Наиболее правильным является отображение таблицы t_letter, которое представленно на листинге 3.17.

152