Статьи

Уровень сохраняемости с помощью Spring 3 и Hibernate 4

Это первая из серии статей о постоянстве с весной. Эта статья будет посвящена настройке и реализации уровня персистентности в Spring 3.1 и Hibernate. Для шаг за шагом введение о создании контекста Spring , используя конфигурацию на основе Java и основной Maven POM для проекта, см это .

Серия « Стойкость с весны »:

Больше нет шаблонов весны

Начиная с 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» и подпишитесь на мою рассылку