Java Persistency API (JPA) 2.0 также известная как JSR-317 зарелизилась совсем недавно (10 декабря 2009) и до прошлой недели единственным ORM, который полностью реализовывал эту спецификацию, был EclipseLink. Это отличный фреймворк, который, судя по отзывам в сети, работает быстрее чем Hibernate. Однако, на прошлой неделе появился Hibernate 3.5, полностью реализующий спецификацию JPA 2.0. В этой статье я кратко расскажу о новых возможностях JPA 2.0 и Hibernate 3.5.

Вот несколько основных нововведений:

  1. опция orphanRemoval;
  2. аннотация ElementCollection;
  3. аннотация CollectionTable.

Подключение Hibernate 3.5 к проекту

Подключать Hibernate к проекту мы будем как всегда, с помощью Maven 2. Чтобы все заработало, нам понадобится подключить репозиторий JBoss:

<repository>
    <id>JBoss-Maven-Repository</id>
    <name>JBoss Maven Repository</name>
    <url>http://repository.jboss.org/maven2</url>
</repository>

Теперь нужно подключить все необходимые зависимости:

<properties>
    <hibernate-core-version>3.5.0-Final</hibernate-core-version>
</properties>
 
...
 
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>${hibernate-core-version}</version>
</dependency>
 
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-annotations</artifactId>
    <version>${hibernate-core-version}</version>
</dependency>
 
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>${hibernate-core-version}</version>
</dependency>
 
<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.9.0.GA</version>
</dependency>
 
<dependency>
    <groupId>org.apache.derby</groupId>
    <artifactId>derbyclient</artifactId>
    <version>10.5.3.0_1</version>
</dependency>
 
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.5.8</version>
</dependency>

Здесь я подключил еще JDBC-драйвер Apache Derby, так как использую его для тестов.

В результате должно получиться такое дерево зависимостей:

Дерево зависимостей Hibernate

Дерево зависимостей Hibernate

Я использую Hibernate 3.5 в JSF-проекте, поэтому на картинке вы видите еще и зависимости JSF. Можете просто не обращать на них внимание.

В документации к Hibernate 3.5 говорится что ядро Hibernate поставляется с Hibernate annotations и Entity Manager’ом, однако, после скачивания зависимостей Maven’ом я заметил, что классы, отвечающие за работу аннотаций и Entity Manager в Core отсутствуют. После недолгих плясок с бубном, выяснилось, что при вытягивании зависимостей из JBoss Maven репозитория, эти пакеты действительно отсутствуют в ядре, а вот если скачать zip-архив hibernate-distribution-3.5.0-Final-dist.zip с сайта JBoss (точнее, с SourceForge), то пакеты annotations и ejb действительно присутствуют в Core. Именно поэтому я добавил в POM зависимости Hibernate Annotations и Hibernate Entity Manager.

Как выяснилось, объясняется это тем, что теперь эти проекты живут в одном SVN-проекте и имеют одинаковый цикл выпуска, а значит и версия у всех этих библиотек одинаковая. Поэтому, я вынес версию библиотек Hibernate в pom.xml в properties.

Стандартные свойства

В более ранних спецификациях JPA (до 2.0) не было определено ни каких стандартных имен для свойств в persistence.xml, поэтому каждый поставщик реализации JPA, должен был сам определить имена свойств. В JPA 2.0 определен небольшой набор стандартных свойств. Сейчас, при настройке persistence.xml можно использовать как имена свойств поставщика реализации (в нашем случае Hibernate), так и стандартизированные имена.

Выглядят они так:

  1. javax.persistence.jdbc.driver (В Hibernate: hibernate.connection.driver_class)
  2. javax.persistence.jdbc.user (В Hibernate: hibernate.connection.username)
  3. javax.persistence.jdbc.password (В Hibernate: hibernate.connection.password)
  4. javax.persistence.jdbc.url (В Hibernate: hibernate.connection.url)

Думаю, объяснять тут ничего не нужно, т.к. имена свойств говорят сами за себя.

Вот как выглядит мой persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
 
    <persistence-unit name="topcodeCorePersistenceUnit" transaction-type="RESOURCE_LOCAL">
 
        <class>ru.topcode.entity.User</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
 
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/topcode_db"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect"/>
 
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="root"/>
 
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
        </properties>
    </persistence-unit>
</persistence>

Новые функции маппинга и аннотации

В JPA 2.0 есть ряд новых аннотаций.

Удаление сирот с помощью атрибута orphanRemoval

Сирота – это объект, родительский объект которого был удален. В предыдущей версии JPA не было ни какого эквивалента хибернейтовского DELETE_ORPHAN в cascade type. В JPA 2.0 такое поведение (удаление сирот) можно определить с помощью атрибута orphanRemoval аннотаций ManyToOne и OneToMany. Спецификация определяет два различных сценария поведения orphanRemoval:

  • Если целевая сущность была оторвана от сущности-владельца коллекции (один-ко-многим) или ссылке присвоен null, то она (целевая сущность) будет удалена из базы во время выполнения flush.
  • Если родительская сущность была удалена, то целевая сущность тоже будет удалена . Другими словами, если orphanRemoval = true, то устанавливать cascade = REMOVE не имеет смысла, т.к. каскадное удаление произойдет в результате применения правила orphanRemoval = true.

Вот пример аннотирования:

@OneToMany(mappedBy="customer",cascade=CascadeType.PERSIST, fetch=FetchType.LAZY, orphanRemoval=true)
@BatchSize(size=100)
private Set<Order> orders = new HashSet<Order>();

Маппинг коллекции элементов с помощью аннотации ElementCollection

Еще одно нововведение в JPA 2.0 – это эквивалент аннотации CollectionOfElements в Hibernate: аннотация @ElementCollection. С помощью этой аннотации можно замапить коллекцию простых типов или встраиваемых (embeddable) объектов. Вот пример простого маппинга:

public class Customer {
 ....
@ElementCollection
private Collection<String> hobbies = new HashSet<String>();

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

  1. Имя таблицы будет “customer_hobbies”
  2. Таблица будет состоять из двух столбцов: “customer_id” – идентификатор клиента, имеющего хобби и “hobbies” – значение хобби. Для каждого элемента в коллекции будет создаваться строка в таблице.

По умолчанию, имя колонки для embedded-данных генерится из имен атрибутов embedded-класса или из имени коллекции (в нашем случае hobbies) для простых типов. Это можно изменить, проаннотировав свойство с типом встроенного класса аннотацией @AttributeOverride или @Column для простых типов:

public class Customer {
 ....
@ElementCollection @Column(name="HOBBY_DATA")
private Collection<String> hobbies = new HashSet<String>();

Настроить имя таблицы можно с помощью новой аннотации @CollectionTable:

public class Customer {
 ....
@ElementCollection
@Column(name="HOBBY_NAME")
@CollectionTable(name="HOBBIES", joinColumns=@JoinColumn(name="CUSTID"))
private Collection<String> hobbies = new HashSet<String>();

А так для встраиваемых типов:

@ElementCollection
@CollectionTable(name="CUST_ADDITIONAL_ADDRS")private List<Address> additionalAddresses = new ArrayList<Address>();

На сегодня все.

Источник: Hibernate 3.5 – JPA 2.0 New Features (part 1)