Spring Data 2020.0 - New and Noteworthy in Spring Data R2DBC 1.2

Engineering | Mark Paluch | November 18, 2020 | ...

This release is a major milestone in Spring's R2DBC support. Spring support for R2DBC started as an experimental project two years ago to evaluate how a reactive SQL integration might look. With this release, Spring Data R2DBC underwent a major refactoring by moving several components into a dedicated spring-r2dbc module and deprecating components that are provided by the Spring Framework. Besides that change, Spring Data R2DBC ships with the following most notable changes:

  • Add lifecycle callbacks through the EntityCallback API
  • Reactive auditing
  • Pass-through of enum values and Postgres Geo types

Spring R2DBC

Spring Data R2DBC is now based on spring-r2dbc, a new module in Spring Framework that ships with core R2DBC support, such as DatabaseClient, ConnectionFactoryInitializer, and R2dbcTransactionManager. Those components were deprecated in favor of Spring R2DBC components and marked for removal with the next major Spring Data R2DBC release. See the migration guide to learn about deprecations and what replacement API to use.

Lifecycle EntityCallback API

Spring Data Moore introduced the EntityCallback API to pre-process and post-process entities upon load, save, and delete operations. Entity callbacks are invoked during persistence operations and allow in-place modifications to entities and returning new object instances if an entity class is immutable.

EntityCallback interfaces are reactive-enabled by returning Publisher<T> where T represents the entity type. Returning a publisher allows consuming the subscriber context (as is done for reactive auditing) or integrating with other reactive components that perform I/O before saving an entity or after loading one.

You can use EntityCallback for all sorts of callback hooks. The following example defines a BeforeConvertCallback to generate a primary key value from a H2 sequence for the Customer type if its identifier is null:

BeforeConvertCallback<Customer> idGeneratingCallback(DatabaseClient databaseClient) {  
   return (customer, sqlIdentifier) -> {  
      if (customer.getId() == null) {  
         return databaseClient.sql("SELECT primary_key.nextval") //  
  .map(row -> row.get(0, Long.class)) //  
  .first() //  
      return Mono.just(customer);  

You can read more about R2DBC EntityCallbacks in the R2DBC reference documentation.

Pass-thru of enum Values and Postgres Geo Types

Postgres supports various data types, of which enumerations and Geometric types can be used directly from Spring Data R2DBC. By default, Spring Data converts enum values to String by using the enum's name() method to represent the enumeration value. R2DBC Postgres supports (since version 0.8.4) a specific codec that accepts Java enum types and maps these onto the corresponding Postgres enum type.

Consider the following Java enum:

enum Color {
	Blue, Green, White

Enumeration types in Postgres need to be created before using enum types:

CREATE TYPE color_enum as enum ('Blue', 'Green', `White`);

CREATE TABLE product (id SERIAL PRIMARY KEY, color color_enum);

Once the enum type is in place, you need to configure the driver to associate the Java enum with the corresponding Postgres enum type. This is done by using the R2DBC Postgres EnumCodec builder . The resulting CodecRegistrar must be registered with the connection configuration before creating a ConnectionFactory:

CodecRegistrar codecRegistrar = EnumCodec.builder().withEnum("color_enum", Color.class).build();

PostgresqlConnectionConfiguration configuration = PostgresqlConnectionConfiguration.builder()
    // additional configuration

See also the driver documentation on Postgres Enum Types for further reference.

Once the ConnectionFactory is configured, you can configure specific enum types in Spring Data to be passed-thru to the driver with their actual value instead of converting the enum value to String. R2dbcCustomConversions is the entry point to use when configuring simple types and their converters:

R2dbcCustomConversions conversions = R2dbcCustomConversions.of(PostgresDialect.INSTANCE, new ColorConverter());

Now you can use the enumeration type in your domain class:

class Product {
  @Id Integer id;
  Color color;
  // getter, setter, constructors omitted for brevity

See the reference documentation on Overriding Enum Mapping with Explicit Converters.

In contrast to enums, using Postgres Geometric types requires no special setup since Geo types have fixed type identifiers and map to individual Geo types. Spring Data R2DBC supports the use of the following driver-specific types in your entities:

  • io.r2dbc.postgresql.codec.Box
  • io.r2dbc.postgresql.codec.Circle
  • io.r2dbc.postgresql.codec.Line
  • io.r2dbc.postgresql.codec.Lseg
  • io.r2dbc.postgresql.codec.Point
  • io.r2dbc.postgresql.codec.Path
  • io.r2dbc.postgresql.codec.Polygon

Alternatively, you can use Spring Data's Geo Types in your domain model:

  • org.springframework.data.geo.Box
  • org.springframework.data.geo.Circle
  • org.springframework.data.geo.Point
  • org.springframework.data.geo.Polygon

To use Geometric types, create a table and a corresponding entity type:

CREATE TABLE venue (id SERIAL PRIMARY KEY, name VARCHAR(255), location POINT);
class Venue {
  @Id Long id;
  String name;
  Point location;
  // getter, setter, constructors omitted for brevity

Finally, you can use the entity in your application:

R2dbcEntityTemplate template = …;

template.insert(new Venue(null, "Seattle Space Needle", Point.of(47.6204,-122.3491));

Get the Spring newsletter

Thank you!

Get ahead

VMware offers training and certification to turbo-charge your progress.

Learn more

Get support

Spring Runtime offers support and binaries for OpenJDK™, Spring, and Apache Tomcat® in one simple subscription.

Learn more

Upcoming events

Check out all the upcoming events in the Spring community.

View all