Статьи

Уровень сохраняемости с Spring 3.1 и JPA

1. Обзор

Это третья из серии статей о постоянстве с весной. Эта статья будет посвящена настройке и реализации Spring с JPA . Пошаговое введение в настройку контекста Spring с использованием конфигурации на основе Java и базового Maven pom для проекта см. В этой статье .

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

2. Больше нет весенних шаблонов

По состоянию на весну 3.1, JpaTemplate и соответствующий JpaDaoSupport были осуждается в пользу использования родного Java Persistence API.

Кроме того, оба эти класса относятся только к JPA 1 (из javadoc JpaTemplate ):

Обратите внимание, что этот класс не был обновлен до JPA 2.0 и никогда не будет обновлен.

Как следствие, в настоящее время рекомендуется использовать непосредственно API персистентности Java вместо JpaTemplate , что эффективно полностью отделит реализацию уровня DAO от Spring.

Исключение перевода без шаблона

Одной из обязанностей JpaTemplate является перевод исключений — перевод исключений низкого уровня — которые связывают API с JPA — в обобщенные исключения Spring более высокого уровня.

Без этого шаблона преобразование исключений можно включить, пометив DAO аннотацией @Repository . Это, в сочетании с постпроцессором bean-компонента Spring, будет советовать всем bean- компонентам @Repository со всеми реализациями PersistenceExceptionTranslator, найденными в контейнере, предоставлять перевод исключений без использования шаблона.

Перевод исключений осуществляется через прокси; чтобы Spring мог создавать прокси вокруг классов DAO, они не должны быть объявлены как final .

Внедрение JPA EntityManager в Spring без шаблона

EntityManager является API контекста постоянства; это может быть введено непосредственно в DAO. Контейнер Spring способен выступать в качестве контейнера JPA и  внедрять EntityManager, поддерживая @PersistenceContext (как аннотацию на уровне поля, так и на уровне метода).

Чтобы это работало, bean-компонент PersistenceAnnotationBeanPostProcessor должен существовать в контейнере Spring. Бин может быть создан либо явно, определив его в конфигурации, либо автоматически , определив context: annotation-config или context: component-scan в конфигурации.

3. Конфигурация Spring Java

EntityManager устанавливается в конфигурации путем создания фабрики боб Spring управлять ею; это позволит PersistenceAnnotationBeanPostProcessor получить его из контейнера.

Есть две опции, чтобы установить это — или более простой LocalEntityManagerFactoryBean или более гибкий LocalContainerEntityManagerFactoryBean . Последний вариант используется здесь, так что для него можно настроить дополнительные свойства:

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
      LocalContainerEntityManagerFactoryBean factoryBean
       = new LocalContainerEntityManagerFactoryBean();
      factoryBean.setDataSource( this.restDataSource() );
      factoryBean.setPackagesToScan( new String[ ] { "org.rest" } );

      JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(){
         {
            // JPA properties ...
         }
      };
      factoryBean.setJpaVendorAdapter( vendorAdapter );
      factoryBean.setJpaProperties( this.additionlProperties() );

      return factoryBean;
   }

   @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 PlatformTransactionManager transactionManager(){
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(
       this.entityManagerFactoryBean().getObject() );

      return transactionManager;
   }

   @Bean
   public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
      return new PersistenceExceptionTranslationPostProcessor();
   }
}

Также обратите внимание, что cglib должен находиться в пути к классам для работы классов Java @Configuration ; Чтобы лучше понять необходимость использования cglib в качестве зависимости, см. эту статью .

4. Конфигурация Spring XML

Та же конфигурация Spring с XML:

<bean id="myEmf"
 class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
   <property name="dataSource" ref="dataSource" />
   <property name="packagesToScan" value="org.rest" />
   <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
         <property name="showSql" value="${hibernate.show_sql}" />
         <property name="generateDdl" value="${jpa.generateDdl}" />
         <property name="databasePlatform" value="${persistence.dialect}" />
      </bean>
   </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.jpa.JpaTransactionManager">
   <property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />

