35

I am getting error when I start my app JPA: Reactive Repositories are not supported by JPA. My Pom has below dependencies and i am using Spring Boot 2.0.5

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>

here is my repository interface.

public interface CustomerRepository extends ReactiveCrudRepository {
}

when I start my application it throws error:

org.springframework.dao.InvalidDataAccessApiUsageException: Reactive Repositories are not supported by JPA. Offending repository is com.example.demo.CustomerRepository!
    at org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport.useRepositoryConfiguration(RepositoryConfigurationExtensionSupport.java:310) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
    at org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport.getRepositoryConfigurations(RepositoryConfigurationExtensionSupport.java:103) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
    at org.springframework.data.repository.config.RepositoryConfigurationDelegate.registerRepositoriesIn(RepositoryConfigurationDelegate.java:126) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
    at org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport.registerBeanDefinitions(AbstractRepositoryConfigurationSourceSupport.java:60) ~[spring-boot-autoconfigure-2.0.5.RELEASE.jar:2.0.5.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:358) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) ~[na:1.8.0_144]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:357) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:145) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:117) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:328) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:233) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:271) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:91) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:694) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.0.9.RELEASE.jar:5.0.9.RELEASE]
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:61) ~[spring-boot-2.0.5.RELEASE.jar:2.0.5.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780) [spring-boot-2.0.5.RELEASE.jar:2.0.5.RELEASE]

Can someone please advise if JPA is not supported then what should I use, any help is appreciated..

user10400282
  • 606
  • 1
  • 6
  • 11

8 Answers8

33

If you want all the benefits of reactive, async / non-blocking, you'll need to make the whole stack async / non-blocking. JDBC is indeed inherently a blocking API, so you can't build a fully reactive / non-blocking app if you need to access the database through JDBC.

But you still you need relational database then will recommend to use rxjava2-jdbc and here is full example of using RxJava and RxJava jdbc spring-webflux-async-jdbc-sample

Seems currently Spring webflux support Mongodb, Redis, etc nosql reactive so instead of JPA use spring-boot-starter-data-mongodb-reactive .

kj007
  • 6,073
  • 4
  • 29
  • 47
  • 3
    Good answer. However that example doesn't use https://github.com/davidmoten/rxjava2-jdbc if that's what you meant. It simply wraps the `CrudRepository` in `Flux` and `Mono`s. – heez Oct 25 '18 at 23:02
  • Spring Data Redis does not support reactive repositories: https://jira.spring.io/browse/DATAREDIS-796 – Alexander Berezovsky Mar 20 '19 at 15:04
  • @kj007 Can you explain what you mean by "blocking API"? A JDBC query returns a `ResultSet` and AFAIK that call can return quite quickly -- it doesn't need to actually fetch the whole result set, even though that is the default behaviour of many databases. I believe both MySQL and PostgreSQL have ways of streaming result sets a few records at a time instead of all at once. – Frans Oct 07 '20 at 12:45
  • 4
    @Frans despite being quick, it still blocks a thread while waiting for a response. Reactive is based on messaging / callbacks so you could make a database request, close the thread, and the callback will be handled in another thread. – cody mikol Dec 01 '20 at 19:01
  • I agree with @heez in that [spring-webflux-async-jdbc-sample](https://github.com/chang-chao/spring-webflux-async-jdbc-sample) doesn't really have the right effect. If a query waits until the full result set is retrieved (normal behavior for Relational DBs) before feeding rows into the Flux then you won't get the same benefit as you would from a reactive, NoSQL DB. Grabbing data in chunks (paginated) would probably make a RDB query act a bit more reactive. – TastyWheat Jul 03 '21 at 16:45
7

I don't know about previous support, but as of 09 June 2019, you can absolutely use WebFlux with JPA Repositories!

Your stack doesn't have to be fully reactive. I like WebFlux, but need a relational database.

I have:

  • spring-boot-starter-data-redis-reactive
  • spring-boot-starter-webflux
  • spring-boot-starter-data-jpa

edit: (FYI) code is in Kotlin, but should still work in Java.

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = ["com.example.core.repositories"])
@EnableJpaAuditing
class PersistenceConfig

src/core/models/User

