20

I have to re-implement a few back-end services and one of the main requirement is to make the whole flow reactive. Previously the services used hibernate with PostgreSQL so the mentioned connections were provided by the framework.

Since I have to keep the original DB and just change the service implementation I have to use r2dbc-postgresql. I couldn't find any resource about this topic, but my best guess is to do something similar what I would do with JDBC and introduce some new connection tables between my entities.

  1. Is this would be a correct approach or should I consider some different solutions?
  2. What would be the steps to achieve the mentioned connections?
gabkov
  • 342
  • 5
  • 9

2 Answers2

19

I am looking into something similar and have come to the same conclusion (1), since there is no support for relations in R2DBC. To migrate a one-to-many relation I first made the collection containing the "many" entities to @Transient in the "one" entity. Persisting the "one" entity is accomplished using the following steps in a reactive sequence:

  1. Persist the "many" entities. This in order to be able to update these in the "one" entity so that the "many" entities are assigned ids.
  2. Persist the "one" entity.
  3. Persist the relationships. I can do this at this stage since I now have the ids of all involved entities. For the relationships I have introduced a helper entity, something along the lines of OneManyRelation and a corresponding repository.

In code it looks like this:

public <S extends Drawing> Mono<S> save(final S inDrawing) {
    final List<Shape> theDrawingShapes = inDrawing.getShapes();

    return Mono.defer(() -> {
        return Flux.fromIterable(theDrawingShapes)
            .log()
            .flatMap(theDrawingShape -> {
                /* Save the shapes contained in the drawing. */
                if (theDrawingShape instanceof Circle) {
                    final Circle theUnsavedCircle = (Circle) theDrawingShape;
                    return mCircleRepository.save(theUnsavedCircle);
                } else if (theDrawingShape instanceof Rectangle) {
                    final Rectangle theUnsavedRectangle = (Rectangle) theDrawingShape;
                    return mRectangleRepository.save(theUnsavedRectangle);
                } else {
                    LOGGER.warn("Unrecognized entity type: {}",
                        theDrawingShape.getClass().getName());
                    return Mono.just(theDrawingShape);
                }
            })
            /* Update the drawing, setting the shapes of the drawing to the saved shapes. */
            .collectList()
            .map(theSavedShapesList -> {
                inDrawing.setShapes(new ArrayList<>(theSavedShapesList));
                return inDrawing;
            })
            /* Save the drawing itself. */
            .flatMap(theDrawing -> super.save(theDrawing))
            .flatMap(theDrawing -> {
                /* Save the relations between the drawing and the shapes of the drawing. */
                return Flux.fromIterable(theDrawing.getShapes())
                    .flatMap(theDrawingShape -> {
                        final var theDrawingShapeRelation = new DrawingShapesRelation();
                        theDrawingShapeRelation.setDrawingId(theDrawing.getId());
                        theDrawingShapeRelation.setShapeId(theDrawingShape.getId());
                        theDrawingShapeRelation.setShapeType(theDrawingShape.getClass()
                            .getName());
                        return mDrawingShapesRelationRepository.save(theDrawingShapeRelation);
                    })
                    .collectList()
                    .map(theDrawingShapesRelationList -> theDrawing);
            });
    });
}

My conclusion this far is that unless you are certain that there are major gains to be made from switching to R2DBC, I would settle for using Spring Data JPA and executing calls to repositories in a separate thread using subscribeOn.
Good luck and happy coding!

1

I would like to summarize the information related to reactive SQL. All the infromation provided is relevant at the time of writing this post.

  • R2DBC still does not have relations support. You can find out about the current progress here
  • There is Hibernate Reactive , which has support of relation mappings, however:
    • it is not integrated with Spring
    • specifiaclly it is not integrated with Spring Data
    • instead, Hibernate Reactive is integrated with Vert.x
    • there are two different types of asynchronous APIs available in Hibernate Reactive, one is based on Java 8 CompletionStage, another is built on Smallrye Munity project. The later fully implements the Reactive Streams specification. But unfortunately, Spring does not include built-in Smallrye Mutiny support as RxJava 2/3, etc. However, it is possible to integrate it with Spring and with Project Reactor
kerbermeister
  • 2,985
  • 3
  • 11
  • 30