Skip to content

suspending support within JpaRepository #3598

@cmdjulian

Description

@cmdjulian

This is a small extract from my demo project from GitHub including some tests:

@RestController
class HelloController(private val service: HelloService) {
    @PutMapping("/hello/{id}")
    suspend fun update(@PathVariable("id") id: Long): Unit = service.update(id)

    @GetMapping("/hello/{id}")
    suspend fun find(@PathVariable("id") id: Long): HelloEntity? = service.find(id)
}

@Service
class HelloService(private val helloRepository: HelloRepository) {
    @Transactional
    suspend fun update(id: Long) {
        helloRepository.updateMessage(id, "Hello, Spring Boot!")
    }

    @Transactional
    suspend fun find(id: Long): HelloEntity? = helloRepository.suspendFindById(id)
}

@Entity
class HelloEntity(@Id var id: Long? = null, var message: String = "Hello")

@Repository
interface HelloRepository : ListCrudRepository<HelloEntity, Long> {
    @Modifying
    @Query("update HelloEntity e set e.message = :message where e.id = :id")
    suspend fun updateMessage(id: Long, message: String): Int

    @Query("select e from HelloEntity e where e.id = :id")
    suspend fun suspendFindById(id: Long): HelloEntity?

    @Query("select e from HelloEntity e")
    fun findAllFlow(): Flow<HelloEntity>
}

I found multiple problems:

  • Having a method like fun findAllFlow(): Flow<HelloEntity> results in startup failure as IllegaleStateException: Reactive Repositories are not supported by JPA
  • Having a method like suspend fun suspendFindById(id: Long): HelloEntity? returning null cause the underlying entity is not found results in InvalidDataAccessApiUsageException: Reactive source object must not be null
  • Same method as before but this time a record is found: ConversionFailedException: Failed to convert from type [com.example.demo.HelloEntity] to type [reactor.core.publisher.Flux<?>] for value [com.example.demo.HelloEntity@15045e40]
  • As you can see from the demo code, the Service is annotated with @Transactional. When calling the service update method, an error is triggered from some of the Spring Data repository infrastructure code: TransactionRequiredException: Executing an update/delete query. Seems like the transaction is not properly propagated

At the moment suspend functions are pretty unusable at all from my app. Is this something which should be supported? Would definitely appreciate it. The only thing I found is within the Spring Data Commons Reference Documentation, which seems to outline support.

Some more background information: We use a lot of suspending functions in our Kotlin Spring Boot app which get invoked within the controller. At the moment we use blocking JPA repository methods and wrap them manually in a withContext(Dispatcher.IO) call, which is not just tedious, but also error prone.

Being able to natively use them within the repository would help us in reducing complexity here.

For instance, JooQ also supports Kotlin Coroutines even for blocking JDBC drivers.

Other considered resources:

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: declinedA suggestion or change that we don't feel we should currently apply

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions