Simultaneous use of Hibernate and Spring data jpa?

22,692

Solution 1

Instead of creating a SessionFactory, use EntityManager.unwrap(Session.class) to get a Hibernate Session and retrieve the session factory from the Session object.

You can also use EntityManagerFactory.unwrap(SessionFactory.class) to get the Hibernate SessionFactory directly.

Solution 2

You need a single way of configuration you are now configuring both Hibernate and JPA. You should be using JPA for configuration so remove the hibernate setup.

You are using Hibernate4 so you can take advantage of the, not so well known, HibernateJpaSessionFactoryBean of Spring. If you need access to the SessionFactory (which I assume you need).

When applied your configuration will like something like this.

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

I would suggest to only use this as an intermediate solution while you are refactoring your applicaiton to use the plain JPA api. I wouldn't suggest mixing both strategies.

Solution 3

This is what I did, and it worked well: one data source, two transaction manager.
Data source bean:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- bulabula... -->
</bean>

For Hibernate XML based configuration:

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

And for spring-data-jpa java based configuration:

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

}

Notice that, transactionManagerRef = "jpaTransactionManager" set the JpaTransactionManager to be used with the repositories. Spring Data JPA namespace attributes

Share:
22,692
akashihi
Author by

akashihi

Updated on March 20, 2020

Comments

  • akashihi
    akashihi about 4 years

    Is it possible to use Spring Data JPA (backed by Hibernate as JPA provider) and directly use Hibernate at the same time?

    The problem is that when i use JpaTransactionManager, i'm not able to retrieve current session with org.hibernate.HibernateException: No Session found for current thread. When i switch to HibernateTransaction manager, JPA repositories are not able to commit changes.

    Here is the part of my Spring context (with that context i'm not able to use direct Hibernate calls):

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

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

    Example of Spring data repository definition:

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

    Software versions:

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

    So, the question is - is it possible to use in the same transaction (specified by @Transactional annotation) both Spring JPA repositories and direct Hibernate calls and how to achieve that?