На работе завершена большая и сложная задача и перед началом решения следующей хочется немного отвлечься и поделиться чем-нибудь с вами, уважаемые читатели. Сегодняшний пост будет из серии "для самых маленьких". Давайте поговорим о связке
и динамическом управлении транзакциями.
штука довольно сложная и интересная. В частности, в его состав входит package
.
Давайте создадим простое консольное приложение (чтобы не заморачиваться на определение сервлетов и прочего overhead'а), которое что-то пишет в БД.
Соответственно, прежде всего определим сущность, с которой будем работать. Назовем ее непритязательно: MyEntity.
Код сущности будет таким:
package ru.naumen.demo.entity;import java.io.Serializable;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import org.hibernate.annotations.GenericGenerator;@
Entitypublic class MyEntity
implements Serializable{ private static final long serialVersionUID = 382157955767771714L
; @Id
@Column
(name =
"uuid") @GeneratedValue
(generator =
"system-uuid") @GenericGenerator
(name =
"system-uuid", strategy =
"uuid") private String id
; @Column
(name =
"name") private String name
; public MyEntity
() { } public MyEntity
(String id,
String name
) { this.
id = id
; this.
name = name
; } public String getId
() { return id
; } public void setId
(String id
) { this.
id = id
; } public String getName
() { return name
; } public void setName
(String name
) { this.
name = name
; }}
Напомню, что аннотации @Entity, @Id и т.д. относятся к JPA и заменяют собой Hibernate-mapping.
Работать с сущностью мы будем не напрямую, а через DAO. Использование DAO является одним из устоявшихся паттернов работы со SpringFramework. Определив бин, реализующий DAO можно легко и просто инъектировать его в бины, реализующие бизнес-логику приложения и тем самым полностью отделить бизнес-логику от работы с данными. DAO у нас будет реализовано следующим интерфейсом:
package ru.naumen.demo.dao;
import ru.naumen.demo.entity.MyEntity;
public interface IEntityDao
{
public void save(MyEntity entity);
}
Для примера мы определим один метод - save, который будет сохранять сущность в БД. Реализация DAO довольно примитивна:
package ru.naumen.demo.dao;import org.springframework.orm.hibernate3.support.HibernateDaoSupport;import ru.naumen.demo.entity.MyEntity;public class EntityDao
extends HibernateDaoSupport
implements IEntityDao
{ @
Override public void save
(MyEntity entity
) { getHibernateTemplate
().
save(entity
); }}
Мы наследуемся от класса HibernateDaoSupport, который инкапсулирует работу с Hibernate Session, Hibernate Session Factory и предоставляет нам простое API для взаимодействия с Hibernate. Рекомендую статью, в которой описано, как грамотно организовать слой DAO в своем приложении.
Теперь перейдем к классам, которые будут реализовывать бизнес-логику. В нашем случае бизнес-логика будет проста - мы будем просто брать и сохранять сущность.
Интерфейс IMyEntityService:
package ru.naumen.demo.services;
import ru.naumen.demo.entity.MyEntity;
public interface IMyEntityService
{
public void saveEntity(MyEntity entity);
}
Реализация - класс MyEntityService:
package ru.naumen.demo.services;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import ru.naumen.demo.dao.IEntityDao;import ru.naumen.demo.entity.MyEntity;@Transactional
(readOnly =
true)public class MyEntityService
implements IMyEntityService
{ private IEntityDao dao
; public void setDao
(IEntityDao dao
) { this.
dao = dao
; } @
Override @Transactional
(readOnly =
false, propagation = Propagation.
REQUIRED) public void saveEntity
(MyEntity entity
) { dao.
save(entity
); }}
Данный класс - самое интересное, что есть в нашей программе. Нам необходимо обернуть метод saveEntity в транзакцию. Для этого существует аннотация @Transactional, которой можно аннотировать методы или целый класс. Параметрами данной аннотации задается поведение транзакции. Основными параметрами являются readOnly, который указывает на возможность или невозможность менять состояние БД и propagation, который задает стратегию создания транзакции (не создавать транзакцию, создать новую, присоединиться к существующей и т.д.). Помимо этих параметров можно указывать таймаут, уровень изоляции, классы и типы исключений для которых надо и ненадо делать rollback.
Подробнее про параметры и их значения можно прочесть в официальном руководстве по SpringFramework.
Собственно, теперь надо рассмотреть конфигурацию Spring-контекста, которая будет храниться в файле applicationContext.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
</beans>
Обратите внимание! Очень важно правильно прописать все namespaces и пути к схемам, иначе конфиг просто не будет парситься.
Итак, сначала добавим в контекст необходимые конфигурационные файлы, в нашем случае - jdbc.properties, в котором мы будем хранить параметры подключения к СУБД. Для работы с конфигурационными файлами SpringFramework содержит класс org.springframework.beans.factory.config.PropertyPlaceholderConfigurer. Разметка будет вот такой:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="jdbc.properties" />
</bean>
Далее следует определить источник данных - мост между СУБД и Hibernate. Я предпочитаю использовать для этого замечательную библиотеку apache.commons.dbcp.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
После того, как определили источник данных, пришла пора описать фабрику, которая будет строить Hibernate-сессии. Для этого существует класс org.springframework.orm.hibernate3.LocalSessionFactoryBean. Мы опишем этот бин следующим образом:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
</props>
</property>
</bean>
Все специфичные настройки Hibernate будем хранить в файле hibernate.cfg.xml, диалект - в файле jdbc.properties. Обратите внимание, что т.к. мы определяем мэппинг аннотациями, то работать с такой конфигурацией должен класс org.hibernate.cfg.AnnotationConfiguration.
С базой данных мы соединились и Hibernate-сессию создали. Пришла пора указать приложению на то, что нужно динамически управлять транзакциями. Что значит "динамически управлять транзакциями?" Это значит, что нам не нужно писать код, который создает/закрывает/откатывает транзакции и размещать его везде, где нужно. Нам достаточно лишь передать классу HibernateTransactionManager некие правила создания/завершения транзакций, а остальное он возьмет на себя.
Понятно, что все это счастье работает через AOP. Правило представляет собой соответствие между методом и типом создаваемой транзакции. Это обозначает, что когда мы входим в метод (перед самым началом выполнения кода метода) - необходимо создать транзакцию, а перед выходом из метода (после выполнения последней инструкции метода) транзакцию закоммитить. Ну и дополнительно можно описать при каких типах исключений должен быть выполнен откат транзакции.
Существует два основных способа определения правил: использование нотации Spring AOP в xml-конфигах Spring и использование аннотаций в Java-коде. Каждый метод имеет свои достоинства и недостатки, но это уже тема другой статьи. Мы же рассмотрим как управлять транзакциями с помощью аннотаций.
Для управления транзакциями в Spring существует пространство имен tx, в котором определена, в частности, директива tx:annotation-driven, включающая механизм управления транзакциями через аннотации. Про параметры этой директивы можно прочитать в секции 9.5.6. документа.
Мы определим менеджер транзакций следующим образом:
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Ну и остается определить бины для слоя DAO и слоя бизнес-логики:
<bean id="entityDAO" class="ru.naumen.demo.dao.EntityDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="entityService" class="ru.naumen.demo.services.MyEntityService">
<property name="dao" ref="entityDAO" />
</bean>
Напоследок приведу код класса Main, который запускает приложение:
package ru.naumen.demo;import org.springframework.context.support.ClassPathXmlApplicationContext;import ru.naumen.demo.entity.MyEntity;import ru.naumen.demo.services.IMyEntityService;public class Main
{ public static void main
(String[] args
) { ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext
("applicationContext.xml"); IMyEntityService service =
(IMyEntityService
) ctx.
getBean("entityService"); MyEntity entity =
new MyEntity
(); entity.
setName("Pavel"); service.
saveEntity(entity
); }}
Код не сложный. Сначала мы загружаем контекст приложения, затем из контекста достаем нужный нам бин (в данном случае - "entityService". Ну а дальше используем бин по назначению - сохраняем с его помощью сущность в БД.
Вообще, я считаю, что конфигурация аннотациями проще, чем xml, да и читается лучше. В принципе, можно было бы и взаимодействие бинов сконфигурировать с помощью аннотаций, Spring это позволяет уже довольно давно. На тему конфигурирования Spring-бинов через аннотации можно почитать статьи на habrahabr: эту и эту.
Теперь вы знаете, как подключить к SpringFramework СУБД и Hibernate, опеспечить динамическое управление транзакциями, описать слой DAO и подключить DAO к бизнес-логике. Фактически, мы создали "рыбу" приложения и теперь можем неограниченно наращивать его функционал.
Скачать приложение со всеми исходниками и библиотеками можно отсюда (6.3 Мб).
Понравилось сообщение - подпишитесь на блог или читайте меня в twitter