Two Hip Friends — JHipster and Neo4j

Spring Data Neo4j⚡️RX available as a new core module in JHipster

Michael Simons
Neo4j Developer Blog
9 min readMay 15, 2020

--

TL;DR

For the impatient, here’s a complete, running example. You need to havenpm, curl, Docker and Maven installed:

mkdir bootiful-music && cd bootiful-music
npm install -g generator-jhipster
curl -L https://r.neo4j.com/hipster-music -o bootiful-music.jh
jhipster import-jdl bootiful-music.jh
docker-compose -f src/main/docker/neo4j.yml up -d
./mvnw

Access the application at http://localhost:8080.

Welcome screen for the bootiful music application

And now for the story behind it:

Let’s create some good relations.

Back in September 2019, the Spring Data and Object mapping team at Neo4j was asked about their opinion about an integration of our Spring Data module with JHipster.

There was an ongoing issue at that point opened by one of Neo4js long-time partners, Larus in Italy:”Provide First-Class Support to Neo4j”.

The original idea was to use the existing Spring Data Neo4j module (which we call SDN+OGM). This module was created with a lot of JPA / Hibernate inspiration and it is very flexible in some ways. It can connect to Neo4j via Neo4j’s native Bolt protocol as well as HTTP and it can also fire up an embedded Neo4j instance and use its internal Java Graph-API.

However, that flexibility comes with a price: Many moving parts, many modules and many things to combine. The OGM in SDN+OGM stands for Object Graph Mapping and it is the pendant to JPA/Hibernate when you compare SDN+OGM to Spring Data JPA.

Users need to make very educated choices to combine all those things. Not the optimal baseline to add to an application generator that itself already has tons of options to choose from.

At this point in time, Gerrit Meier and Michael Simons had been working on a successor to SDN+OGM for a couple of months already. The successor is called SDN/RX and is replacing SDN+OGM going forward.

What makes SDN/RX a good candidate for the integration with JHipster? Let’s have a look at some of the decisions we have made while creating SDN/RX as a Spring Data implementation for Neo4j from scratch.

SDN/RX

Vertical building blocks

SDN/RX should have gotten building blocks that stack nicely together, pretty much like Lego bricks do. We decided, that SDN/RX would only use the recommended way of talking to Neo4j, which is the Bolt protocol.

At the lowest abstraction level this is achieved with the Neo4j Java Driver. Its configuration is exposed into the Spring Boot infrastructure via a custom starter and the namespace org.neo4j.driver.

That configuration fits of course nicely into the things that need to be generated for JHipster.

Integration with Spring Data and Spring’s platform or reactive transaction manager starts at the level of the Neo4j Client. The client is part of SDN/RX which is configured through a separate starter, spring-data-neo4j-rx-spring-boot-starter. The configuration namespace of that starter is org.neo4j.data.

The client is mapping agnostic. It doesn’t know about your domain classes and you are responsible for mapping a result to an object suiting your needs.

The next higher level of abstraction is the Neo4j Template. It is aware of your domain and you can use it to query for arbitrary domain objects. The template comes in handy in scenarios with a large number of domain classes or custom queries for which you don’t want to create an additional repository abstraction.

The highest level of abstraction is a Spring Data Repository.

All abstractions of SDN/RX come in both imperative and reactive fashions.

SDN/RX building blocks

Utilize the Spring Data MappingContext

Spring Data Commons provide the concept of a MappingContext and SDN/RX implements it via a Neo4jMappingContext. The purpose of a mapping context is providing access to persistent entities and their properties.

By utilizing existing Spring Data components, SDN/RX is standing on the shoulders of giants: The heavy duties of class scanning, determining properties, and the associations to map from and to the database, is completely delegated to a module that has been proven to work for many stores such as MongoDB, Redis, and others.

Full support of all Spring Data features

As we built the library using our Neo4j driver and client on top of Spring Data infrastructure, SDN/RX can easily support all Spring Data features that SDN+OGM couldn’t, such as:

  • Full support for immutable entities (represented for example via Lombok @Data annotated entities, Kotlin’s data classes or even JDK 14 Records (a preview feature))
  • Support of all Spring Data keywords in derived finder methods (See “Repository query keywords”)
  • “Query by example”
  • Domain events (can used for auditing)
  • Many extension points for running custom queries with ad-hoc mapping through our Cypher-DSL

And of course, support for both imperative and reactive database access.

What could the Neo4j team provide?

From a backend developers perspective, JHipster generates the following things:

  • Basic configuration classes for initializing a Spring Data module in the Spring context
  • Domain entities as needed
  • Repositories as needed
  • Services on top of that if the users wishes for
  • Controllers using the services or the repositories directly

The fact that SDN/RX doesn’t leave out any Spring Data feature, makes it a perfect fit for providing the repository implementation. This is what Neo4j brought to the table.

Entering an a new world. The JHipster generator.

JHipster itself is not a framework per se, but an application generator creating two sides of an application:

  • The backend, using (among others)
  • Spring Boot
  • Spring Security (including Social Logins)
  • Spring MVC REST + Jackson
  • Support of various databases through Spring Data modules
  • The front end, using either AngularJS or React, styled with Bootstrap

Both the backend and the frontend are generated having both best practices, configurability and practicability in mind.

While the Spring Data Neo4j team was quite familiar with most of the things happening in the backend (Spring, Spring Boot and Spring Data), we didn’t know to much how the generator itself works. Technically JHipster is a Yeoman generator running on NPM. The templates for the artifacts generated are written with Embedded JavaScript templating (EJS).

