szmozes
szmozes

Reputation: 163

Apply @Transactional on callback function

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

Answers (1)

majster
majster

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

Related Questions