Относительно небольшого различия между способом настройки Spring в XML и новой конфигурацией на основе Java — в XML ссылка на другой компонент может указывать либо на компонент, либо на фабрику компонентов для этого компонента. В Java, однако, поскольку типы различаются, компилятор не позволяет этого, и поэтому EntityManagerFactory сначала извлекается из его фабрики компонентов, а затем передается менеджеру транзакций:

txManager.setEntityManagerFactory ( this.entityManagerFactoryBean (). getObject () );

5. Переход на полный XML-менее

Обычно JPA определяет единицу постоянства через файл META-INF / persistence.xml . Начиная с Spring 3.1, этот XML — файл больше не нуженLocalContainerEntityManagerFactoryBean теперь поддерживает «packagesToScan» свойство , где пакеты для сканирования @Entity классов может быть указан.

Persistence.xml файл был последний кусок XML должен быть удален — теперь, JPA может быть полностью настроен с не XML .

5.1. JPA Свойства

Свойства JPA обычно указываются в файле persistence.xml ; в качестве альтернативы, свойства могут быть указаны непосредственно для фабричного компонента управления объектами:

factoryBean.setJpaProperties (this.additionlProperties ());

В качестве дополнительного примечания: если Hibernate будет поставщиком сохраняемости, то это будет способ указать конкретные свойства Hibernate.

5.2. DAO

Каждый DAO будет основан на параметризованном, абстрактном классе класса DAO с поддержкой общих родовых операций:

public abstract class AbstractJpaDAO< T extends Serializable > {

   private Class< T > clazz;

   @PersistenceContext
   EntityManager entityManager;

   public void setClazz( Class< T > clazzToSet ){
      this.clazz = clazzToSet;
   }

   public T findOne( Long id ){
      return this.entityManager.find( this.clazz, id );
   }
   public List< T > findAll(){
      return this.entityManager.createQuery( "from " + this.clazz.getName() )
       .getResultList();
   }

   public void save( T entity ){
      this.entityManager.persist( entity );
   }

   public void update( T entity ){
      this.entityManager.merge( entity );
   }

   public void delete( T entity ){
      this.entityManager.remove( entity );
   }
   public void deleteById( Long entityId ){
      T entity = this.getById( entityId );

      this.delete( entity );
   }
}

Здесь интересно несколько аспектов — как уже говорилось, абстрактный DAO не расширяет шаблон Spring (такой как JpaTemplate ). Вместо этого JPA EntityManager внедряется непосредственно в DAO и будет выполнять роль основного API постоянства.

Также обратите внимание, что класс сущности передается в конструкторе для использования в общих операциях:

@Repository
public class FooDAO extends AbstractHibernateDAO< Foo > implements IFooDAO{

   public FooDAO(){
      setClazz(Foo.class );
   }

}

6. Конфигурация Maven

В дополнение к конфигурации Maven, определенной в предыдущей статье , добавлены следующие зависимости: spring-ormкачестве зависимости также используется spring-tx ) и hibernate-entitymanager :

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>3.2.2.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>4.2.0.Final</version>
   <scope>runtime</scope>
</dependency>

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.24</version>
   <scope>runtime</scope>
</dependency>

Обратите внимание, что зависимость MySQL включена в качестве ссылки — для настройки источника данных необходим драйвер, но подойдет любая база данных, поддерживаемая Hibernate .

7. Заключение

В этой статье рассказывается о настройке и реализации персистентного уровня в JPA 2 и Spring 3.1 с использованием конфигурации на основе XML и Java. Обсуждались причины, по которым мы перестали полагаться на шаблоны для уровня DAO, а также избавились от последнего фрагмента XML, обычно связанного с JPA, — persistence.xml . Конечный результат — легкая, чистая реализация DAO, практически не зависящая от времени компиляции с Spring. Вы можете проверить полную реализацию в проекте github .

Оригинальный слой Persistence с пружиной 3.1 и JPA из серии Persistence with Spring