Это первая из серии статей о постоянстве с весной. Эта статья будет посвящена настройке и реализации уровня персистентности в 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» и подпишитесь на мою рассылку