Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot hibernate no transaction is in progress

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?

like image 585
Daimon Avatar asked Jun 01 '18 19:06

Daimon


People also ask

Why is my Hibernate session not working in spring?

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.

Why is my transaction management not working in spring?

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.

What is hibernatetransactionmanager used for?

The HibernateTransactionManager is for RESOURCE_LOCAL transactions. In your case, it’s ok to use the JTA one. But why use Spring and WebSphere?

Should I upgrade from Hibernate to spring?

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.


1 Answers

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.

like image 172
Naros Avatar answered Oct 05 '22 23:10

Naros