Home > Software design >  Hibernate in Spring Boot failed to lazily initialize a collection of role, could not initialize prox
Hibernate in Spring Boot failed to lazily initialize a collection of role, could not initialize prox

Time:10-11

I have three entities, two of which each contain a collection of the third. When I go to set them up by creating two of the "owning" entities with an instance of the third, only one of them works properly. The other throws this exception when adding it, even though the collections are set up exactly the same way:

"Owning" entities:

@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
    }
}

Mutually "owned" entity:

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

And here's the setup:

@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)
}

I'm baffled as to why I need to manually initialize the collection on the Job entity but not on the Source entity, seeing as the relationships appear to me to be exactly the same. Plus, the Job entity is already initializing the set in its constructor.

CodePudding user response:

I think the problem is about the combination of @PostConstruct and @Transactional.

The @PostConstruct is being called after postProcessBeforeInitialization stage. While @Transactional proxy is being created on postProcessAfterInitialization stage.

So, the method is not wrapped within the one transaction and you get no session exception.

You can try to use the @EventListener approach.

@Component
class SomeListener {
  @Autowired
  private MyService service;

  @EventListener
  public void onInit(ContextRefreshedEvent e) {
    service.init();
  }
}
  • Related