Пример использования Hibernate в качестве JPA провайдера совместно со Spring Framework

Ни для кого не секрет, что в настоящий момент Hibernate является стандартом “de facto” в ORM-мире. В то же самое время промышленным стандартом “de jure” является JPA – с этим тоже сложно поспорить. Более того, начиная с версии 3.6.* Hibernate полностью поддерживает стандарт JPA (разных версий), а это значит, что есть возможность вести разработку на базе промышленного стандарта, не отказываясь от любимого многими инструмента. Тем не менее, в сети довольно мало компактных и легких для понимания примеров, иллюстрирующих эту возможность, особенно когда идет речь о связке со Spring Framework. Именно ей (связке Spring + Hibernate as JPA) и будет посвящена эта статья.

Вступление

Как я уже писал выше примеров иллюстрирующим работу, именно в связке, не так много, чаще всего это будут примеры работы Spring + Hibernate, или Spring + JPA, или Hibernate as JPA провайдер, etc.

Ниболее простое и доступное описание использования Hibernate аки JPA вы найдете прямо в дистрибутиве: {HIBERNATE_HOME}/documentation/quickstart/en-US/html/hibernate-gsg-tutorial-jpa.html (сам пример, находится тут:  {HIBERNATE_HOME}/project/documentation/src/main/docbook/quickstart/tutorials/entitymanager/ ) . Т.к. в моей статье, за основу взят именно он, крайне желательно, чтобы к моменту прочтения следующих строк вы уже ознакомились с этим примером.

[ FYI: ] У SpringSource есть отдельный проект, дополняющий возможности базового Spring ORM и сфокусированный на поддержке JPA – Spring Data JPA: http://static.springsource.org/spring-data/data-jpa/docs/1.1.0.RC1/reference/html/

 

Мы же сосредоточимся на варианте связки Spring Framework + JPA, где Hibernate выступает в роли JPA – реализации.  Соответственно нам понадобятся последние (на момент написания статьи) версии Spring Framework 3.1 и Hibernate 4.0.1

Подготовка

