Транзакции JpaTransactionManager не используются Hibernate Session
Я работаю над проектом, который использует Spring Framework (4.3.3.RELEASE) и Hibernate (5.2.3.Final), и я начинаю переходить на использование Spring Data JPA.
Я только что перенесLocalSessionFactoryBean
сHibernateTransactionManager
Конфигурация с использованием конфигурации JPALocalContainerEntityManagerFactoryBean
сJpaTransactionManager
сHibernateJpaSessionFactoryBean
.
Существующий код гибернации, который используетSession
с изSessionFactory
Казалось, что все работает нормально, пока я не протестировал некоторый код, который сохраняет одну сущность, а затем запускает несколько запросов на обновление в той же транзакции, и код завершается ошибкой при обновлении sql с помощью:
javax.persistence.TransactionRequiredException: Executing an update/delete query
Журналы менеджера транзакций показали, что транзакция была активной, а затем откат, что было странно. Затем я заметил, что операция сохранения достигла базы данных.
При отладке я вижу, что у объекта сеанса, похоже, нет объекта транзакции, поэтому кажется, что сеанс гибернации не работает или не использует настроенныйJpaTransactionManager
сделки.
Когда я настраиваю дополнительный менеджер транзакций (HibernateTransactionManager
) помечены как первичныеPlatformTransactionManager
код тогда работает.
В дальнейшем, когда я перенесу код в Spring Data Jpa, я захочу использовать некоторый код Dao на основе Hibernate и некоторый репозиторий Spring Data Jpa в одной и той же транзакции. Как я могу заставить фабрику сеансов использоватьJpaTransactionManager
?
ОБНОВИТЬ:
Теперь я обнаружил, что вышеуказанный конфиг означает, что сессия не сбрасывается в базу данных менеджером транзакций, поэтому не работает правильно.
Я также обнаружил, что если я добавлю EntityManager в мой Daos:
@PersistenceContext()
private EntityManager entityManager;
и использовать:
entityManager.unwrap( Session.class )
Тогда код правильно участвует в транзакции. Но если я получуSessionFactory
(либо вводится весной, либо распаковывается изentityManagerFactory
или используяgetSessionFactory()
из развернутогоSession
) и позвонитеgetCurrentSession()
он возвращает другой объект Session, который не связан с транзакцией.
Моя конфигурация:
@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;
}
}