...
...

Использование Hibernate Java Persistence

Не секрет, что данные являются основой практически для любого проекта. На основе требований к программной системе строится модель данных. В дальнейшем именно с данной моделью работает программа, вводится некоторая информация, производятся вычисления, формируются отчеты и т.д. В процессе развития программных систем проектировались и используются различные системы управления базами данных (СУБД): иерархические, реляционные, объектные и др. На практике наибольшую популярность получили именно реляционные модели баз данных, хотя в современных методологиях программирования популярностью пользуется объектноориентированное программирование. Для стыковки данных технологий разработано множество технологий, спецификаций и фреймуорков для маппинга объектов на таблицы реляционных баз данных. Java-разработчикам доступно множество технологий для работы с данными. Это может быть просто сериализация объектов, JDBC, JDO и множество других. Но каждая из них имеет ряд достоинств и недостатков.

Таблица 1. Основные технологии хранения данных в Java



ПоддержкаСериализацияJDBCORMODBEJB2JDOJPA
Java-объектыесть нетесть есть есть есть есть
Объектноориентированный подходесть нетесть есть нетесть есть
Танзакционностьнетесть есть есть есть есть есть
Параллелизмнетесть есть есть есть есть есть
Работа с наборами данныхнетесть есть есть есть есть есть
Схема данныхнетесть есть нетесть есть есть
Хранение данных в реляционном и нереляционном форматахнетнетнетнетесть есть нет
Поддержка запросов к даннымнетесть есть есть есть есть есть
Переносимость и жесткие стандартыесть нетнетнетесть есть есть
Простотаесть есть есть есть нетесть есть


. Сериализация (Serialization) является встроенным механизмом хранения и передачи объектов в Java. Но для практической работы с данными этот подход малопригоден, так как требуется извлекать и хранить весь граф объектов, что затрудняет работу с большими объемами данных.
. Java Database Connectivity (JDBC) Application programming interface (API) разрабатывался для работы с реляционными базами данных. Минусом данной технологии является отсутствие механизмов проекции реляционных данных на объекты, что существенно увеличивает объем кода для данного преобразования.
. Object-relational mapping (ORM) представляет собой попытки различных поставщиков маппинга объектов на реляционные данные. Отсутствие стандартов привело к созданию множества реализаций данного подхода, несовместимых друг с другом. Как результат код становится непереносимым и жестко завязанным на конкретного поставщика.
. Object databases (ODB) представляют собой объектные реализации баз данных. Как и в случае с ORM, здесь множество реализаций, несмотря на попытки Object Database Management Group (ODMG) создать и стандартизировать API для доступа к объектным базам данных.
. Enterprise Java Beans (EJB) введены в Enterprise Edition платформу Java уровня предприятия. Сущности EJB 2 представляют собой компоненты для хранения в хранилищах данных. Данная технология позволяет работать с данными на уровне объектов. Варианты физического хранения данных не лимитированы только реляционными базами данных. К сожалению, в стандарте EJB 2.x лимитирован объектноориентированный подход. Это выражается в отсутствии или сложности реализации таких важных функций, как наследование, полиморфизм и внешние связи объектов.

Дополнительные проблемы возникают из-за необходимости применения дорогих и тяжеловесных серверов.
. Спецификация JDO на текущий момент является одной из самых прогрессивных и позволяет использовать не только реляционные, но и объектные хранилища данных.
. Java Persistence API (JPA) сочетает в себе простоту сериализации объектов с возможностью работы с данными на уровне объектноориентированной модели. При этом остается возможность комбинирования доступа к данным как в JDBC на уровне реляционных данных, что позволяет достичь порой большей производительности и гибкости по сравнению с JDO.
На текущий момент существует множество реализаций спецификации JPA — как коммерческих, так и свободных с открытым исходным кодом (open source).

Пример реализации Hibernate JPA

Рассмотрим пример использования реализации Hibernate JPA для простых Java Standart Edition (SE) приложений. Идея данного проекта в создании максимально упрощенной архитектуры приложения, т.е. сведении к минимуму количества всевозможных настроек и фокусировании только на поставленной задаче. Java-разработчики, которые ранее имели дело с Hibernate, смогут оценить всю мощь нововведений. Применение аннотаций для внедрения в код служебной информации позволяет освободиться от десятков служебных XML-файлов с описанием маппинга Java-бинов на таблицы баз данных. Задача: Требуется создать методы для доступа и манипулирования информацией о клиентах.

При помощи утилиты сборки проектов Maven 2 создадим базовую структуру проекта.