@Entity
@Table(name = "user")
class User(
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "user_id")
    var id: Long,

    @Column(name = "username")
    var username: String,

    @Column(name = "created_date", nullable = false, updatable = false)
    @CreatedDate
    @Temporal(TemporalType.TIMESTAMP)
    val createdDate: Date,

    @Column(name = "modified_date")
    @LastModifiedDate
    @Temporal(TemporalType.TIMESTAMP)
    val modifiedDate: Date
) : Serializable {

    /**
     * This constructor is not to be used. This is for hibernate,
     * which requires an empty constructor.
     */
    constructor() : this(1L, "", "", Date(), Date())

    companion object {
        private const val serialVersionUID = 2398467923L
    }

}

I got the same JPA: Reactive Repositories are not supported by JPA. error when I was still returning mono objects from the Spring Data query like Mono<User>. However, if you remove the Mono wrapper, it should work just fine.

src/core/repositories/UserRepository

@Repository
interface UserRepository: CrudRepository<User, Long> {

    fun findUserByUsername(username: String): User?

}
NanoNova
  • 829
  • 10
  • 19
Clement
  • 4,491
  • 4
  • 39
  • 69
  • 8
    By using that you're losing transaction support and more. Your session is only active when you call findUserByUsername - not any longer. So no support for lazy loading of relationships etc. – Boni2k Nov 12 '19 at 15:23
  • If you return a User instead of a Mono then, as a java-user, I don't see how this can be non-blocking. The implementation needs to wait for a User to be loaded from the database in order to create one, right? – julaine Nov 14 '22 at 15:21
  • @Boni2k: Why would you loose transaction support? As long as the whole code in `@Transactional` does not call any reactive interfaces, the code should run in the same thread. Of course it's going to be blocking, but that's what the author of the answer says: You can mix reactive and non-reactive. – chris Mar 28 '23 at 15:31
  • @chris you're answering a remark from 2019 - by now the kotlin + coroutine support advances quite well indeed – Boni2k Mar 29 '23 at 16:04
4

Even though your chosen database (H2) doesn’t support non-blocking reactive queries, you can still fetch data in a blocking fashion and then translate it into a reactive type as soon as possible for the benefit of upstream components.

You can’t do anything about the blocking nature of invoking a method on a JPA repository. What you can do, however, is convert the non-reactive type into a reactive type (Flux/Mono) as soon as you receive it, so that you can deal with the results reactively from there on.

Or you can use another databases that support reactive model like Cassandra, MongoDB, Couchbase, or Redis.

Iakov Burtsev
  • 377
  • 1
  • 2
  • 15
4

Why don't you use R2DBC? It supports reactive access to relational database store and supports Postgresql, SQL server and more.

https://spring.io/projects/spring-data-r2dbc

WesternGun
  • 11,303
  • 6
  • 88
  • 157
2

I solved like this

import org.springframework.data.repository.reactive.ReactiveCrudRepository;


@Repository
public interface ProductRepository extends ReactiveCrudRepository<Product, Long> {

    @Query("SELECT * FROM product WHERE id = :id")
    Flux<Product> findById(@Param("id") Long id);

}
2

If you plan to use blocking API's then you could simply use SpringMVC, as it's best suited to handle these technologies, as given in this documentation.

From the documentation

If you have blocking persistence APIs (JPA, JDBC) or networking APIs to use, Spring MVC is the best choice for common architectures at, least. It is technically feasible with both Reactor and RxJava to perform blocking calls on a separate thread but you would not be making the most of a non-blocking web stack.

else you can use rxjava2 or even Reactor with slight modification to handle blocking APIs. e.g this git hub project


PS: I Know I'm late here, but this if for anyone else who is intrested.

Akash Jain
  • 684
  • 9
  • 23
1

You can try this small JPA reactive wrapper, is not really reactive but runs the JDBC calls on a isolated ThreadPool.

https://github.com/IBM/reactive-components

Javier Sainz
  • 301
  • 1
  • 2
  • 7
0

Pleas try it

@Configuration
@EnableR2dbcAuditing
@EnableR2dbcRepositories(basePackages = {"com.abc.test.repositories"})
class R2dbcConfig extends AbstractR2dbcConfiguration {
    @Override
    @Bean
    public ConnectionFactory connectionFactory() {
       //add your DB connection config
    }
}

Hope to help you!

phancuongviet
  • 311
  • 2
  • 11