Использование Hibernate Java Persistence
Содержание
1. Основные технологии хранения данных в java2. Пример реализации Hibernate JPA
2.1. Файл настроек pom.xml проекта для Maven’а
2.2. Настройка параметров подключения к базе данных
2.3. Бизнес - модель данных
2.4. Бизнес - логика
3. Рекомендуемые ресурсы
1. Основные технологии хранения данных в java
Не секрет, что данные являются основой практически для любого проекта. На основе требований к программной системе строится модель данных. В дальнейшем именно с данной моделью работает программа, вводится некоторая информация, производятся вычисления, формируются отчеты и т. д. В процессе развития программных систем проектировались и используются различные системы управления базами данных (СУБД), иерархические, реляционные, объектные и др.
На практике наибольшую популярность получили именно реляционные модели баз данных, хотя в современных методологиях программирования пользуется популярностью объектно-ориентированное программирование. Для стыковки данных технологий разработано множество технологий, спецификаций и фреймворков для маппинга объектов на таблицы реляционных баз данных. Java разработчикам доступно множество технологий для работы с данными это может быть просто сереализация объектов, JDBC, JDO и множество других. Но каждая из них имеет ряд достоинств и недостатков.
Таблица 1. Основные технологии хранения данных в java
Поддержка | Сериализация | JDBC | ORM | ODB | EJB2 | JDO | JPA |
---|---|---|---|---|---|---|---|
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).
2. Пример реализации 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 минут можно научиться создавать новые проекты, собирать билды и т.д.
2.1. Файл настроек pom.xml проекта для Maven’а
Файл настроек 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
2.2. Настройка параметров подключения к базе данных
Проект 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.
2.3. Бизнес - модель данных
Создадим простой POJO (Plain Old Java Object) объект для хранения данных о контактах. Это типовой JavaBean с доступом к приватным полям через get и set методы. Аннотации можно “привязывать” либо к приватным молям, либо к get методам.
/******************************************************************************* * Copyright (c) 2005, 2006 Berdaflex Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ 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; /** * Contact bean. * * @author Siarhei Berdachuk */ @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 contactId) { this.contactId = contactId; } @Column(name = "FIRST_NAME") public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @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" />).
2.4. Бизнес - логика
Основным интерфейсом для работы с хранимыми объектами является 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.
/******************************************************************************* * Copyright (c) 2005, 2006 Berdaflex Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ 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();
Пример тестов, код которого можно использовать в создаваемых приложениях.
/******************************************************************************* * Copyright (c) 2005, 2006 Berdaflex Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ 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; /** * Simple JPA tests. * * @author Siarhei Berdachuk */ public class HibernateUtilsTest extends TestCase { public static final Log logger = LogFactory.getLog(HibernateUtilsTest.class .getName()); @Override protected void setUp() throws Exception { super.setUp(); } @Override protected void tearDown() throws Exception { super.tearDown(); } 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(); 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(); Contact testContact = em.find(Contact.class, id_500); assertEquals("FName_500", testContact.getFirstName()); assertEquals("LName_500", testContact.getLastName()); em.getTransaction().commit(); em.close(); } }