I'm using spring boot and it perfectly makes me entity manager. And I decided to test getting session factory from the entity manager and to use it for an example. But I get the next problem:javax.persistence.TransactionRequiredException: no transaction is in progress
properties
spring.datasource.url= jdbc:postgresql://localhost:5432/ring
spring.datasource.username=postgres
spring.datasource.password=root
spring.jpa.show-sql = false
spring.jpa.properties.hibernate.format_sql=false
#Note: The last two properties on the code snippet above were added to suppress an annoying exception
# that occurs when JPA (Hibernate) tries to verify PostgreSQL CLOB feature.
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false
spring.jpa.properties.hibernate.current_session_context_class = org.springframework.orm.hibernate5.SpringSessionContext
service class
package kz.training.springrest.service;
import kz.training.springrest.entity.User;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
@Service
public class UserService {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void insertUser(User user) {
SessionFactory sessionFactory = entityManager.unwrap(Session.class).getSessionFactory();
Session session = sessionFactory.getCurrentSession();
session.save(user);
}
}
runner
package kz.training.springrest.run;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EntityScan("kz.training.springrest.entity")
@EnableTransactionManagement
@ComponentScan(basePackages="kz.training.springrest")
public class SpringrestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringrestApplication.class, args);
}
}
Do you have any ideas how to solve it?
You're running into your problem because of the call to #getCurrentSession (). What is happening is Spring creates the EntityManager, then inside your method when you make the call to #getCurrentSession (), you're asking Hibernate to create a second session that is not bound to the transaction started by your @Transactional annotation.
There’s a transaction management issue due to a miss-configuration of Spring. You need to ask the question on the Spring forum. Thank you very much for guiding me to ask in related plateform and offcourse thanks for your quick response.
The HibernateTransactionManager is for RESOURCE_LOCAL transactions. In your case, it’s ok to use the JTA one. But why use Spring and WebSphere?
If you upgrade Hibernate, most likely that you need to upgrade Spring as well and use the org.springframework.orm.hibernate5.HibernateTransactionManager. We are also migrating from Spring 2.3 to Spring 4.3.
I don't quite understand why you're making your service method so unnecessarily complex. You should simply be able to do it this way
@Transactional
public void insertUser(User user) {
entityManager.persist( user );
}
If there are points where you need access to the native Hibernate Session
you can simply unwrap and use the Session
directly like this:
@Transactional
public void doSomethingFancyWithASession() {
Session session = entityManager.unwrap( Session.class );
// use session as needed
}
The notion here is that Spring provides you an already functional EntityManager
instance by you using the @PersistenceContext
annotation. That instance will safely be usable by the current thread your spring bean is being executed within.
Secondly, by using @Transactional
, this causes Spring's transaction management to automatically make sure that the EntityManager
is bound to a transaction, whether that is a RESOURCE_LOCAL
or JTA
transaction is based on your environment configuration.
You're running into your problem because of the call to #getCurrentSession()
.
What is happening is Spring creates the EntityManager
, then inside your method when you make the call to #getCurrentSession()
, you're asking Hibernate to create a second session that is not bound to the transaction started by your @Transactional
annotation. In short its essentially akin to the following:
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
Session aNewSession = entityManager.unwrap( Session.class )
.getFactory()
.getCurrentSession();
// at this point entityManager is scoped to a transaction
// aNewSession is not scoped to any transaction
// this also likely uses 2 connections to the database which is a waste
So follow the paradigm I mention above and you should no longer run into the problem. You should never need to call #getCurrentSession()
or #openSession()
in a Spring environment if you're properly allowing Spring to inject your EntityManager
instance for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With