【问题标题】:Hibernate in Spring Boot failed to lazily initialize a collection of role, could not initialize proxy - no Session exceptionSpring Boot 中的 Hibernate 无法懒惰地初始化角色集合,无法初始化代理 - 没有 Session 异常
【发布时间】:2021-11-27 17:23:12
【问题描述】:

我有三个实体,其中两个每个都包含第三个的集合。当我通过使用第三个实例创建两个“拥有”实体来设置它们时,只有一个可以正常工作。另一个在添加时抛出此异常,即使集合的设置方式完全相同:

“拥有”实体:

@Entity
class Source(
    @OneToMany(mappedBy = "source", cascade = [CascadeType.ALL]) 
    var targets: MutableSet<Target> = mutableSetOf(),
    @Id @GeneratedValue 
    var id: Long? = null,
) {
    fun addTarget(target: Target) {
        targets.add(target)
        target.source = this
    }
}

@Entity
class Job(
    @OneToMany(mappedBy = "job", cascade = [CascadeType.ALL]) 
    var targets: MutableSet<Target> = mutableSetOf(),
    @Id @GeneratedValue 
    var id: Long? = null,
) {
    fun addTarget(target: Target) {
        targets.add(target) // exception actually thrown here
        target.job = this
    }
}

共同“拥有”的实体:

@Entity
class Target(
    @ManyToOne var source: Source? = null,
    @ManyToOne var job: Job? = null,
    @Id @GeneratedValue var id: Long? = null
)

设置如下:

@PostConstruct
@Transactional
fun init() {
    val initialJob = Job()
    initialJob.targets = mutableSetOf() // no idea why this is necessary, but without it I get the exception
    jobRepository.save(initialJob)
    
    val source = Source()
    sourceRepository.save(source)
    
    val target = Target()
    
    source.addTarget(target) // this works fine
    initialJob.addTarget(target) // but this doesn't unless I initialize the set above
    targetRepository.save(target)
    sourceRepository.save(source)
    jobRepository.save(initialJob)
}

我很困惑为什么我需要手动初始化 Job 实体上的集合,而不是 Source 实体上的集合,因为在我看来这些关系是完全相同的。另外,Job 实体已经在其构造函数中初始化集合。

【问题讨论】:

    标签: java spring-boot hibernate kotlin jpa


    【解决方案1】:

    我认为问题在于@PostConstruct@Transactional 的组合。

    @PostConstructpostProcessBeforeInitialization 阶段之后被调用。在 postProcessAfterInitialization 阶段创建 @Transactional 代理时。

    因此,该方法未包装在一个事务中,您会收到no session 异常。

    您可以尝试使用@EventListener 方法。

    @Component
    class SomeListener {
      @Autowired
      private MyService service;
    
      @EventListener
      public void onInit(ContextRefreshedEvent e) {
        service.init();
      }
    }
    

    【讨论】:

    • 很有趣。实际上,我最终进行了重构以完全避免该问题,但这是一个非常有趣的想法。你也许是对的。我会看看我是否可以设置一个场景来测试它。谢谢!
    • 很高兴能帮到你。请把我的帖子标记为答案好吗?
    • 是的,做到了。我从服务的 init() 方法中删除了 @PostConstruct 注释,并从事件侦听器中显式调用它。谢谢!
    猜你喜欢
    • 2020-11-25
    • 2021-06-15
    • 2014-05-03
    • 2016-11-10
    • 1970-01-01
    • 2021-08-20
    • 2022-07-08
    • 2018-12-12
    • 1970-01-01
    相关资源
    最近更新 更多