I am working on a project using Spring framework (4.3.3.RELEASE) and Hibernate (5.2.3.Final), and I'm starting to move on to using Spring JPA data.
I just migrated the LocalSessionFactoryBean
configuration with HibernateTransactionManager
to the JPA LocalContainerEntityManagerFactoryBean
configuration with JpaTransactionManager
using HibernateJpaSessionFactoryBean
.
The existing hibernation code that uses Session
from SessionFactory
seems to work fine until I check some code that saves only one object and then runs some update requests in a single transaction and the code doesn't work when updating sql with:
javax.persistence.TransactionRequiredException: Executing an update/delete query
Transaction manager logs showed that the transaction was active and then roll back, which was strange. Then I noticed that the save operation reached the database.
When debugging, I see that the session object has no transaction object, so it seems that the hibernate session is not working or not using the configured JpaTransactionManager
transactions.
When I configure an additional transaction manager ( HibernateTransactionManager
), marked as the primary PlatformTransactionManager
, then the code works.
Moving forward, when I port the code to Spring Data Jpa, I want to use some Hibernate based Dao code and some Spring Data Jpa repository in the same transaction. How can I get a factory session to use JpaTransactionManager
?
UPDATE:
Now I have found that the above configuration means that the session is not dumped into the database by the transaction manager, therefore it does not work correctly.
I also found that if I introduce EntityManager in my Daos:
@PersistenceContext() private EntityManager entityManager;
and use:
entityManager.unwrap( Session.class )
Then the code is involved in the transaction correctly. But if I get a SessionFactory
(either entered by spring, or deployed from entityManagerFactory
, or using getSessionFactory()
from the deployed Session
), and the call to getCurrentSession()
, it returns another session object that is not connected to the transaction.
My configuration:
@Configuration @EnableJpaRepositories( basePackages = "com.mycompany.common.services", transactionManagerRef = "jpaTransactionManager" ) @EnableTransactionManagement(order = 5) public class PersistenceConfiguration { @Bean @Qualifier(value = "entityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory( DataSource dataSource ) { LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); factory.setPersistenceUnitName( "entityManagerFactory" ); factory.setPackagesToScan( entityPackages() ); factory.setJpaVendorAdapter( getHibernateJpaVendorAdapter() ); factory.setJpaProperties( getJpaProperties() ); factory.setDataSource( dataSource ); factory.afterPropertiesSet(); return factory; } @Bean @Qualifier(value = "jpaTransactionManager") public PlatformTransactionManager jpaTransactionManager( EntityManagerFactory entityManagerFactory, DataSource dataSource ) { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory( entityManagerFactory ); txManager.setDataSource( dataSource ); return txManager; } @Bean @Qualifier(value = "sessionFactory") public FactoryBean<SessionFactory> sessionFactory( EntityManagerFactory entityManagerFactory ) { HibernateJpaSessionFactoryBean hibernateJpaSessionFactoryBean = new HibernateJpaSessionFactoryBean(); hibernateJpaSessionFactoryBean.setEntityManagerFactory( entityManagerFactory ); return hibernateJpaSessionFactoryBean; } // How do I remove this and just use the one transaction manager above? /* @Bean @Qualifier(value = "hibernateTransactionManager") @Primary public PlatformTransactionManager hibernateTransactionManager( SessionFactory sessionFactory ) { HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager( sessionFactory ); return hibernateTransactionManager; } */ @Bean public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { return new PersistenceExceptionTranslationPostProcessor(); } protected HibernateJpaVendorAdapter getHibernateJpaVendorAdapter() { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setGenerateDdl( isGenerateDDL() ); vendorAdapter.setDatabase( Database.MYSQL ); vendorAdapter.setDatabasePlatform( com.mycompany.common.utils.hibernate.MySQL56InnoDBDialect.class.getName() ); return vendorAdapter; } protected Properties getJpaProperties() { Properties properties = new Properties(); properties.put("hibernate.current_session_context_class", "org.springframework.orm.hibernate5.SpringSessionContext"); properties.put("hibernate.hbm2ddl.auto", "validate"); properties.put("hibernate.transaction.flush_before_completion", "true"); properties.put("hibernate.transaction.auto_close_session", "false"); properties.put("hibernate.use_outer_join", "true"); properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"); properties.put("hibernate.cache.use_second_level_cache", "true"); properties.put("net.sf.ehcache.configurationResourceName", "META-INF/resources/ehcache-hibernate.xml"); properties.put("hibernate.cache.use_query_cache", "true"); properties.put("hibernate.jdbc.batch_size", "100"); properties.put("hibernate.generate_statistics", "true"); properties.put("hibernate.format_sql", "true"); properties.put("hibernate.use_sql_comments", "true"); properties.put("org.hibernate.SQL", "info"); return properties; } protected boolean isGenerateDDL() { return false; } }