为啥我在 Spring-boot 应用程序中通过 SessionFactory 有循环依赖?

Posted

技术标签:

【中文标题】为啥我在 Spring-boot 应用程序中通过 SessionFactory 有循环依赖?【英文标题】:Why do I have a circular dependency through SessionFactory in Spring-boot application?为什么我在 Spring-boot 应用程序中通过 SessionFactory 有循环依赖? 【发布时间】:2020-02-02 00:52:48 【问题描述】:

我在启动 Spring-Boot 应用程序时遇到了错误。

现在,我想在 DAO 存储库中使用 HibernateDaoSupport,因为 Spring boot 不会自动创建 SessionFactory。所以,我从 EntityManagerFactory 创建了SessionFactory bean,并尝试在 DAO 类中自动装配它。

但我收到以下错误:

Description:

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

   fooDao defined in file [/home/user/test/out/production/classes/by/test/testing_the_java_service_layer/repository/FooDao.class]
┌─────┐
|  sessionFactory defined in class path resource [by/test/testing_the_java_service_layer/configuration/Config.class]
└─────┘

我不明白为什么SessionFactory 指的是FooDao 类。

以下是代码示例:

FooDao.java

@Repository
public class FooDao extends HibernateDaoSupport

    @Autowired
    public void setSessionFactories( SessionFactory sessionFactory )
    
        setSessionFactory( sessionFactory );

    

    @Transactional
    public int create( Foo entity )
    
        return (int) this.getHibernateTemplate().save( entity );
    

Config.java

@ComponentScan( basePackages =  "by.test"  )
@Configuration
public class Config

    /*
     *     Spring boot doesn't create SessionFactory bean, so we have to create it manually, using EntityManagerFactory
     */
    @Bean
    public SessionFactory sessionFactory( EntityManagerFactory entityManagerFactory )
    
        return entityManagerFactory.unwrap( SessionFactory.class );
    

Foo.java

@Entity
@Table( name = "bibis" )
public class Foo

    @Id
    @Column( name = "foo", nullable = false )
    public int foo;

    @Column( name = "bar" )
    public String bar;

TestApplication.java

@SpringBootApplication
public class TestApplication


    public static void main( String[] args )
    
        SpringApplication.run( TestApplication.class, args );
    

application.yaml

spring:
  datasource:
    username: 'bibis'
    password: 'bibis'
    schema: 'bibis'
    host: 'localhost:3306'
    url: 'jdbc:mariadb://localhost:3306/bibis'

以及来自 build.gradle 的 gradle 依赖项

implementation('org.mariadb.jdbc:mariadb-java-client')
developmentOnly 'org.springframework.boot:spring-boot-devtools'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.1.8.RELEASE'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

我尝试使用@Lazy 注释、构造函数或字段注入,但没有帮助。

【问题讨论】:

【参考方案1】:

嗯,你要明确你是要使用JPA还是Hibernate(JPA的实现),EntityManager对应JPA,SessionFactory对应Hibernate,如果使用EntityManager就不需要使用SessionFactory, EM 在后台调用休眠会话。而如果你需要一些EntityManager中没有的特定功能,可以通过调用来获取会话:

Session session = entityManager.unwrap(Session.class);

重构并尝试

【讨论】:

感谢您的回答,但我不需要 Session 对象,我只想使用需要 SessionFactory 的 HibernateTemplate。它不是生产代码,仅用于培训目的。【参考方案2】:

我已经参考了Spring Boot - Handle to Hibernate SessionFactory的答案

Config 类返回HibernateJpaSessionFactoryBean obj 而不是SessionFactory 对象。

修改后的代码为:

@ComponentScan( basePackages =  "by.test"  )
@Configuration
public class Config

    @Bean
    public HibernateJpaSessionFactoryBean sessionFactory(EntityManagerFactory entityManagerFactory)
    
        HibernateJpaSessionFactoryBean hibernateJpaSessionFactoryBean = new HibernateJpaSessionFactoryBean();
        hibernateJpaSessionFactoryBean.setEntityManagerFactory(entityManagerFactory); 
        return hibernateJpaSessionFactoryBean;
    


或者你可以这样做:

@Repository
public class FooDao extends HibernateDaoSupport

    @Autowired
    public void setSessionFactories(EntityManagerFactory entityManagerFactory)
    
        setSessionFactory(entityManagerFactory.unwrap(SessionFactory.class));
    

    @Transactional
    public int create(Foo entity)
    
        return (int) this.getHibernateTemplate().save( entity );
    

【讨论】:

第一个变体对我没有帮助。第二种变体是可以的。谢谢!但我还是不明白,为什么我有循环依赖。 SessionFactory 在哪里引用了我的 FooDao?【参考方案3】:

在我看来,当您在 setter 值中使用 @Autowired 时,它会创建循环依赖项。这是一种更好的方法,可以解决您作为属性构造函数的问题

@Repository
public class FooDao extends HibernateDaoSupport

    @Autowired
    public FooDao(SessionFactory sessionFactory)
    
        this.sessionFactory = sessionFactory
    

    @Transactional
    public int create( Foo entity )
    
        return (int) this.getHibernateTemplate().save( entity );
    

【讨论】:

不幸的是它没有帮助。看起来它不依赖于构造函数或字段注入。

以上是关于为啥我在 Spring-boot 应用程序中通过 SessionFactory 有循环依赖?的主要内容,如果未能解决你的问题,请参考以下文章

Django:为啥当我在 django 中通过 popen 使用 Ghostscript 时会出现“找不到文件”错误

在 Symbian^3 中通过 VBO 方法加载 3D 对象时获取 KERN-EXEC 3,为啥?

为啥不调用 WebBrowser 控件中通过 setTimeout 方法调度的代码

为啥在 PHP 中通过引用传递?

为啥在python中通过字符串声明unicode?

为啥我们需要一个默认构造函数来在 C++ 中通过引用传递一个对象?