Рассмотрим пример использования реализации 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 методам.
/******************************************************************************* * 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" />).
Основным интерфейсом для работы с хранимыми объектами является 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(); } }