{{announcement.body}}
{{announcement.title}}

You Don’t Need Hibernate With Spring WebFlux and R2DBC

DZone 's Guide to

You Don’t Need Hibernate With Spring WebFlux and R2DBC

We focus on fetching composed entities from PostgreSQL and mapping them with Data Mapper pattern with a Spring ReactiveCrudRepository extension.

· Java Zone ·
Free Resource

One of issues when you work with relational databases in Java is that they are unable to translate object relationships (such as composition), due to the tabular nature of data sources. That means that as developers, we usually tend to have an intermediate layer, which is responsible to abstract the data source’s data organization. This is called ORM or object-relational mapping. In Spring's ecosystem the de facto standard is Hibernate, however it is not [yet] available for new non-blocking the Spring R2DBC API.

Frankly speaking, you don’t need Hibernate to do object mapping, as R2DBC is easy to use and you can do it yourself. And moreover it is a great pleasure, especially because we – Java devs – tend to rely on tools that are not state of the art of software design. 

This post focuses on a problem of fetching composed entities from Postgresql (using INNER JOIN) and mapping it with Data Mapper pattern (as defined in P of EAA) with custom Spring ReactiveCrudRepository extension.

Problem

In the object-oriented programming, we work with objects that possess rich relationships between each other, based on aggregation, association, or composition. Imagine that you work on a task management app, where each task entity holds a reference to project entity. Or, another case – job app, where job entity points the corresponding employer object.

However, in relational databases, despite their name, data is organized in a tabular way. That means we need to have an intermediate layer between database and business logic. Such a mechanism is called object-relational mapping (or ORM), and it allows to access data entities in a way independent from how they are stored in data sources. In Java de facto choice is Hibernate, and as Spring developers we use it a lot with relational DBs, such as MySQL, Postgre, etc.

But, when you use non-blocking Webflux APIs and R2DBC to work with relational databases, you don’t have an ORM because Hibernate is not supported by R2DBC. Yet, maybe. And I think it is good – as Java developers we love to talk about software design, but we depend on tools, that are not best examples of software architectural principles. This post is about dealing with composition in entities without Hibernate in Spring R2DBC and PostgreSQL.

Solution

Consider the following example: we build a task management application, and we have Task and Project entities. Both are stored in separate tables, but are referenced using project_id key. When we want to retrieve a list of tasks, we want to have information about corresponding projects, so we could show it in client apps. Yet, we can have two separate repos and then combine data it is highly inefficient idea. The better approach is to write a custom TaskRepository that returns composed objects.

Step 1. Define a Custom Repository

By default, ReactiveCrudRepository is an entry point, that offers basic CRUD functionality, but it is limited to that. In order to provide custom queries, we need to extend it with a custom repository. For that, we first create a new interface that defines a contract for custom repository, and then extend the entry repository with it:

Java


The next step is to extend core TaskRepository with CustomTaskRepository, as shown below:

Java


Step 2. Implement the Contract

Now, we can implement the aforementioned interface. Note, that implementations should have Impl endings due to Spring DI rules. In that component, we need to inject a DatabaseClient that is non-blocking client to handle database operations. In Spring Boot, things are pre-configured, so you just need to define a dependency and use constructor-based DI to make Spring inject it:

Java


Step 3. Prepare SQL Queries

Fetching composed entities means that we need to use JOIN operations. PostgreSQL has 6 types of join operations, but this is out of scope of this post. Maybe in the future, I will add PostgreSQL as a topic of my blog, but not now. Here, we use the INNER JOIN operation that returns rows that match the given condition in both tables.

In our example, we have Task and Project entities connected with project_id. Take a look at the code snippet below:

Java


Step 4. Bind Params and Execute Query

Likewise, to do it in plain JDBC, we first prepare a query and then execute it. In R2DBC, we use DatabaseClient.execute() method for this. We also may bind some variables, like userId. This is done using the bind() method, which accepts two arguments: key (a name of variable in query) and a value.

Java


Step 5. Use Mapper to Work With Results

This is cumberstone, as that is a reason why we use ORM frameworks. We need to map raw results to Java objects. For that (as well to promote reusability), we create a mapper. This design pattern, defined by Martin Fowler is used to move data between objects and a database while keeping them independent of each other and the mapper itself. The idea is displayed below:

Mapper implementation

In R2DC, reactive client mapping operation is performed by the map() method. It accepts a normal BiFunction that maps raw row results to corresponding Java model. We can implement it using a functional interface like this:

Java


Next, we can add this component to our custom repository:

Java


Step 6. Consume data

The final step is to call a terminal operation to consume the data pipeline. DatabaseClient has three operations to work with queries:

  • all() = returns all rows of the result.
  • first() = returns the first row of the entire result.
  • one() = returns exactly one result and fails if the result contains more rows.

In our example, we need all entities that satisfy the query, so we use the all() method, as shown below:

Java


As you can see, this is not a rocket science to use R2DBC with complex composed objects without a need of ORM framework, like Hibernate. R2DBC provides a fluent API and is easy to use and to abstract database operations, so you can implement required persistence-layer logic yourself.

If you have questions regarding this post, don’t hesitate to drop a comment below or contact me. Have a nice day!

References

  • Mark Paluch Reactive programming with SQL databases (2019) Jaxenter access here.
  • Martin Fowler P of EAA: Data Mapper access here.
  • Piotr Mińkowski Introduction to reactive APIS with Postgres, R2DBC, Spring Data JDBC and Spring Webflux (2018) acces here.
Topics:
java, microservices, postgresql, project reactor, r2dbc, spring 5, webflux

Published at DZone with permission of Yuri Mednikov . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}