Это первая из серии статей о постоянстве с весной. Эта статья будет посвящена настройке и реализации уровня персистентности в Spring 3.1 и Hibernate. Для шаг за шагом введение о создании контекста Spring , используя конфигурацию на основе Java и основной Maven POM для проекта, см это .
Серия « Стойкость с весны »:
- Часть 1 — Уровень сохраняемости с Spring 3.1 и Hibernate
- Часть 2 — Упрощая уровня доступа к данным с Spring и Java дженериков
- Часть 3 — Уровень сохраняемости с Spring 3.1 и JPA
- Часть 4 — Уровень сохраняемости с данными Spring JPA
- Часть 5 — конфигурация сделки с JPA и Spring 3.1
Больше нет шаблонов весны
Начиная с Spring 3.0 и Hibernate 3.0.1, управление сеансом Hibernate с помощью Springs HibernateTemplate больше не требуется. Теперь можно использовать контекстные сеансы — сеансы, управляемые непосредственно Hibernate и сохраняющие активность в рамках транзакции.
Как следствие, в настоящее время рекомендуется использовать API Hibernate напрямую, а не HibernateTemplate , что эффективно полностью отделит реализацию уровня DAO от Spring.
Исключение перевода без шаблона
Одной из обязанностей HibernateTemplate является перевод исключений — перевод низкоуровневых исключений Hibernate — которые связывают API с Hibernate как единственно возможный ORM — в обобщенные исключения Spring более высокого уровня.
Без этого шаблона преобразование исключений можно включить, пометив DAO аннотацией @Repository . Это, в сочетании с постпроцессором bean-компонента Spring, будет рекомендовать всем bean- компонентам @Repository со всеми реализациями PersistenceExceptionTranslator, найденными в контексте Spring, — обеспечить преобразование исключений без использования шаблона.
Перевод исключений осуществляется через прокси; чтобы Spring мог создавать прокси вокруг классов DAO, они не должны быть объявлены как final .
Управление Hibernate Session без шаблона
Когда появилась поддержка Hibernate для контекстных сессий, HibernateTemplate по сути устарел; фактически, javadoc класса был обновлен с этим советом (выделенный жирным шрифтом из оригинала):
ПРИМЕЧАНИЕ. Начиная с Hibernate 3.0.1, транзакционный код доступа Hibernate также можно кодировать в простом стиле Hibernate. Поэтому для новых проектов рассмотрите возможность принятия стандартного стиля кодирования объектов доступа к данным в стиле Hibernate3 на основе {@link org.hibernate.SessionFactory # getCurrentSession ()}.
Конфигурация Spring Java
Hibernate SessionFactory настраивается в конфигурации путем создания фабричного компонента Spring для управления им — AnnotationSessionFactoryBean ; это позволит автоматически определять классы сущностей путем сканирования пути к классам. Обратите внимание, что для этого требуется Hibernate 3.2+ и JDK 1.5+.
Альтернатива — вручную указать все аннотированные классы сущностей для фабричного компонента сеанса с помощью метода setAnnotatedClasses .
@Configuration
@EnableTransactionManagement
public class PersistenceHibernateConfig{
@Bean
public AnnotationSessionFactoryBean alertsSessionFactory(){
AnnotationSessionFactoryBean sessionFactory = new AnnotationSessionFactoryBean();
sessionFactory.setDataSource( this.restDataSource() );
sessionFactory.setPackagesToScan( new String[ ] { "org.rest" } );
sessionFactory.setHibernateProperties( this.hibernateProperties() );
return sessionFactory;
}
@Bean
public DataSource restDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName( this.driverClassName );
dataSource.setUrl( this.url );
dataSource.setUsername( "restUser" );
dataSource.setPassword( "restmy5ql" );
return dataSource;
}
@Bean
public HibernateTransactionManager transactionManager(){
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory( this.alertsSessionFactory().getObject() );
return txManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
}
Также обратите внимание, что cglib должен находиться в пути к классам для работы классов Java @Configuration ; Чтобы лучше понять необходимость использования cglib в качестве зависимости, см. эту статью .
Конфигурация Spring XML
Та же конфигурация Spring с XML:
<bean id="sessionFactory" class=
"org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="org.rest" />
<property name="hibernateProperties">
...
</property>
</bean>
<bean id="dataSource" class=
"org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="restUser" />
<property name="password" value="restmy5ql" />
</bean>
<bean id="txManager" class=
"org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
Относительно небольшого различия между способом настройки Spring в XML и новой конфигурацией на основе Java — в XML ссылка на другой компонент может указывать либо на компонент, либо на фабрику компонентов для этого компонента. Однако в Java компилятор этого не допустил, поэтому SessionFactory сначала извлекается из фабрики, а затем передается менеджеру транзакций:
TransactionsManager.setSessionFactory ( this.alertsSessionFactory (). getObject () );
Спящие свойства
Hibernate настроен для работы с Spring с помощью следующих свойств Hibernate :
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.show_sql=false
Обратите внимание, что диалект MySQL включен в качестве ссылки, но подойдет любой диалект, поддерживаемый Hibernate .
Потенциал для исключений
Фабрика транзакций
Контракт Hibernate для создания транзакций определяется интерфейсом TransactionFactory . Чтобы Spring полностью управлял транзакциями, стандартная реализация этого контракта — JDBCTransactionFactory — заменяется по умолчанию его аналогом Spring-Spring — SpringTransactionFactory .
Это также можно сделать вручную, в свойствах Hibernate (однако, это избыточно):
transaction.factory_class = org.springframework.orm.hibernate3.SpringTransactionFactory
Текущий контекст сеанса
Когда Hibernate SessionFactory создается в контексте Spring его фабричным компонентом, он создает CurrentSessionContext . Это контракт на поддержку текущей концепции сеанса, и его реализация решается путем анализа свойства Hibernate «hibernate.current_session_context_class» .
Установка этого свойства в «управляемый» означает использование управляемой реализации для контекстных сеансов — ManagedSessionContext — что предполагает, что текущий сеанс управляется внешним объектом . В нашем контексте Spring, это не сработает с:
org.springframework.orm.hibernate3.HibernateSystemException: ни один сеанс в настоящее время не связан с контекстом выполнения
Установка для свойства «thread» включит привязанную к потоку стратегию в конфигурации Hibernate; это также будет противоречить управлению транзакциями Spring и приведет к:
org.springframework.orm.hibernate3.HibernateSystemException: постоянное действие недопустимо без активной транзакции
Чтобы Spring мог управлять транзакциями, это свойство должно быть «org.springframework.orm.hibernate3.SpringSessionContext» ; поскольку это также значение по умолчанию, явное определение свойства может быть удалено.
DAO
Каждый DAO будет основан на параметризованном, абстрактном классе класса DAO с поддержкой общих родовых операций:
public abstract class AbstractHibernateDAO< T extends Serializable >{
private final Class< T > clazz;
@Autowired
SessionFactory sessionFactory;
public void setClazz( final Class< T > clazzToSet ){
this.clazz = clazzToSet;
}
public T getById( final Long id ){
Preconditions.checkArgument( id != null );
return (T) this.getCurrentSession().get( this.clazz, id );
}
public List< T > getAll(){
return this.getCurrentSession()
.createQuery( "from " + this.clazz.getName() ).list();
}
public void create( final T entity ){
Preconditions.checkNotNull( entity );
this.getCurrentSession().persist( entity );
}
public void update( final T entity ){
Preconditions.checkNotNull( entity );
this.getCurrentSession().merge( entity );
}
public void delete( final T entity ){
Preconditions.checkNotNull( entity );
this.getCurrentSession().delete( entity );
}
public void deleteById( final Long entityId ){
final T entity = this.getById( entityId );
Preconditions.checkState( entity != null );
this.delete( entity );
}
protected final Session getCurrentSession(){
return this.sessionFactory.getCurrentSession();
}
}
Здесь интересно несколько аспектов — как уже говорилось, абстрактный DAO не расширяет какой-либо шаблон Spring (например, HibernateTemplate ). Вместо этого Hibernate SessionFactory внедряется непосредственно в DAO и будет играть роль основного Hibernate API через контекстный сеанс, который он предоставляет:
this.sessionFactory. getCurrentSession ();
Также обратите внимание, что класс сущности передается в конструкторе для использования в общих операциях:
@Repository
public class FooDAO extends AbstractHibernateDAO< Foo > implements IFooDAO{
public FooDAO(){
setClazz(Foo.class );
}
}
Конфигурация Maven
В дополнение к конфигурации Maven, определенной в предыдущей статье , добавлены следующие зависимости: spring-orm (в качестве зависимости также используется spring-tx ) и hibernate-core :
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>3.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.2.0.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.24</version> <scope>runtime</scope> </dependency>
Обратите внимание, что зависимость MySQL включена в качестве ссылки — для настройки источника данных необходим драйвер, но подойдет любая база данных, поддерживаемая Hibernate .
Вывод
В этой статье рассказывается о настройке и реализации уровня персистентности в Hibernate и Spring 3.1 с использованием конфигурации на основе XML и Java. Обсуждались причины прекращения использования шаблонов для уровня DAO, а также возможные подводные камни настройки Spring для управления транзакциями и сеансом Hibernate. Конечный результат — легкая, чистая реализация DAO, практически не зависящая от времени компиляции с Spring. Вы можете проверить полную реализацию в проекте github .
Я не хочу, чтобы ваше обучение о Spring Persistence остановилось здесь.
>> Получите мою электронную книгу «Spring Persistence» и подпишитесь на мою рассылку