Est-il possible d'utiliser Spring Data JPA (soutenu par Hibernate en tant que fournisseur JPA) et d'utiliser directement Hibernate en même temps?
Le problème est que lorsque j'utilise JpaTransactionManager, je ne suis pas en mesure de récupérer la session en cours avec org.hibernate.HibernateException: No Session found for current thread
. Lorsque je passe au gestionnaire HibernateTransaction, les référentiels JPA ne sont pas en mesure de valider les modifications.
Voici la partie de mon contexte Spring (avec ce contexte, je ne suis pas en mesure d'utiliser les appels Hibernate directs):
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/IPGCONF"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSource">
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<jpa:repositories base-package="com.satgate"/>
Exemple de référentiel de mise en veille prolongée:
public Collection<Layer> listCurrent(Carrier carrier) {
Criteria query = sessionFactory.getCurrentSession()
.createCriteria(Layer.class)
.add(Restrictions.eq("carrier", carrier));
query.createCriteria("bitrate")
.addOrder(Order.desc("bitrate"))
.add(Restrictions.eq("symbolrate", carrier.getSymbolrate()));
return query.list();
}
Exemple de définition du référentiel de données Spring:
public interface BitrateRepository extends PagingAndSortingRepository<Bitrate, Long> { }
Versions du logiciel:
<org.springframework.version>4.0.0.RELEASE</org.springframework.version>
<org.springframework.data.version>1.4.3.RELEASE</org.springframework.data.version>
<hibernate.version>4.3.0.Final</hibernate.version>
Donc, la question est - est-il possible d'utiliser dans la même transaction (spécifiée par l'annotation @Transactional) à la fois les référentiels Spring JPA et les appels Hibernate directs et comment y parvenir?
Au lieu de créer un SessionFactory, utilisez EntityManager.unwrap(Session.class)
pour obtenir une session de mise en veille prolongée et récupérer la fabrique de sessions à partir de l'objet Session.
Vous pouvez également utiliser EntityManagerFactory.unwrap(SessionFactory.class)
pour obtenir directement Hibernate SessionFactory.
Vous avez besoin d'une seule façon de configurer, vous configurez maintenant à la fois Hibernate et JPA. Vous devez utiliser JPA pour la configuration, donc supprimez la configuration de mise en veille prolongée.
Vous utilisez Hibernate4 afin de pouvoir profiter du HibernateJpaSessionFactoryBean
, moins connu, de Spring. Si vous avez besoin d'accéder au SessionFactory
(dont je suppose que vous avez besoin).
Une fois appliquée, votre configuration aimera quelque chose comme ça.
<bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="entityManagerFactory"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
</bean>
Je suggérerais de ne l'utiliser que comme solution intermédiaire pendant que vous refactorisez votre applicaiton pour utiliser l'api JPA ordinaire. Je ne suggérerais pas de mélanger les deux stratégies.
C'est ce que j'ai fait et cela a bien fonctionné: une source de données, deux gestionnaire de transactions.
Bean source de données:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- bulabula... -->
</bean>
Pour la configuration basée sur XML Hibernate:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingLocations" value="#{propertyUtils.getList('hibernate.hbm')}"/>
<property name="hibernateProperties">
<value>
<!-- bulabulabula... -->
</value>
</property>
</bean>
<bean id="transactionManager" primary="true" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Et pour spring-data-jpa Java:
@Configuration
@EnableJpaRepositories(basePackages = {"org.sharder.core.repository"},
transactionManagerRef = "jpaTransactionManager")
@EnableTransactionManagement
public class JpaConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(ComboPooledDataSource comboPooledDataSource) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("org.sharder.core.entity");
factory.setDataSource(comboPooledDataSource);
factory.setJpaProperties(getHibernateProperties());
return factory;
}
@Bean(name = "jpaTransactionManager")
public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory");
properties.setProperty("hibernate.cache.use_query_cache", "true");
properties.setProperty("hibernate.cache.use_second_level_cache", "true");
properties.setProperty("hibernate.cache.use_structured_entries", "true");
properties.setProperty("hibernate.format_sql", "true");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.use_sql_comments", "true");
properties.setProperty("hibernate.query.substitutions", "true 1, false 0");
properties.setProperty("hibernate.jdbc.fetch_size", "20");
properties.setProperty("hibernate.connection.autocommit", "false");
properties.setProperty("hibernate.connection.release_mode", "auto");
return properties;
}
}
Remarquerez que, transactionManagerRef = "jpaTransactionManager"
définit le JpaTransactionManager à utiliser avec les référentiels. attributs d'espace de noms Spring Data JPA