【问题标题】:Simultaneous use of Hibernate and Spring data jpa?Hibernate和Spring数据jpa同时使用?
【发布时间】:2014-03-07 11:03:56
【问题描述】:

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

问题是当我使用 JpaTransactionManager 时,我无法使用org.hibernate.HibernateException: No Session found for current thread 检索当前会话。当我切换到 HibernateTransaction 管理器时,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 仓库示例:

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 调用以及如何实现?

【问题讨论】:

    标签: java spring hibernate jpa spring-data


    【解决方案1】:

    这就是我所做的,并且效果很好: 一个数据源,两个事务管理器。
    数据源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>
    

    对于 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;
    }
    

    }

    请注意,transactionManagerRef = "jpaTransactionManager" 将 JpaTransactionManager 设置为与存储库一起使用。 Spring Data JPA namespace attributes

    【讨论】:

    • 现在几个月过去了,您还推荐使用双事务管理器吗?我也有同样的问题;我想在遗留的 Hibernate 代码之上使用 Spring JPA。
    • @krislarson 如果你想将一个旧项目升级到最新的架构项目,我认为是可以的。因为这可以逐步淘汰旧代码;)
    • ...这正是我想要做的。谢谢
    【解决方案2】:

    使用EntityManager.unwrap(Session.class) 来获取 Hibernate Session 并从 Session 对象中检索会话工厂,而不是创建 SessionFactory。

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

    【讨论】:

    • 如果您包含该软件包,它会更有帮助。 Spring 中有数十亿个包,而 Eclipse 自己找不到 EntityManager...
    • 为此你需要 JPA javax.persistence.EntityManager
    【解决方案3】:

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

    您正在使用 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 时仅将其用作中间解决方案。我不建议混合这两种策略。

    【讨论】:

    • 这比我们目前在reference docs 中推荐的要好得多。你介意创建一张票来改进文档吗?也不介意拉取请求:)。
    • @OliverGierke Done & Done.
    • @M.Deinum 我确认它并不知名但非常优雅!一个问题(好奇):你为什么不混合两种策略?
    • 混合 2 种不同的 ORM 策略可能会令人困惑,最好坚持使用单一方法恕我直言。
    • 补充一点:这种方法需要一个额外的 JPA 属性到期 No CurrentSessionContext configured!(在 stackoverflow.com/a/28852407/525238 中解释)
    猜你喜欢
    • 1970-01-01
    • 2017-05-10
    • 1970-01-01
    • 1970-01-01
    • 2019-12-19
    • 1970-01-01
    • 2019-07-20
    • 1970-01-01
    • 2011-01-25
    相关资源
    最近更新 更多