Spring Boot 2.0.0 循环bean依赖EntityManger和SessionFactory

Posted

技术标签:

【中文标题】Spring Boot 2.0.0 循环bean依赖EntityManger和SessionFactory【英文标题】:Spring Boot 2.0.0 cycle bean dependency EntityManger and SessionFactory 【发布时间】:2018-09-21 17:38:31 【问题描述】:

我对新的 Spring Boot 版本 2.0.0 有疑问。 我需要创建 SessionFactory bean,为此我需要 Spring 来注入 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);

    


此代码在使用 Spring Boot 1.5.11.RELEASE 时有效。但不适用于 Spring Boot 2.0.0.RELEASE

当我运行代码时,由于循环依赖,没有创建 ApplicationContext。

控制台有输出。

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>

【问题讨论】:

为什么是@Transactional这个方法?你到底为什么要得到EntityManager 得到Session 得到SessioNFactory。如果有一种复杂的方法可以获取SessionFactory,那就是它,我真的很惊讶它甚至可以工作。而是注入EntityManagerFactory 并将其解包到SessionFactory 没有@Transactional 我得到Hibernate 无法解开接口org.hibernate.SessionFactory, 好吧,这行得通,我以前确实有过这样的情况,但我正在尝试修复它。但这并不能解决我的循环依赖问题......应用程序上下文中一些bean的依赖关系形成了一个循环:┌─────┐ | cz.moravec.Main 中定义的 sessionFactory └──────┘ @JanMoravec 你找到答案了吗?我现在面临同样的问题。 对不起,我没有解决问题。 【参考方案1】:

在初始化 SpringBoot 应用程序时是否必须将 SessionFactory 设置为 bean? 另一种解决方案是在您的 Dao 类中获取 hibernate sessionFactory / session。

以下是一个例子:

@Repository
public class CountryDao 

    @Autowired
    private EntityManagerFactory entityManagerFactory;

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

我可以成功获取到 Dao 类中的 SessionFactory。

但是如果你的目的是只获取当前的 Hibnerate Session 对象,下面是一种更简洁的方法:

@Repository
public class CountryDao 

    @Autowired
    private EntityManager entityManager;

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


我正在测试的 SpringBoot 是 2.1.3.RELEASE

【讨论】:

以上是关于Spring Boot 2.0.0 循环bean依赖EntityManger和SessionFactory的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot UnsatisfiedDependencyException 创建名称为无法解析的循环引用的 bean 时出错

当我尝试使用 Spring Boot 为 SessionFactory 创建 bean 时无法解析的循环引用

Spring Boot创建动态数据源:当前正在创建请求的bean:是否存在无法解析的循环引用?

Spring Boot源码:循环依赖

Spring Boot + Quartz:“请求的 bean 当前正在创建中:是不是存在无法解析的循环引用?”

升级 spring boot 2.0.0.RC2 异常 No ServletContext set