Hibernate5 SessionImpl is not recognizing transaction started by JpaTransactionManager
I recently migrated a legacy java web application to spring-boot, Hibernate. Jersey was already used in legacy app, so continued with spring-boot-starter-jersey. So the stack is:
RestfulService(Jersey) -> Spring Service -> Hibernate DAO (legacy code).
Both Spring service and Hibernate DAOs are used through respective public interface and has @Transactional on public method of implementation class.
Hibernate DAO looks like:
@Repository public class UserDaoImpl implements UserDao {
@Autowired
@Qualifier("entityManagerFactory")
EntityManagerFactory entityManagerFactory;
public SessionFactory getSessionFactory() {
return entityManagerFactory.unwrap(SessionFactory.class);
}
@Override
public User getUserByUserName(String userName) throws Exception {
User user = null;
Criteria criteria = getSessionFactory().getCurrentSession().createCriteria(User.class);
criteria.add(Restrictions.eq("userName", userName));
user = (User) criteria.uniqueResult();
return user;
}
}
With JpaTransactionManager I was getting exception :
javax.persistence.TransactionRequiredException: no transaction is in progress.
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3505)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1427)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1423)
at org.springframework.orm.hibernate5.SessionFactoryUtils.flush(SessionFactoryUtils.java:147)
at org.springframework.orm.hibernate5.SpringSessionSynchronization.beforeCommit(SpringSessionSynchronization.java:95)
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationUtils.java:96)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:922)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:532)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.iotu.serviceImpl.UserServiceImpl$$EnhancerBySpringCGLIB$$d0c10f8c.checkLoginDetails(<generated>)
at com.iotu.webservices.UserWS.userAuthentication(UserWS.java:1467)
at com.iotu.webservices.UserWS$$FastClassBySpringCGLIB$$66e6577d.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684)
at com.iotu.webservices.UserWS$$EnhancerBySpringCGLIB$$99ca5efe.userAuthentication(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:76)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:148)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:191)
Switched to HibernateTransactionManager and Hibernate is able to recognize transaction in progress. No exception thrown.
@Configuration
@EnableJpaRepositories("com.app.repository")
public class AppConfiguration {
@Bean
public HibernateTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
SessionFactory sf = entityManagerFactory.unwrap(SessionFactory.class);
return new HibernateTransactionManager(sf);
}
But it has a down side. For newer features i am using spring data repository. With HibernateTransactionManager, the JpaRepository's save method is NOT working on entity. Its not firing the update sql (No error on console). findXX methods are working fine.
@Repository
public interface LessonCategoryRepository extends JpaRepository<LessonCategory, Long> {
public List<LessonCategory> findByCategoryAndLanguage(@Param("category") String category, @Param("language") String language);
}
When I switch to JpaTransactionManager, updates through JpaRepository are working fine but start getting the javax.persistence.TransactionRequiredException: no transaction is in progress for services calls that use legacy DAO classes..
To summarize:
- With HibernateTransactionManager: Hibernate5 recognizes current transaction but JpaRepository is not persisting data.
- With JpaTransactionManager: Hibernate5 session does NOT recognizes current transaction but JpaRepository is persisting data.
Can I make JpaTransactionManager to work with existing hibernate DAO classes? Or can I make HibernateTransactionManager to work with JpaRepository? .. so that I can use legacy DAO classes as is but can continue using the JpaRepositories.