Примечание: Применение Maven позволяет абстрагироваться от применяемой разработчиком интегрированной среды разработки. Достаточно вызвать задачу по созданию проекта — например, mvn eclipse:eclipse для Eclipse IDE, mvn jdev:jdev для Oracle Java Developer или mvn idea:idea для Idea. Вторая особенно ценная функция Maven заключается в создании локального репозитория требуемых Java-библиотек и автоматического разрешения зависимостей. Это позволяет быстро обновлять библиотеки и устранять дублирование таковых от проекта к проекту. На сайте проекта Maven есть простое руководство, на основе которого можно получить общее представление о приемах работы с данным продуктом. За 10-15 минут можно научиться создавать новые проекты, собирать билды и т.д.

Файл настроек pom.xml проекта для Maven'а содержит наименование проекта и перечень зависимостей на требуемые библиотеки.

<project xmlns=" http://maven.apache.org/POM/4.0.0"
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.berdaflex</groupId>
<artifactId>com.berdaflex.jpa_simple_test</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>Maven Quick Start Archetype</name>
<url> http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.1.ga</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.2.1.ga</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.2.1.ga</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.3</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<version>1.0.4</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<!-- JDBC Drivers -->
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.2-504.jdbc3</version>
<type>jar</type>
<scope>runtime</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
</project>

Утилита Maven создает проект с разделением программного кода на основной код проекта (src/main/java) и код для тестов (src/test/java), что позволяет легко отделить тесты и не включать их в пакет при конечной сборке проекта. На рис. 1 показана структура созданного Java-проекта для Eclipse IDE.

Рис. 1. Структура Java-проекта com.berdaflex.jpa_simple_test

Проект Hibernate позволяет работать с большим разнообразием популярных СУБД. Постоянно ведется тестирование для следующих баз данных: . Oracle 8i, 9i, 10g
. DB2 7.1, 7.2, 8.1
. Microsoft SQL Server 2000
. Sybase 12.5 (JConnect 5.5)
. MySQL 3.23, 4.0, 4.1, 5.0
. PostgreSQL 7.1.2, 7.2, 7.3, 7.4, 8.0, 8.1
. TimesTen 5.1, 6.0
. HypersonicSQL 1.61, 1.7.0, 1.7.2, 1.7.3, 1.8
. SAP DB 7.3
. InterSystems Cache' 2007.1

Также поддерживается еще много других СУБД (при необходимости можно легко расширить базовый список и добавить свою реализацию требуемого диалекта). Для тестов выберем популярную Open Source базу данных PostgreSql. Для подключения к базе данных создадим конфигурационный файл hibernate.cfg.xml следующего содержания:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
" http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
org.postgresql.Driver
</property>
<property name="hibernate.connection.password">
manager
</property>
<property name="hibernate.connection.url">
jdbc:postgresql://localhost:5432/jpa_test
</property>
<property name="hibernate.connection.username">
postgres
</property>
<property name="hibernate.dialect">
org.hibernate.dialect.PostgreSQLDialect
</property>
<property name="current_session_context_class">thread</property>
<property name="hibernate.hbm2ddl.auto">update</property>

<mapping class="com.berdaflex.contacts.model.Contact" />
</session-factory>
</hibernate-configuration>

Параметр "hibernate.hbm2ddl.auto" устанавливаем в значение "true" для того, чтобы объекты базы данных создавались автоматически на основе маппинга в Java-Hibernate-проекте. Для работы с Hibernate и создания конфигурационных файлов удобно использовать подключаемый модуль Hibernate Tools для Eclipse. Создадим простой POJO (Plain Old Java Object) объект для хранения данных о контактах. Это типовой JavaBean с доступом к приватным полям через get- и set-методы. Аннотации можно "привязывать" либо к приватным модулям, либо к get-методам.

package com.berdaflex.contacts.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.commons.lang.builder.ToStringBuilder;

@Entity
@Table(name = "CONTACT")
public class Contact {

private Long contactId;

private String firstName;

private String lastName;

@Id
@GeneratedValue
@Column(name = "CONTACT_ID")
public Long getContactId() {
return contactId;
}

public void setContactId(Long userId) {
this.contactId = userId;
}

@Column(name = "FIRST_NAME")
public String getFirstName() {
return firstName;
}

public void setFirstName(String userName) {
this.firstName = userName;
}

@Column(name = "LAST_NAME")
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@Override
public String toString() {
return new ToStringBuilder(this)//
.append("contactId", getContactId())//
.append("firstName", getFirstName())//
.append("lastName", getLastName())//
.toString();
}

}

