Spring Data Neo4j RX — Beta III.

On the Road to GA

Gerrit Meier
Neo4j Developer Blog
5 min readFeb 3, 2020

--

Please note that Spring Data Neo4j RX replaces Spring Data Neo4j in the future. More information can be found here.

We are happy to announce that the third beta release of Spring Data Neo4j RX (SDN/RX) has just arrived on Maven central. While it has been pretty silent around the features we put into SDN/RX lately, we want to give you an overview of what’s in there, and what should follow to make it GA-ready for us.

What’s new in the latest versions

In our first introduction to SDN/RX, we have already presented the first(limited) mapping capabilities, like modelling nodes and their direct relationships. But today (and some 86 commits later), SDN/RX has evolved into a grown-up framework to help you work with your graph.

Projection support

Sometimes it is more convenient to load a view-like presentation of your data based on your already defined domain. With projections, you can get the data in the representation you need for your use-case.

Assuming that we have a Person entity defined as:

@Node
public class Person {

@Id @GeneratedValue private Long id;
private String firstName;
private String lastName;

@Relationship("LIVES_AT")
private Address address;

@Node
static class Address {
@Id @GeneratedValue private Long id;
private String zipCode;
private String city;
private String street;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}
}

and you want to return only specific fields of the entity, you could either define an interface like so:

public interface PersonSummary {

String getFirstName();

AddressSummary getAddress();

interface AddressSummary {
String getCity();
}
}

which also shows how to use nested projections. It is also possible to use the Spring Expression Language in your projections:

public interface NamesOnly {

String getFirstName();
String getLastName();

@Value("#{target.firstName + ' ' + target.lastName}")
String getFullName();
}

In situations where it is better to work with a class instead of an interface, you could define something similar to:

public class NamesOnlyDto {

private final String firstName;
private final String lastName;

public NamesOnlyDto(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}
}

and add your presentation model logic there.

Dynamic relationships

With dynamic relationships, you get more freedom in an ever-changing domain. It is as easy as:

Map<String, TargetEntity> dynamicRelationships;

to define them. The key is the relationship type, and the value points to the entity you want to link to. This feature can be used for reading and writing.

Relationship properties mapping

Another feature that targets the relationships is the mapping of properties on relationships. You define such a relationship in an entity by providing the target entity, and a class that holds the properties within a Map:

@Relationship("LIKES")
private Map<Hobby, LikesHobbyRelationship> hobbies;

The properties class needs to get annotated with @RelationshipProperties to show its purpose. The class itself looks like a simple POJO:

@RelationshipProperties
public class LikesHobbyRelationship {

private final LocalDate since;

private Boolean active;
public LikesHobbyRelationship(LocalDate since) {
this.since = since;
}
}

As you can see, it is possible to harness all the supported types that you can also use in entities. Of course, it is also possible to keep those classes immutable, if desired.

New abstraction layer: Neo4jTemplate

We have introduced this layer between the repositories and the Neo4jClient, and based all operations in the repository layer on the templates. This gives us not only a clean separation of concerns, but also enables you to work with an abstraction that is not bounded to a repository. You are also still aware of how to load and persist your domain.

Kotlin support

Besides the general support for Kotlin (data) classes and repositories, SDN/RX has got an extra treatment for reified types.

Using the Neo4jClient or its reactive counterpart allows you now to write more fluent code. For example:

neo4jClient
.query("MATCH (n:User) RETURN n.name")
.fetchAs<String>().one()

or:

neo4jClient
.query("MATCH (u:User{name:'Michael') RETURN u")
.mappedBy {_, u ->
val
name = u["name"].asString()
User(name)
}
.one

Additionally, we introduced coroutines support when working with the ReactiveNeo4jClient.

Database selection

Of course, you have heard about Neo4j 4.0’s multi-database feature, and you want to use it in SDN/RX. You can either define a static database name in your configuration (if you are using Spring Boot in the application.properties), or define a DatabaseSelectionProvider bean that can dynamically switch between the databases.

You can find our example here in the SDN/RX repository, and a more detailed blog post written by Michael Simons to give you more information on this topic.

Testing with DataNeo4jTest

The test annotation @DataNeo4jTest brings in additional autoconfiguration for testing purposes. It will start an embedded server to run your tests if the Neo4j test harness is on the class path, and there is no other driver bean already in place.

Also, it recognizes existing ServerControls or Neo4j (part of the Neo4j test harness) beans, in case you wanted to provide a test fixture, or you have configured the Enterprise Edition. The autoconfiguration then steps back from creating that bean and configures only a driver instance connected to your harness.

What else

You can’t get enough of the features?

  • Conversion support for all Neo4j Java Driver types and more
  • Improved immutable support: Related entities will get created first
  • Derived relationship names from property names for missing defined relationship types
  • The (still not stable(!), but available as a public API) Cypher DSL has got more features
  • Bug fixing and improvement (thanks to YOU for reporting issues and feature requests ♥️).

The ecosystem around Spring Data Neo4j RX

We are currently working closely together with the JHipster team to not only integrate SDN/RX there and make Neo4j accessible through this project, but also to give you support for creating entities and their relationships, based on what we think the best practices are.

Thanks to the growing community

Missing features implemented or typos fixed, we appreciate all of your contributions to the project. In no specific order: Vivek Singh, Philipp Tölle, Ján Šúr, Dmitrii Abramov, Harpreet Singh and Frederik Hahne.

But we are also thankful for your non-code contributions like reporting issues, asking for features, and bringing your ideas to us.

The future is bright

Now is the time for us to think about our GA release and what needed features are missing. Looking at the current state of open issues, there are some minor additions left, but also some missing features like optimistic locking and support for multiple labels that we really want to provide you in a 1.0 version.

If you have any questions about Spring Data Neo4j RX, visit our community site. The best channel for your ideas or bugs (yes, please report them) is the project’s GitHub repository.

Thanks to Michael Simons, Michael Hunger and Ljubica Lazarevic for providing feedback.

--

--