R2DBC Repositories

This chapter points out the specialties for repository support for R2DBC. This builds on the core repository support explained in Working with Spring Data Repositories. Before reading this chapter, you should have a sound understanding of the basic concepts explained there.spring-doc.cn

Usage

To access domain entities stored in a relational database, you can use our sophisticated repository support that eases implementation quite significantly. To do so, create an interface for your repository. Consider the following Person class:spring-doc.cn

Sample Person entity
public class Person {

  @Id
  private Long id;
  private String firstname;
  private String lastname;

  // … getters and setters omitted
}

The following example shows a repository interface for the preceding Person class:spring-doc.cn

Basic repository interface to persist Person entities
public interface PersonRepository extends ReactiveCrudRepository<Person, Long> {

  // additional custom query methods go here
}

To configure R2DBC repositories, you can use the @EnableR2dbcRepositories annotation. If no base package is configured, the infrastructure scans the package of the annotated configuration class. The following example shows how to use Java configuration for a repository:spring-doc.cn

Java configuration for repositories
@Configuration
@EnableR2dbcRepositories
class ApplicationConfig extends AbstractR2dbcConfiguration {

  @Override
  public ConnectionFactory connectionFactory() {
    return …
  }
}

Because our domain repository extends ReactiveCrudRepository, it provides you with reactive CRUD operations to access the entities. On top of ReactiveCrudRepository, there is also ReactiveSortingRepository, which adds additional sorting functionality similar to that of PagingAndSortingRepository. Working with the repository instance is merely a matter of dependency injecting it into a client. Consequently, you can retrieve all Person objects with the following code:spring-doc.cn

Paging access to Person entities
@ExtendWith(SpringExtension.class)
@ContextConfiguration
class PersonRepositoryTests {

  @Autowired
  PersonRepository repository;

  @Test
  void readsAllEntitiesCorrectly() {

    repository.findAll()
      .as(StepVerifier::create)
      .expectNextCount(1)
      .verifyComplete();
  }

  @Test
  void readsEntitiesByNameCorrectly() {

    repository.findByFirstname("Hello World")
      .as(StepVerifier::create)
      .expectNextCount(1)
      .verifyComplete();
  }
}

The preceding example creates an application context with Spring’s unit test support, which performs annotation-based dependency injection into test cases. Inside the test method, we use the repository to query the database. We use StepVerifier as a test aid to verify our expectations against the results.spring-doc.cn

Result Mapping

A query method returning an Interface- or DTO projection is backed by results produced by the actual query. Interface projections generally rely on mapping results onto the domain type first to consider potential @Column type mappings and the actual projection proxy uses a potentially partially materialized entity to expose projection data.spring-doc.cn

Result mapping for DTO projections depends on the actual query type. Derived queries use the domain type to map results, and Spring Data creates DTO instances solely from properties available on the domain type. Declaring properties in your DTO that are not available on the domain type is not supported.spring-doc.cn

String-based queries use a different approach since the actual query, specifically the field projection, and result type declaration are close together. DTO projections used with query methods annotated with @Query map query results directly into the DTO type. Field mappings on the domain type are not considered. Using the DTO type directly, your query method can benefit from a more dynamic projection that isn’t restricted to the domain model.spring-doc.cn

Working with multiple Databases

When working with multiple, potentially different databases, your application will require a different approach to configuration. The provided AbstractR2dbcConfiguration support class assumes a single ConnectionFactory from which the Dialect gets derived. That being said, you need to define a few beans yourself to configure Spring Data R2DBC to work with multiple databases.spring-doc.cn

R2DBC repositories require R2dbcEntityOperations to implement repositories. A simple configuration to scan for repositories without using AbstractR2dbcConfiguration looks like:spring-doc.cn

@Configuration
@EnableR2dbcRepositories(basePackages = "com.acme.mysql", entityOperationsRef = "mysqlR2dbcEntityOperations")
static class MySQLConfiguration {

    @Bean
    @Qualifier("mysql")
    public ConnectionFactory mysqlConnectionFactory() {
        return …
    }

    @Bean
    public R2dbcEntityOperations mysqlR2dbcEntityOperations(@Qualifier("mysql") ConnectionFactory connectionFactory) {

        DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);

        return new R2dbcEntityTemplate(databaseClient, MySqlDialect.INSTANCE);
    }
}

Note that @EnableR2dbcRepositories allows configuration either through databaseClientRef or entityOperationsRef. Using various DatabaseClient beans is useful when connecting to multiple databases of the same type. When using different database systems that differ in their dialect, use @EnableR2dbcRepositories(entityOperationsRef = …)` instead.spring-doc.cn