Выкачайте исходные примеры кодов (http://it.vaclav.kiev.ua/wp-content/uploads/2012/02/spring-jpa-hib-example.zip), разверните архив и проимпортируйте проект в Eclipse (или любую другую IDE по вашему усмотрению). Затем откройте файл {spring-jpa-hib-example}/lib/readme.liblist.txt и, изучив список необходимых библиотек, добавьте их в {spring-jpa-hib-example}/lib/ . За исключением нескольких библиотек вы найдете их в дистрибутивах Spring и Hibernate соответственно.

Дополнительные библиотеки, которые вам понадобятся для работы с примером:

Для простоты демонстрации в качестве базы данных мы воспользуемся легким “in-memory” вариантом БД, а именно HSQLDB. Обратите пожалуйста внимание на то, что в то время как ребята из JBoss любят H2, их коллеги из SpringSource отдают предпочтение HSQLDB для тех же целей. В целом, эти БД в большенстве случаем взаимозаменяемые, но мы остановимся на HSQLDB, т.к. ее намного проще конфигурировать в контексте Spring Framework.

Итак, все инфрастукртурные вопросы рассмотрены – исходники есть, все библиотеки выкачаны и доступны к использованию, можно приступать к изучению примера.

Изучаем проект spring-jpa-hib-example

Как я уже писал выше, за основу взят пример, поставляющийся с Hibernate 4.0.1, в котором в качестве @Entity используется класс org.hibernate.tutorial.em.Event . Я надеюсь, что вы уже успели познакомиться с ним. В противном случае – сейчас самое время это сделать! Тем более, что мы не будем останавливаться на нем подробно – это POJO, аннатированный соответствующим образом.

Рассмотрим, модифицированный мною, persistence.xml:

<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="org.hibernate.tutorial.jpa">
        <description>
            Persistence unit for the Spring JPA tutorial with Hibernate using as JPA provider
        </description>

        <!-- <class>org.hibernate.tutorial.em.Event</class> -->

        <properties>
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="create" />
        </properties>

    </persistence-unit>

</persistence>

Как вы видите, это стандартный persistence.xml в соответствии с JPA v.2.0, в котором не осталось практически ничего кроме нескольких строк, параметризующих Hibernte. Даже org.hibernate.tutorial.em.Event закомментирован 😉 . Почему это сделано вы поймете ниже. В этом файле особый интерес для нас представляет имя persistence-unit-a – запомните его оно понадобится нам далее, чтобы сконфигурировать один из бинов Spring-а. К рассмотрению которого мы и переходим.

application-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd">

	<context:annotation-config />

	<context:component-scan base-package="org.hibernate.tutorial.em" />

	<jdbc:embedded-database id="dataSource">
	</jdbc:embedded-database>

	<bean id="lcemf"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="loadTimeWeaver">
			<bean
				class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
		</property>
		<property name="dataSource" ref="dataSource"></property>
		<property name="persistenceUnitName" value="org.hibernate.tutorial.jpa" />
		<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
	</bean>

	<bean id="eventDao" class="foo.bar.dao.EventDaoImpl" />
</beans>

Давайте рассмотрим его по частям, опуская банальные вещи.

<context:component-scan base-package="org.hibernate.tutorial.em" />

Этот фрагмент конфигурации позволит Spring-у в связке с Hibernate найти собственно сам @Entity класс. Если помните мы закомментировали его декларацию из persistence.xml .

	<jdbc:embedded-database id="dataSource">
	</jdbc:embedded-database>

А вот с этим блоком вы возможно еще не сталкивались не смотря на то, что он появился достаточно давно в Spring, особое внимание ему стали уделять в последних версиях фреймворка. Этот тэг позволяет вам автоматически проинициализировать, так называемую “embedded-database“, в качестве которой может использоваться HSQLDB / H2 /DERBY. В SpringSource больше всего любят HSQLDB, но вы можете достаточно быстро перейти на любую из них (как это сделать? смотрите раздел 13.8 Embedded database support, Spring Reference v.3.1). От себя только добавлю то, что подобное решение не используется в продакшн! Оно предназначено для облегчения создания прототипов и тестирования, чем мы и воспользуемся. По завершении “поднятия контекста” вы получите не только готовый бин “dataSource“, но и поднятую в памяти БД, полностью готовую к работе.

<bean id="lcemf"
	class="org.springframework.orm.jpa.с">
	...
	<property name="dataSource" ref="dataSource"></property>
	<property name="persistenceUnitName" value="org.hibernate.tutorial.jpa" />
	<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
</bean>

А вот в этом блоке конфигурации как раз и кроется вся магия работы с Hibernate, как с JPA-провайдером в Spring. Еще недавно Spring предоставлял набор классов XXXTemlate для упрощения работы с различными ORM, но начиная с v.3.0 эта практика прекращается и класс JpaTemplate уже deprecated 😉 . Вместо этого, в 3-м спринге используются несколько вариантов EntityManagerFactoryBean-ов. Мы с вами воспользуемся наиболее мощным из них, а именно LocalContainerEntityManagerFactoryBean. Как видно из контекста, в него мы передаем dataSource, имя persistenceUnitName и класс реализующий интерфейс PersistenceProvider, в случае с 4м Hibernate это HibernatePersistence (Почему ребята из JBoss не назвали его HibernatePersistenceProvider для меня до сих пор загадка 😉 очевидно озадачивать своих пользователей им доставляет дополнительное удовольствие).

Так же, в контексте Spring-а, мы поднимаем имплементацию нашего дао: foo.bar.dao.EventDao, работу с которым мы и продолжим буквально через несколько секунд.

public interface EventDao {

	public abstract Collection<Event> loadEvents();

	public abstract void persistEvent(Event event);

}

В пакете foo.bar.dao найдите, пожалуйста, имплементацию этого интерфейса. В ней тоже кроется небольшой секрет, который не совсем прозрачно преподнесен в документации к Spring.

    private EntityManagerFactory emf;

    @PersistenceUnit
    public void setEntityManagerFactory(EntityManagerFactory emf) {
        this.emf = emf;
    }

Если вы следили за ходом мысли внимательно, то должны были бы увидеть, что бин типа EntityManagerFactory в контексте не поднимался и по сути взяться ему неоткуда. Тем не менее мы с вами создавали экземпляр LocalContainerEntityManagerFactoryBean и именно его связка с аннотацией @PersistenceUnit позволяет Spring-у получить EntityManagerFactory и выполнить его инъекцию в наше DAO.

Вся дальнейшая работа с Hibernate из среды Spring Framework теперь ведется прозрачно, как с JPA реализацией.

Для того чтобы убедиться в работоспособности примера, найдите класс {spring-jpa-hib-example/test}/foo.bar.dao.EventDaoImplTest и запустите его как JUnit-тест (обязательно использовать JUnit v.4.*)

@Test
public void testBasicUsage() {

	EventDao eventDao = this.context.getBean(EventDaoImpl.class);
	Collection<Event> events = eventDao.loadEvents();
	log.info("Events count: " + events.size());
	log.info(events);

	// checking that there are no any events in fresh db schema
	assertEquals(eventCnt, events.size());

	// create and persist new event
	eventDao.persistEvent(new Event( "Our very first event!", new Date() ));
	eventCnt++;

	events = eventDao.loadEvents();
	log.info("Events count: " + events.size());
	log.info(events);

	// checking that there is only one event that has been created recently
	assertEquals(eventCnt, events.size());
}

Если тест прошел успешно, это значит что вам удалось поднять Hibernate в качестве JPA провайдера в контексте Spring-а 😉

В следующих выпусках я познакомлю вас с не менее интересными возможностями Spring Framework 😉

This entry was posted in Java, Main menu and tagged , , . Bookmark the permalink.
  • мне лень искать…. можно в конце статьи линк на следующую часть?
    п.с.: спасибо за проект – все работает

    • Продолжений на эту тему пока не было – давненько ничего не писал.