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();
}
}