4

I have problem with new Spring Boot version 2.0.0. I need to create SessionFactory bean, for that I need Spring to inject EntityManager.

package cz.moravec;
import cz.moravec.provisioning.Provisioner;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceUnit;

@SpringBootApplication
@EntityScan("cz.moravec.data")
public class Main {

//    @Bean
//    public CountryDao countryDao() {
//        return new CountryDao();
//    }
//
//    @Bean
//    public TownDao townDao() {
//        return new TownDao();
//    }


    @Autowired
    @Bean
    @Transactional
    public SessionFactory sessionFactory(EntityManager entityManager) {
        Session session = entityManager.unwrap(Session.class);
        return session.getSessionFactory();
    }

    @Profile({"devel", "test"})
    @Bean(initMethod = "doProvision")
    public Provisioner provisioner() {
        return new Provisioner();
    }


    public static void main(String[] args) {

        SpringApplication app = new SpringApplication(Main.class);
        ApplicationContext ctx = app.run(args);

//        CountryDao countryDao = ctx.getBean(CountryDao.class);

//        List<Country> countries = countryDao.getCountries();

//        UsersDao usersDao = ctx.getBean(UsersDao.class);
//
//        List<User> users = usersDao.getAllUsers();
//        System.out.println(users);

    }

}

This code works when using Spring Boot 1.5.11.RELEASE. But does not with Spring Boot 2.0.0.RELEASE.

When I run code ApplicationContext is not created because of cycle dependency.

There is output from console.

19:44:31.282 [main] WARN  o.s.c.a.AnnotationConfigApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sessionFactory' defined in cz.moravec.Main: Unsatisfied dependency expressed through method 'sessionFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.orm.jpa.SharedEntityManagerCreator#0': Cannot resolve reference to bean 'sessionFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'sessionFactory': Requested bean is currently in creation: Is there an unresolvable circular reference?
    Disconnected from the target VM, address: '127.0.0.1:11537', transport: 'socket'
    19:44:31.286 [main] INFO  o.s.b.a.l.ConditionEvaluationReportLoggingListener - 

    Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
    19:44:31.288 [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter - 

    ***************************
    APPLICATION FAILED TO START
    ***************************

    Description:

    The dependencies of some of the beans in the application context form a cycle:

    ┌─────┐
    |  sessionFactory defined in cz.moravec.Main
    ↑     ↓
    |  org.springframework.orm.jpa.SharedEntityManagerCreator#0
    └─────┘

Maven pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cz.moravec</groupId>
    <artifactId>semester_project</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>


        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

    </dependencies>
</project>
Jan Moravec
  • 496
  • 6
  • 9
  • Why on earth is that method `@Transactional`? Why on earth are you getting an `EntityManager` to get the `Session` to get the `SessioNFactory`. If there is a complex way to get the `SessionFactory` this is it, and I'm actually amazed that that even worked. Instead inject the `EntityManagerFactory` and unwrap that to the `SessionFactory`. – M. Deinum Apr 11 '18 at 18:54
  • without @Transactional I get Hibernate cannot unwrap interface org.hibernate.SessionFactory, – Jan Moravec Apr 11 '18 at 19:08
  • Yeah because you are using the `EntityManager` don't use that. Use the `EntituManagerFactory.unwrap` instead. – M. Deinum Apr 11 '18 at 19:09
  • As stated don't use the `EntityManager` use the `EntityManagerFactory` instead... – M. Deinum Apr 11 '18 at 19:11
  • 1
    Ok, that works, I actually had it like this before, but I was trying something to fix it. But this does not resolve my problem with cycle dependecy ... The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | sessionFactory defined in cz.moravec.Main └─────┘ – Jan Moravec Apr 11 '18 at 19:16
  • Does it work or doesn't it? Your comment is confusing. Is it still the same dependency? – M. Deinum Apr 11 '18 at 19:17
  • No it doesn't. It throws: Error creating bean with name 'sessionFactory': Requested bean is currently in creation: Is there an unresolvable circular reference? – Jan Moravec Apr 11 '18 at 19:22
  • Try `ObjectProvider` instead of directly the `EntityManagerFactory`. Also something else why use a `SessionFactory` why not simply use JPA in your code? – M. Deinum Apr 11 '18 at 19:24
  • I tried but result is without change . Its school project and we should do it this way. Next lesson we will do it with JPA. – Jan Moravec Apr 11 '18 at 19:33
  • 1
    @JanMoravec Did you find the answer? I'm facing the same problem now. – Mikhail Antonov May 30 '18 at 12:04
  • @JanMoravec do you have any update on the issue? I am facing the same issue .. Tried multiple solutions..nothing worked. – vishal munde Aug 05 '18 at 18:45
  • 1
    Sorry guys I did not solve the issue. – Jan Moravec Aug 06 '18 at 10:56

1 Answers1

0

Is is a MUST to set the SessionFactory as bean when init the SpringBoot application? Another solution is to get the hibernate sessionFactory / session in your Dao classes.

Following is an example:

@Repository
public class CountryDao {

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    private Session getSessionFactory() {
        return entityManagerFactory.unwrap(SessionFactory.class);
    }
...
}

I can successfully get the SessionFactory in the Dao class.

But if your purpose is to only get the current Hibnerate Session object, following is a more cleaner way:

@Repository
public class CountryDao {

    @Autowired
    private EntityManager entityManager;

    private Session getSession() {
        return entityManager.unwrap(Session.class);
    }
...

}

The SpringBoot i'm testing is 2.1.3.RELEASE

Raymond Chiu
  • 934
  • 9
  • 15