BERDAFLEX Software Systems
Главная > Java > Использование Hibernate Java Persistence

Пример реализации 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

Структура 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();
  }
}
Rambler's Top100 Рейтинг@Mail.ru