Reputation: 163
What is a good way to apply the @Transactional annotation to a callback function?
I have two service classes:
@Service
class PersonService(
private val personRepository: PersonRepository,
private val schedulerService: SchedulerService,
) {
fun someMethod() {
schedulerService.scheduleTask(this::someTransactionalMethod)
}
@Transactional
fun someTransactionalMethod(int: Int) {
personRepository.streamAllBy().forEach {
println(it)
}
}
}
@Service
class SchedulerService {
fun scheduleTask(task: Task) {
task.execute(5)
}
}
(The Task
is just a functional interface for my callbacks:)
fun interface Task {
fun execute(int: Int)
}
Calling PersonService.someMethod()
I get an exception:
org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed; Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction
I expected the @Transactional
annotation to create a transaction since both the caller and called methods are in separate spring beans, so I don't see why the proxy doesn't work.
However I noticed that if I move the someTransactionalMethod
to a 3rd service and referencing it as something like schedulerService.scheduleTask(thirdService::someTransactionalMethod)
, the proxy works fine.
Any suggestions why is this the case, and how can I use callback functions from the same service?
Upvotes: 1
Views: 259
Reputation: 120
It is because the framework (Spring?) replaces references to transactional methods by proxies only when a dependency injection is used. You pass a lambda referring to the someTransactionalMethod of the original PersonService class and the framework has no chance to replace it by a proxy. As you have found out yourself, wrapping the call in another level of component solves the problem.
Upvotes: 1