Использование javax.persistence аннотаций позволяет встроить маппинг сущностей используемой системы хранения (в данном случае Hibernate) непосредственно в программный код. Это снижает вероятность появления ошибок, и теперь не требуется создавать десятки XML-файлов маппинга объектов. Использование стандартизированного API позволяет сменить при необходимости библиотеку реализации Persistence API, например, с Hibernate на Oracle Toplink. Аннотация @Entity указывает, что данный класс является сущностью бизнес-модели. Аннотация @Table(name = "CONTACT") указывает на имя таблицы в базе данных. Если имя таблицы совпадает с именем класса, то его можно опустить. Для идентификации конкретной записи в базе данных требуется ключевое поле (аннотация @Id). Чаще всего для этого используется суррогатный ключ. В данном случае для автоматической генерации ключа указываем аннотацию @GeneratedValue. Маппинг атрибутов Java-бина на колонки таблиц задается при помощи аннотации @Column, в дополнительных параметрах которой можно указать наименование колонки таблицы. Если наименование колонок совпадает с именем атрибута, то его можно опустить. Классы хранимых (persistence) бинов должны быть перечислены в файле конфигурации (строка <mapping class="com.berdaflex.contacts.model.Contact" />). Основным интерфейсом для работы с хранимыми объектами является javax.persistence.EntityManager. Некая единица работы с объектами (unit of work) непосредственно осуществляется в типовой связке:

//Получаем конкретный экземпляр реализации интерфейса EntityManager
EntityManager em = getEntityManager();
//Начинаем тразакцию
em.getTransaction().begin();

//выполняем некоторую обработку бизнес-объектов
em.persist(объект);
. . .
//завершаем транзакцию
em.getTransaction().commit();
em.close();

Для того, чтобы упростить задачу получения конкретного экземпляра реализации интерфейса EntityManager, создадим класс-помощник (helper) HibernateUtil, который будет автоматически инициализировать конфигурацию Ejb3Configuration на основе созданного нами ранее файла конфигурации Hibernate.

package com.berdaflex.db.utils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.ejb.Ejb3Configuration;

public class HibernateUtil {
public static final Log logger = LogFactory.getLog(HibernateUtil.class
.getName());

private static final SessionFactory sessionFactory;

private static final Ejb3Configuration ejb3Configuration;

static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new AnnotationConfiguration().configure()
.buildSessionFactory();
ejb3Configuration = new Ejb3Configuration()
.configure("/hibernate.cfg.xml");
} catch (Throwable ex) {
logger.error("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getSessionFactory() {
return sessionFactory;
}

public static Ejb3Configuration getEjb3Configuration() {
return ejb3Configuration;
}

Для получения экземпляра EntityManager теперь можно будет использовать следующую конструкцию:
EntityManager em = HibernateUtil.getEjb3Configuration() .buildEntityManagerFactory().createEntityManager();

Пример тестов, код которых можно использовать в создаваемых приложениях.

package com.berdaflex.db;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

import junit.framework.TestCase;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.berdaflex.contacts.model.Contact;
import com.berdaflex.db.utils.HibernateUtil;

public class HibernateUtilsTest extends TestCase {
public static final Log logger = LogFactory.getLog(HibernateUtilsTest.class
.getName());
//Очищаем базу данных
private void clearData() {
logger.debug("clear test database");
EntityManager em = HibernateUtil.getEjb3Configuration()
.buildEntityManagerFactory().createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Query query = em.createQuery(new StringBuilder("delete from ").append(
Contact.class.getName()).toString());
query.executeUpdate();
tx.commit();
em.close();
}

public void testInsertData() {
clearData();

EntityManager em = HibernateUtil.getEjb3Configuration()
.buildEntityManagerFactory().createEntityManager();
em.getTransaction().begin();

//добавляем 1000 тестовых записей
Long id_500 = null;
for (int i = 0; i < 1000; i++) {
Contact newContact = new Contact();
newContact.setFirstName(new StringBuilder("FName_").append(i)
.toString());
newContact.setLastName(new StringBuilder("LName_").append(i)
.toString());
em.persist(newContact);
if (i == 500) {
id_500 = newContact.getContactId();
}
}
em.getTransaction().commit();

em.getTransaction().begin();
//извлекаем из БД 500-ю запись и проверяем ее атрибуты
Contact testContact = em.find(Contact.class, id_500);
assertEquals("FName_500", testContact.getFirstName());
assertEquals("LName_500", testContact.getLastName());
em.getTransaction().commit();

em.close();

}
}

Рекомендуемые ресурсы

. Исходный код тестового проекта: сайт
. FAQ по JPA: сайт
. Java Persistence with Hibernate: сайт
. Проект GlassFish: сайт
. Oracle Toplink JPA: сайт
. Kodo 4.0.0 Developers Guide for JPA/JDO: сайт
. JPA-аннотации в JPOX: сайт
. Проект Maven: сайт
. Каталог подключаемых модулей Eclipse: сайт
. Проект Hibernate Tools сайт

Сергей Бердачук, сайт



© Компьютерная газета

полезные ссылки
IP камеры видеонаблюдения