同时使用Hibernate和Spring Data JPA?

33

是否可能同时使用Spring Data JPA(由Hibernate作为JPA提供者支持)和直接使用Hibernate?

问题在于,当我使用JpaTransactionManager时,无法通过 org.hibernate.HibernateException:没有找到当前线程的会话检索当前会话。 当我切换到HibernateTransaction Manager时,JPA存储库无法提交更改。

这是我的Spring上下文的一部分(使用此上下文,我无法使用直接的Hibernate调用):

<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"/>

hibernate repository的示例:

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();
}

Spring数据仓库定义示例:

public interface BitrateRepository extends PagingAndSortingRepository<Bitrate, Long> { }

软件版本:

<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>

那么问题是 - 是否可以在同一事务中(由@Transactional注释指定)同时使用Spring JPA存储库和直接的Hibernate调用,以及如何实现?

3个回答

25

您需要一种单一的配置方式,您现在正在配置Hibernate和JPA。您应该使用JPA进行配置,因此请删除Hibernate设置。

您正在使用Hibernate4,因此可以利用Spring的不太知名的HibernateJpaSessionFactoryBean。如果您需要访问SessionFactory(我假设您需要访问)。

应用您的配置后,将像这样:

<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>
我建议您仅在重构应用程序以使用纯JPA API时将其用作中间解决方案。我不建议混合使用两种策略。

2
这比我们目前在参考文档中推荐的要好得多。你介意创建一个票来改进这个文档吗?也不介意提供一个pull request :)。 - Oliver Drotbohm
4
@OliverGierke [完成](https://jira.springsource.org/browse/DATAJPA-462)和[完成](https://github.com/spring-projects/spring-data-jpa/pull/57)。 - M. Deinum
1
混合使用两种不同的ORM策略可能会令人困惑,个人认为最好坚持一种方法。 - M. Deinum
3
额外说明:这种方法需要一个额外的JPA属性,因为出现了“No CurrentSessionContext configured!”错误(解释详见https://dev59.com/mIjca4cB1Zd3GeqPwWqg#28852407)。 - knalli
@GuillaumeHusta,你找到任何替代方案了吗? - nani21984
显示剩余3条评论

21

不要创建一个SessionFactory,使用EntityManager.unwrap(Session.class)可以获取Hibernate Session并从Session对象中检索session工厂。

您还可以使用EntityManagerFactory.unwrap(SessionFactory.class)直接获取Hibernate SessionFactory。


2
如果您包含了包,那么会更有帮助。Spring中有数十亿个包,Eclipse本身无法找到EntityManager... - user1944491
1
你需要使用JPA javax.persistence.EntityManager。 - Bilbo Baggins

2
这是我所做的,而且效果很好:一个数据源,两个事务管理器。 数据源bean: 最初的回答
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- bulabula... -->
</bean>

针对Hibernate XML配置:

最初的回答:

<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>

最初的回答:
对于基于Java配置的spring-data-jpa:

@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;
}

请注意,transactionManagerRef = "jpaTransactionManager"将JpaTransactionManager设置为与存储库一起使用的事务管理器。 Spring Data JPA命名空间属性

最初的回答。


现在已经过去了几个月,您是否仍然建议使用双事务管理器?我有同样的问题;我想在传统的Hibernate代码之上使用Spring JPA。 - kris larson
1
@krislarson 如果你想将一个旧项目升级到最新的架构项目,我认为这是可以的。因为这可以逐步淘汰旧代码 ;) - CloudSen
这正是我想要做的。谢谢。 - kris larson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接