We started to play around with this and learned about a couple of things:

  • There are core modules and blueprints in JHipster
  • There are different concepts for storing JHipsters authentication (it can work with Okta but also stores identities local)
  • The local store is independent of the entity store

Luckily, there are a lot of connections in the Java world, personally and through many Java User Groups. The Neo4j team was able to bring in Frederik Hahne.

We met at the Java Forum Nord in September 2019 to discuss SDN/RX and learn more about JHipster:

Gerrit, Frederik and Michael at Java Forum Nord 2019

We decided to evolve the existing ticket into a JHipster core module on the same level as the support for JPA, Mongo, Couchbase and ElasticSearch.

From Neo4j’s side, we provided:

  • Additions to the templates, which have mainly been additional shims for different annotations (for example @Node instead of @Table) and of course adapted imports and the like. It has been a pleasant experience to add to the EJS code.
  • We needed a way to execute database migrations on Neo4j to create initial users. For that, we created the Neo4j-Migrations, which is now part of the JHipster Neo4j support.

Based on that work, Frederik over to integrate the module further into JHipster

Additional work on the JHipster side

Michael did a great job in preparing nearly all relevant templates and prompts to support Neo4j as a database. We needed to do a lot of grunt work, like adding translations, updating generated tests or adding Neo4j to more complex and less used templates (e.g. OAuth2).

We needed some way to test the new option in our CI. We have already a lot of checks for each pull request. Therefore we execute additional tests for less frequently used options only nightly.

Our tests generate sample entities with different options and relations, so this was good test if we covered all options. Besides some edge cases, there where no severe problems revealed.

To support Neo4j, the JHipster domain language was extended with a new database options, such that Neo4j can be used with the preferred workflow.

We developed the Neo4j support while webflux/reactive support was still in alpha stage from JHipsters side and so we couldn’t implement fully reactive support with the first shot. During the last weeks the JHipster team did a great effort to bring reactive support out of alpha. As a result we could extend Neo4j support to all reactive templates.

As JHipster wants to make the developers life easier, Neo4j support has been added to the Herokusubgenerator. When you deploy an application to Heroku, it will automatically provisioned with a Neo4j database plugin and all required settings will be set correctly. For other deployment options (e.g. kubernetes) Neo4j support will be added in the next couple of weeks.

JDL

The JHipster domain language is a dedicated language to define JHipster applications and entities as well as complete microservice architectures. We will use it to define entities and their relations in the following.

An example application

We try to build an application similar to Michaels bootiful music example, but with JHipster and of course without writing a single line of code. :)

You need a JDK (>=8) as well as Node.js (>=12.16.3) to follow this example.

Create the application

After installing JHipster via npm install -g generator-jhipster you start the generator with the command jhipster. You can answer all questions with the default choice, except the database.

After starting Neo4j with the generated docker-compose script (docker-compose -f src/main/docker/neo4j.yml up -d) you can bring up the application with ./mvwn. This will build the frontend, the backend and start the application. The application can be accessed via localhost:8080.

Generate Entities

Instead of using the JHipster entity generator, which can be cumbersome to use, we import the following JDL to create all entities, web apis and a CRUD user interface:

jhipster import-jdl bootiful-music.jdl

Which we fetched from Michaels repository

curl -L https://r.neo4j.com/hipster-music -o bootiful-music.jh

Here is the content of the JDL file:

entity Artist {
name String required
}
entity Genre {
name String required
}
entity Track {
name String required
}
entity Album {
name String required
}
relationship OneToOne {
Album{artist(name)} to Artist
Album{genre(name)} to Genre
}
relationship OneToMany {
Album{track(name)} to Track{album(name)}
}
paginate Artist, Genre, Track, Album with pagination

If you would like to write a JDL with syntax support and graphical view of the defined relationships one can use the JDL Studio.

JDL Studio

You can restart the application with ./mvnw and create some albums with the generated user interface (login with the default credentials admin/admin or user/user).

Editing an Album entity

You can explore the generated nodes and relations with the Neo4j browser of course. Just open it at http://localhost:7474 You see the entities just created via the UI as well as the nodes created during database migration on startup.

Some of the Nodes and Relationships created by our App

Our conclusion

JHipster integrates many moving pieces, relevant to today’s enterprise applications:

  • Stable backends for applications
  • A sane way to bootstrap the frontend
  • Security out of the box, either through an integrated OAuth server, JWTs or solutions such as Okta
  • A wide choice of database backends, which just got this great addition

The new Spring Data Neo4j module SDN-RX fits perfect into this ecosystem. Backed by the Spring Data standard, a reactive Neo4jRepository works just like a reactive R2DBC repository. SDN/RX connects directly over the Bolt protocol to any Neo4j instance, regardless whether it’s running on premise, in a Docker container, in any cloud marketplace, or "as a service" in Neo4j Aura.

Having Neo4j and SDN/RX gives you support for one more great database inside a well known application generator.

So now go ahead, try it out and let us know how well the new integration worked for you. Looking forward to your comments.

Find out more about JHipster and Neo4j

Here are some entry points for further reading.

--

--

Michael Simons
Neo4j Developer Blog

👨‍👩‍👦‍👦👨🏻‍💻🚴🏻 — Father, Husband, Programmer, Cyclist. Author of @springbootbuch, founder of @euregjug. Java champion working on @springdata at @neo4j.