diff --git a/etc/jqassistant/index.adoc b/etc/jqassistant/index.adoc index d66d080d8..a42b41481 100644 --- a/etc/jqassistant/index.adoc +++ b/etc/jqassistant/index.adoc @@ -1,3 +1,4 @@ +[[coding-rules]] = Coding Rules The following rules are checked during a build: @@ -8,16 +9,19 @@ The following rules are checked during a build: - <> - <> +[[coding-rules.api]] == API Ensure that we publish our API in a sane and consistent way. include::api.adoc[] +[[coding-rules.naming]] == Naming things include::naming.adoc[] +[[coding-rules.structure]] == Structuring things include::structure.adoc[] diff --git a/src/main/asciidoc/appendix/build.adoc b/src/main/asciidoc/appendix/build.adoc index 2219e825b..cf5b13a74 100644 --- a/src/main/asciidoc/appendix/build.adoc +++ b/src/main/asciidoc/appendix/build.adoc @@ -1,6 +1,7 @@ [[building-SDN]] = Building Spring Data Neo4j +[[building-SDN.requirements]] == Requirements * JDK 8+ (Can be https://openjdk.java.net[OpenJDK] or https://www.oracle.com/technetwork/java/index.html[Oracle JDK]) @@ -9,6 +10,7 @@ ** running locally ** or indirectly via https://www.testcontainers.org[Testcontainers] and https://www.docker.com[Docker] +[[building-SDN.jdk.version]] === About the JDK version Choosing JDK 8 is a decision influenced by various aspects @@ -18,6 +20,7 @@ Spring Data commons baseline is still JDK 8 and so is Spring Framework's baselin Thus, it is only natural to keep the JDK 8 baseline. * While there is an increase of projects started with JDK 11 (which is Oracle's current LTS release of Java), many existing projects are still on JDK 8. We don't want to lose them as users right from the start. +[[building-SDN.running-the-build]] == Running the build The following sections are alternatives and roughly sorted by increased effort. @@ -44,8 +47,10 @@ Java(TM) SE Runtime Environment (build 12.0.1+12) Java HotSpot(TM) 64-Bit Server VM (build 12.0.1+12, mixed mode, sharing) ---- +[[building-SDN.docker]] === With Docker installed +[[building-SDN.docker.default-image]] ==== Using the default image If you don't have https://en.wikipedia.org/wiki/Docker_(software)[Docker] installed, head over to https://www.docker.com/products/docker-desktop[Docker Desktop]. @@ -71,6 +76,7 @@ $ mvnw.cmd clean verify The output should be similar. +[[building-SDN.docker.another-image]] ==== Using another image The image version to use can be configured through an environmental variable like this: @@ -86,6 +92,7 @@ Here we are using 3.5.11 enterprise and also accept the license agreement. Consult your operating system or shell manual on how to define environment variables if specifying them inline does not work for you. +[[building-SDN.local-database]] === Against a locally running database WARNING: Running against a locally running database *will* erase its complete content. @@ -107,6 +114,7 @@ After that, you can run a complete build by specifying the local `bolt` URL: $ SDN_NEO4J_URL=bolt://localhost:7687 SDN_NEO4J_PASSWORD=secret ./mvnw clean verify ---- +[[building-SDN.environment-variables]] == Summary of environment variables controlling the build [cols="3,1,3",options="header"] @@ -136,6 +144,7 @@ We use this a lot during development. NOTE: You need to set both `SDN_NEO4J_URL` and `SDN_NEO4J_PASSWORD` to use a local instance. +[[building-SDN.checkstyle-and-co]] == Checkstyle and friends There is no quality gate in place at the moment to ensure that the code/test ratio stays as is, but please consider adding tests to your contributions. @@ -143,6 +152,7 @@ There is no quality gate in place at the moment to ensure that the code/test rat We have some rather mild checkstyle rules in place, enforcing more or less default Java formatting rules. Your build will break on formatting errors or something like unused imports. +[[building-SDN.jqassistant]] === jQAssistant We also use https://jqassistant.org[jQAssistant], a Neo4j-based tool, to verify some aspects of our architecture. @@ -150,6 +160,7 @@ The rules are described with Cypher and your build will break when they are viol include::../../../../etc/jqassistant/index.adoc[leveloffset=4] +[[building-SDN.jqassistant.database]] ==== Accessing the jQAssistant database jQAssistant uses Neo4j to store information about a project. diff --git a/src/main/asciidoc/appendix/conversions.adoc b/src/main/asciidoc/appendix/conversions.adoc index 9202893a8..cafdd5355 100644 --- a/src/main/asciidoc/appendix/conversions.adoc +++ b/src/main/asciidoc/appendix/conversions.adoc @@ -175,8 +175,10 @@ If you require the time zone, use a type that supports it (i.e. `ZoneDateTime`) |=== +[[custom.conversions]] == Custom conversions +[[custom.conversions.attribute.types]] === For attributes of a given type If you prefer to work with your own types in the entities or as parameters for `@Query` annotated methods, you can define and provide a custom converter implementation. @@ -202,6 +204,7 @@ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/ If you need multiple converters in your application, you can add as many as you need in the `Neo4jConversions` constructor. +[[custom.conversions.attribute.specific]] === For specific attributes only If you need conversions only for some specific attributes, we provide `@ConvertWith`. @@ -211,7 +214,7 @@ With an implementation of `Neo4jPersistentPropertyConverter` all specific conver We provide `@DateLong` and `@DateString` as meta-annotated annotations for backward compatibility with Neo4j-OGM schemes not using native types. -[[composite-properties]] +[[custom.conversions.composite-properties]] ==== Composite properties With `@CompositeProperty`, attributes of type `Map` or `Map` can be stored as composite properties. @@ -219,4 +222,4 @@ All entries inside the map will be added as properties to the node or relationsh Either with a configured prefix or prefixed with the name of the property. While we only offer that feature for maps out of the box, you can `Neo4jPersistentPropertyToMapConverter` and configure it as the converter to use on `@CompositeProperty`. A `Neo4jPersistentPropertyToMapConverter` needs to know how a given type can -be decomposed to and composed back from a map. \ No newline at end of file +be decomposed to and composed back from a map. diff --git a/src/main/asciidoc/appendix/migrating.adoc b/src/main/asciidoc/appendix/migrating.adoc index 98a9c5f5d..c5bf17500 100644 --- a/src/main/asciidoc/appendix/migrating.adoc +++ b/src/main/asciidoc/appendix/migrating.adoc @@ -1,6 +1,7 @@ [[Migrating]] = Migrating from SDN+OGM to SDN +[[migrating.known-issues]] == Known issues with past SDN+OGM migrations SDN+OGM has had quite a history over the years and we understand that migrating big application systems is neither fun nor something that provides immediate profit. @@ -29,12 +30,14 @@ TIP: Backed by those observations, we recommend to make sure you're using only t Thus, your application and the access layer of your application is to large extend independent from the databases version. From that state, consider moving from SDN+OGM to SDN. +[[migrating.preparation]] == Prepare the migration from SDN+OGM Lovelace or SDN+OGM Moore to SDN NOTE: The _Lovelace_ release train corresponds to SDN 5.1.x and OGM 3.1.x, while the _Moore_ is SDN 5.2.x and OGM 3.2.x. First, you must make sure that your application runs against Neo4j in server mode over the Bolt protocol, which means work in two of three cases: +[[migrating.embedded]] === You're on embedded You have added `org.neo4j:neo4j-ogm-embedded-driver` and `org.neo4j:neo4j` to you project and starting the database via OGM facilities. @@ -46,16 +49,19 @@ Migrating from the embedded solution is probably the toughest migration, as you It is however the one that gives you much value in itself: In the future, you will be able to upgrade the database itself without having to consider your application framework, and your data access framework as well. +[[migrating.http]] === You're using the HTTP transport You have added `org.neo4j:neo4j-ogm-http-driver` and configured an url like `http://user:password@localhost:7474`. The dependency has to be replaced with `org.neo4j:neo4j-ogm-bolt-driver` and you need to configure a Bolt url like `bolt://localhost:7687` or use the new `neo4j://` scheme, which takes care of routing, too. +[[migrating.bolt]] === You're already using Bolt indirectly A default SDN+OGM project uses `org.neo4j:neo4j-ogm-bolt-driver` and thus indirectly, the pure Java Driver. You can keep your existing URL. +[[migrating.migrating]] == Migrating Once you have made sure, that your SDN+OGM application works over Bolt as expected, you can start migrating to SDN. @@ -123,6 +129,7 @@ You're then ready to replace annotations: NOTE: Several Neo4j-OGM annotations have not yet a corresponding annotation in SDN, some will never have. We will add to the list above as we support additional features. +[[migrating.bookmarks]] === Bookmarkmanagement Both `@EnableBookmarkManagement` and `@UseBookmark` as well as the `org.springframework.data.neo4j.bookmark.BookmarkManager` diff --git a/src/main/asciidoc/appendix/neo4j-client.adoc b/src/main/asciidoc/appendix/neo4j-client.adoc index e6e930711..4f395089f 100644 --- a/src/main/asciidoc/appendix/neo4j-client.adoc +++ b/src/main/asciidoc/appendix/neo4j-client.adoc @@ -26,6 +26,7 @@ The Neo4j Client comes in two flavors: While both versions provide an API using the same vocabulary and syntax, they are not API compatible. Both versions feature the same, fluent API to specify queries, bind parameters and extract results. +[[neo4j-client.imperative.reactive]] == Imperative or reactive? Interactions with a Neo4j Client usually ends with a call to @@ -41,6 +42,7 @@ The reactive version will in contrast return a publisher of the requested type. Interaction with the database and retrieval of the results will not happen until the publisher is subscribed to. The publisher can only be subscribed once. +[[neo4j-client.instance]] == Getting an instance of the client As with most things in SDN, both clients depend on a configured driver instance. @@ -97,6 +99,7 @@ The client won't be able to synchronize transactions if you use another instance Our Spring Boot starter provide a ready to use bean of the Neo4j Client that fits the environment (imperative or reactive) and you usually don't have to configure your own instance. +[[neo4j-client.usage]] == Usage [[neo4j-client-selecting-the-target-database]] @@ -118,13 +121,14 @@ Flux> allActors = client ---- <.> Select the target database in which the query is to be executed. +[[neo4j-client.specifying.queryies]] === Specifying queries The interaction with the clients starts with a query. A query can be defined by a plain `String` or a `Supplier`. The supplier will be evaluated as late as possible and can be provided by any query builder. -[[neo4j-client-specifying-queries]] +[[neo4j-client-specifying-queries.example]] [source,java] .Specifying a query ---- @@ -134,6 +138,7 @@ Mono> firstActor = client .first(); ---- +[[neo4j-client.retrieving.results]] === Retrieving results As the previous listings shows, the interaction with the client always ends with a call to `fetch` and how many results shall be received. @@ -178,6 +183,7 @@ System.out.println(counters.nodesDeleted() + " nodes have been deleted") ---- <.> Here the query is immediately triggered. +[[neo4j-client.mapping.parameters]] === Mapping parameters Queries can contain named parameters (`$someName`) and the Neo4j client makes it easy to bind values to them. @@ -284,6 +290,7 @@ Mono summary = client ---- <.> The `with` method allows for specifying the binder function. +[[neo4j-client.result-objects]] === Working with result objects Both clients return collections or publishers of maps (`Map`). @@ -310,6 +317,7 @@ Mono lily = client `TypeSystem` gives access to the types the underlying Java driver used to fill the record. +[[neo4j-client.interacting.driver.directly]] === Interacting directly with the driver while using managed transactions In case you don't want or don't like the opinionated "client" approach of the `Neo4jClient` or the `ReactiveNeo4jClient`, you can have the client delegate all interactions with the database to your code. diff --git a/src/main/asciidoc/appendix/query-creation.adoc b/src/main/asciidoc/appendix/query-creation.adoc index 58e62a768..1a9f229c4 100644 --- a/src/main/asciidoc/appendix/query-creation.adoc +++ b/src/main/asciidoc/appendix/query-creation.adoc @@ -12,6 +12,7 @@ This chapter is about the technical creation of queries when using SDN's abstraction layers. There will be some simplifications because we do not discuss every possible case but stick with the general idea behind it. +[[query-creation.save]] == Save Beside the `find/load` operations the `save` operation is one of the most used when working with data. @@ -41,6 +42,7 @@ A save operation call in general issues multiple statements against the database WARNING: As you can see SDN does its best to keep your graph model in sync with the Java world. This is one of the reasons why we really advise you to not load, manipulate and save sub-graphs as this might cause relationships to get removed from the database. +[[query-creation.save.multiple-entities]] === Multiple entities The `save` operation is overloaded with the functionality for accepting multiple entities of the same type. @@ -54,6 +56,7 @@ and the parameters look like `:params {{neo4jEntities}: [{{neo4jId}: 'aa', {neo4jProperties}: {name: "PersonName", theId: "aa"}}, {{neo4jId} 'bb', {neo4jProperties}: {name: "AnotherPersonName", theId: "bb"}}]}` +[[query-creation.load]] == Load The `load` documentation will not only show you how the _MATCH_ part of the query looks like but also how the data gets returned. diff --git a/src/main/asciidoc/faq/faq.adoc b/src/main/asciidoc/faq/faq.adoc index e463d5c93..e9e6f687f 100644 --- a/src/main/asciidoc/faq/faq.adoc +++ b/src/main/asciidoc/faq/faq.adoc @@ -11,6 +11,7 @@ Bear in mind that SDN will not create the databases for you. You can do this with the help of a https://github.com/michael-simons/neo4j-migrations[migrations tool] or of course with a simple script upfront. +[[faq.multidatabase.statically]] === Statically configured Configure the database name to use in your Spring Boot configuration like this (the same property applies of course for YML or environment based configuration, with Spring Boot's conventions applied): @@ -22,6 +23,7 @@ org.neo4j.data.database = yourDatabase With that configuration in place, all queries generated by all instances of SDN repositories (both reactive and imperative) and by the `ReactiveNeo4jTemplate` respectively `Neo4jTemplate` will be executed against the database `yourDatabase`. +[[faq.multidatabase.dynamically]] === Dynamically configured Provide a bean with the type `Neo4jDatabaseNameProvider` or `ReactiveDatabaseSelectionProvider` depending on the type of your Spring application. @@ -49,12 +51,14 @@ NOTE: Be careful that you don't mix up entities retrieved from one database with The database name is requested for each new transaction, so you might end up with less or more entities than expected when changing the database name in between calls. Or worse, you could inevitably store the wrong entities in the wrong database. +[[faq.transactions.cluster]] == Do I need specific configuration so that transactions work seamless with a Neo4j Causal Cluster? No, you don't. SDN uses Neo4j Causal Cluster bookmarks internally without any configuration on your side required. Transactions in the same thread or the same reactive stream following each other will be able to read their previously changed values as you would expect. +[[faq.annotations.specific]] == Do I need to use Neo4j specific annotations? No. @@ -80,11 +84,13 @@ You are free to use the following, equivalent Spring Data annotations: |=== +[[faq.ids.assignment]] == How do I use assigned ids? Just use `@Id` without `@GeneratedValue` and fill your id attribute via a constructor parameter or a setter or _wither_. See this https://medium.com/neo4j/neo4j-ogm-and-spring-data-neo4j-a55a866df68c[blog post] for some general remarks about finding good ids. +[[faq.ids.externally]] == How do I use externally generated ids? We provide the interface `org.springframework.data.neo4j.core.schema.IdGenerator`. @@ -146,6 +152,7 @@ And here is the reactive version, omitting the setup for brevity: include::../../../../src/test/java/org/springframework/data/neo4j/documentation/spring_boot/ReactiveTemplateExampleTest.java[tags=faq.template-reactive] ---- +[[faq.parameters.custom-query]] == How do I specify parameters in custom queries? You do this exactly the same way as in a standard Cypher query issued in the Neo4j Browser or the Cypher-Shell, with the `$` syntax (from Neo4j 4.0 on upwards, the old `{foo}` syntax for Cypher parameters has been removed from the database); @@ -164,6 +171,7 @@ The Spring Boot Maven and Gradle plugins do this automatically for you. If this is not feasible for any reason, you can either add `@Param` and specify the name explicitly or use the parameters index. +[[faq.spel.custom-query]] == How do I use Spring Expression Language in custom queries? {spring-framework-ref}/core.html#expressions[Spring Expression Language (SpEL)] can be used in custom queries inside `:#{}`. @@ -183,6 +191,7 @@ Don't confuse this with the above Cypher syntax! The SpEL expression concatenates both parameters into one single value that is eventually passed on to the <>. The SpEL block ends with `}`. +[[faq.entities.auditing]] == How do I audit entities? All Spring Data annotations are supported. @@ -193,6 +202,7 @@ Those are * `org.springframework.data.annotation.LastModifiedBy` * `org.springframework.data.annotation.LastModifiedDate` +[[faq.find-by-example]] == How do I use "Find by example"? "Find by example" is a new feature in SDN. @@ -219,6 +229,7 @@ movieExample = Example.of( movies = this.movieRepository.findAll(movieExample); ---- +[[faq.spring-boot.sdn]] == Do I need Spring Boot to use Spring Data Neo4j? No, you don't. @@ -240,6 +251,7 @@ The coordinates for a Gradle setup are the same. To select a different database - either statically or dynamically - you can add a Bean of type `DatabaseSelectionProvider` as explained in <>. For a reactive scenario, we provide `ReactiveDatabaseSelectionProvider`. +[[faq.sdn-without-spring-boot]] === Using Spring Data Neo4j inside a Spring context without Spring Boot We provide two abstract configuration classes to support you in bringing in the necessary beans: `AbstractNeo4jConfig` for imperative database access and `AbstractReactiveNeo4jConfig` for the reactive version. @@ -321,6 +333,7 @@ class MyConfiguration extends AbstractReactiveNeo4jConfig { } ---- +[[faq.sdn-in-cdi-2.0]] === Using Spring Data Neo4j in a CDI 2.0 environment For your convenience we provide a CDI extension with `Neo4jCdiExtension`. @@ -385,4 +398,4 @@ public class SomeClass { } } } ----- \ No newline at end of file +---- diff --git a/src/main/asciidoc/getting-started/getting-started.adoc b/src/main/asciidoc/getting-started/getting-started.adoc index c42026bed..059e82e97 100644 --- a/src/main/asciidoc/getting-started/getting-started.adoc +++ b/src/main/asciidoc/getting-started/getting-started.adoc @@ -15,6 +15,7 @@ SDN supports Those are all included in the same binary. The reactive programming model requires a 4.0 Neo4j server on the database side and reactive Spring on the other hand. +[[prepare-the-database]] == Prepare the database For this example, we stay within the https://neo4j.com/developer/movie-database/[movie graph], as it comes for free with every Neo4j instance. @@ -33,6 +34,7 @@ The above command sets the password of the server to `secret`. Note the command ready to run in the prompt (`:play movies`). Execute it to fill your database with some test data. +[[create-spring-boot-project]] == Create a new Spring Boot project The easiest way to setup a Spring Boot project is https://start.spring.io[start.spring.io] @@ -41,6 +43,7 @@ The easiest way to setup a Spring Boot project is https://start.spring.io[start. Select the "Spring Web Starter" to get all the dependencies needed for creating a Spring based web application. The Spring Initializr will take care of creating a valid project structure for you, with all the files and settings in place for the selected build tool. +[[create-spring-boot-project-using-maven]] === Using Maven You can issue a _curl_ request against the Spring Initializer to create a basic Maven project: @@ -71,6 +74,7 @@ As this starter is not yet on the initializer, you will have to add the followin You would also add the dependency manually in case of an existing project. +[[create-spring-boot-project-using-gradle]] === Using Gradle The idea is the same, just generate a Gradle project: @@ -99,6 +103,7 @@ dependencies { You would also add the dependency manually in case of an existing project. +[[configure-spring-boot-project]] == Configure the project Now open any of those projects in your favorite IDE. @@ -106,9 +111,9 @@ Find `application.properties` and configure your Neo4j credentials: [source,properties] ---- -org.neo4j.driver.uri=bolt://localhost:7687 -org.neo4j.driver.authentication.username=neo4j -org.neo4j.driver.authentication.password=secret +spring.neo4j.uri=bolt://localhost:7687 +spring.neo4j.authentication.username=neo4j +spring.neo4j.authentication.password=secret ---- This is the bare minimum of what you need to connect to a Neo4j instance. @@ -116,6 +121,7 @@ This is the bare minimum of what you need to connect to a Neo4j instance. NOTE: It is not necessary to add any programmatic configuration of the driver when you use this starter. SDN repositories will be automatically enabled by this starter. +[[create-domain-spring-boot-project]] == Create your domain Our domain layer should accomplish two things: @@ -123,6 +129,7 @@ Our domain layer should accomplish two things: * Map your graph to objects * Provide access to those +[[example-node-spring-boot-project]] === Example Node-Entity SDN fully supports unmodifiable entities, for both Java and `data` classes in Kotlin. @@ -200,6 +207,7 @@ It creates a new entity and sets the field accordingly, without modifying the or You can of course use SDN with https://kotlinlang.org/[Kotlin] and model your domain with Kotlin's data classes. https://projectlombok.org/[Project Lombok] is an alternative if you want or need to stay purely within Java. +[[spring-data-repositories-spring-boot-project]] === Declaring Spring Data repositories You basically have two options here: diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc index dce9b2310..03ae35e19 100644 --- a/src/main/asciidoc/index.adoc +++ b/src/main/asciidoc/index.adoc @@ -51,7 +51,7 @@ include::object-mapping/index.adoc[] include::{spring-data-commons-docs}/repository-projections.adoc[leveloffset=+1] -include::object-mapping/projections.adoc[leveloffset=+1,lines=5..] +include::object-mapping/projections.adoc[leveloffset=+1,lines=4..] include::testing/index.adoc[] diff --git a/src/main/asciidoc/introduction-and-preface/building-blocks.adoc b/src/main/asciidoc/introduction-and-preface/building-blocks.adoc index 71425542d..8026491e2 100644 --- a/src/main/asciidoc/introduction-and-preface/building-blocks.adoc +++ b/src/main/asciidoc/introduction-and-preface/building-blocks.adoc @@ -74,6 +74,7 @@ Find some more information about it in <>. The Neo4j Client as such is unique to SDN. You will find it's documentation in the <>. +[[sdn-packages]] == On the package level [%header,cols=2*] diff --git a/src/main/asciidoc/introduction-and-preface/preface.adoc b/src/main/asciidoc/introduction-and-preface/preface.adoc index 15fa18199..bdd6ed7ff 100644 --- a/src/main/asciidoc/introduction-and-preface/preface.adoc +++ b/src/main/asciidoc/introduction-and-preface/preface.adoc @@ -60,6 +60,7 @@ JVM primitives are mapped to node or relationship properties. An OGM abstracts the database and provides a convenient way to persist your domain model in the graph and query it without having to use low level drivers directly. It also provides the flexibility to the developer to supply custom queries where the queries generated by SDN are insufficient. +[[what-is-in-the-box-sdn]] === What's in the box? Spring Data Neo4j or in short SDN is a next-generation https://spring.io/projects/spring-data[Spring Data] module, created and maintained by https://neo4j.com[Neo4j, Inc.] in close collaboration with https://www.vmware.com/[VMware's] Spring Data Team. @@ -73,6 +74,7 @@ Noteworthy features that differentiate the new SDN from Spring Data Neo4j + OGM * Full support for the reactive programming model in the Spring Framework itself and Spring Data * Brand new Neo4j client and reactive client feature, resurrecting the idea of a template over the plain driver, easing database access +[[why-should-you-favor-sdn]] === Why should I use SDN in favor of SDN+OGM SDN has several features not present in SDN+OGM, notably @@ -82,6 +84,7 @@ SDN has several features not present in SDN+OGM, notably * Full support for fully immutable entities * Support for all modifiers and variations of derived finder methods, including spatial queries +[[sdn-related-to-ogm]] === How does SDN relate to Neo4j-OGM? https://neo4j.com/docs/ogm-manual/current/[Neo4j-OGM] is an Object Graph Mapping library, which is mainly used by previous versions of Spring Data Neo4j as its backend for the heavy lifting of mapping nodes and relationships into domain object. @@ -90,18 +93,22 @@ SDN uses Spring Data's mapping context exclusively for scanning classes and buil While this pins SDN to the Spring ecosystem, it has several advantages, among them the smaller footprint regarding CPU and memory usage and especially, all the features of Spring's mapping context. +[[sdn-and-http-support]] === Does SDN support connections over HTTP to Neo4j? No. +[[sdn-and-embedded-support]] === Does SDN support embedded Neo4j? Embedded Neo4j has multiple facets to it: +[[sdn-and-embedded-instances-support]] ==== Does SDN provide an embedded instance for your application? No. +[[sdn-interact-with-embedded-instances]] ==== Does SDN interact directly with an embedded instance? No. @@ -111,6 +118,7 @@ SDN can however work very much with Neo4j's test harness, the test harness is sp Support for both Neo4j 3.5 and 4.0 test harness is implemented via link:{java-driver-starter-href}[the Spring Boot starter for the driver]. Have a look at the corresponding module `org.neo4j.driver:neo4j-java-driver-test-harness-spring-boot-autoconfigure`. +[[sdn-without-spring-boot]] ==== Can I use SDN without Spring Boot? Yes, see our `README`. diff --git a/src/main/asciidoc/object-mapping/mapping.adoc b/src/main/asciidoc/object-mapping/mapping.adoc index 100afc05b..afe31ba62 100644 --- a/src/main/asciidoc/object-mapping/mapping.adoc +++ b/src/main/asciidoc/object-mapping/mapping.adoc @@ -14,9 +14,11 @@ To take full advantage of the object mapping functionality inside SDN, you shoul Although it is not necessary for the mapping framework to have this annotation (your POJOs are mapped correctly, even without any annotations), it lets the classpath scanner find and pre-process your domain objects to extract the necessary metadata. If you do not use this annotation, your application takes a slight performance hit the first time you store a domain object, because the mapping framework needs to build up its internal metadata model so that it knows about the properties of your domain object and how to persist them. +[[mapping.annotations.overview]] === Mapping Annotation Overview -==== From SDN: +[[mapping.annotations.overview.from.sdn]] +==== From SDN * `@Node`: Applied at the class level to indicate this class is a candidate for mapping to the database. * `@Id`: Applied at the field level to mark the field used for identity purpose. @@ -36,6 +38,7 @@ The following annotations are used to specify conversions and ensure backwards c See <> for more information on that. +[[mapping.annotations.overview.from.commons]] ==== From Spring Data commons * `@org.springframework.data.annotation.Id` same as `@Id` from SDN, in fact, `@Id` is annotated with Spring Data Common's Id-annotation. @@ -50,6 +53,7 @@ The initial value is zero which is bumped automatically on every update. Have a look at <> for all annotations regarding auditing support. +[[mapping.annotations.node]] === The basic building block: `@Node` The `@Node` annotation is used to mark a class as a managed domain class, subject to the classpath scanning by the mapping context. @@ -76,6 +80,7 @@ This allows polymorphic queries: You can pass in base or intermediate classes an This is only supported for abstract bases annotated with `@Node`. The labels defined on such a class will be used as additional labels together with the labels of the concrete implementations. +[[mapping.annotations.node.dynamic.labels]] ==== Dynamic or "runtime" managed labels All labels implicitly defined through the simple class name or explicitly via the `@Node` annotation are static. @@ -89,6 +94,7 @@ During writes, all labels of the node will be replaced with the statically defin WARNING: If you have other applications add additional labels to nodes, don't use `@DynamicLabels`. If `@DynamicLabels` is present on a managed entity, the resulting set of labels will be "the truth" written to the database. +[[mapping.annotations.id]] === Identifying instances: `@Id` While `@Node` creates a mapping between a class and nodes having a specific label, we also need to make the connection between individual instances of that class (objects) and instances of the node. @@ -116,6 +122,7 @@ That bean also needs to implement `IdGenerator`, but can make use of everything NOTE: Don't skip the important notes about ID handling in <> +[[mapping.annotations.property]] === Mapping properties: `@Property` All attributes of a `@Node`-annotated class will be persisted as properties of Neo4j nodes and relationships. @@ -124,6 +131,7 @@ Without further configuration, the name of the attribute in the Java or Kotlin c If you are working with an existing Neo4j schema or just like to adapt the mapping to your needs, you will need to use `@Property`. The `name` is used to specify the name of the property inside the database. +[[mapping.annotations.relationship]] === Connecting nodes: `@Relationship` The `@Relationship` annotation can be used on all attributes that are not a simple type. @@ -136,6 +144,7 @@ We support dynamic relationships. Dynamic relationships are represented as a `Map` or `Map`. In such a case, the type of the relationship to the other domain class is given by the maps key and must not be configured through the `@Relationship`. +[[mapping.annotations.relationship.properties]] ==== Map relationship properties Neo4j supports defining properties not only on nodes but also on relationships. @@ -157,6 +166,7 @@ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/domain/MovieEntity.java[tags=mapping.relationship.properties] ---- +[[mapping.annotations.relationship.remarks]] ==== Relationship query remarks In general there is no limitation of relationships / hops for creating the queries. @@ -170,6 +180,7 @@ This won't be problematical if the relationship from _movie_ to _actor_ were jus In a bidirectional scenario SDN would fetch the particular _movie_, its _actors_ but also the other movies defined for this _actor_ per definition of the relationship. In the worst case, this will cascade to fetching the whole graph for a single entity. +[[mapping.annotations.example]] === A complete example Putting all those together, we can create a simple domain. @@ -213,6 +224,7 @@ While you can do this, you may end up rebuilding a graph database inside your ob [[mapping.id-handling]] == Handling and provisioning of unique IDs +[[mapping.id-handling.internal-id]] === Using the internal Neo4j id The easiest way to give your domain classes an unique identifier is the combination of `@Id` and `@GeneratedValue` @@ -283,6 +295,7 @@ You either have to provide a setter for the id attribute or something like a _wi * Disadvantage: It is tied to Neo4js internal database id, which is not unique to our application entity only over a database lifetime. * Disadvantage: It takes more effort to create an immutable entity +[[mapping.id-handling.external-id]] === Use externally provided surrogate keys The `@GeneratedValue` annotation can take a class implementing `org.springframework.data.neo4j.core.schema.IdGenerator` as parameter. @@ -388,6 +401,7 @@ public class MovieEntity { ---- ==== +[[mapping.id-handling.business-key]] === Using a business key We have been using a business key in the complete example's `MovieEntity` and <>. diff --git a/src/main/asciidoc/object-mapping/projections.adoc b/src/main/asciidoc/object-mapping/projections.adoc index 948eb3483..1425326d4 100644 --- a/src/main/asciidoc/object-mapping/projections.adoc +++ b/src/main/asciidoc/object-mapping/projections.adoc @@ -1,7 +1,7 @@ -[[projections]] +[[projections.sdn]] = Projections -[[projections.general-remarks]] +[[projections.sdn.general-remarks]] == General remarks As stated above, projections come in two flavors: Interface and DTO based projections. @@ -31,7 +31,8 @@ additional properties - via accessors or fields - Spring Data Neo4j looks in the Properties must match exactly by name and can be of simple types (as defined in `org.springframework.data.neo4j.core.convert.Neo4jSimpleTypes`) or of known persistent entites. Collections of those are supported, but maps are not. -=== A full example +[[projections.sdn.full-example]] +== A full example Given the following entities, projections and the corresponding repository: diff --git a/src/main/asciidoc/object-mapping/sdc-object-mapping.adoc b/src/main/asciidoc/object-mapping/sdc-object-mapping.adoc index 60530d5cc..8a4a5d211 100644 --- a/src/main/asciidoc/object-mapping/sdc-object-mapping.adoc +++ b/src/main/asciidoc/object-mapping/sdc-object-mapping.adoc @@ -204,6 +204,7 @@ Even if the intent is that the calculation should be preferred, it's important t The core idea here is to use factory methods instead of additional constructors to avoid the need for constructor disambiguation through `@PersistenceConstructor`. Instead, defaulting of properties is handled within the factory method. +[[mapping.fundamentals.recommendations]] == General recommendations * _Try to stick to immutable objects_ -- @@ -226,6 +227,7 @@ As persistence operations usually require a constructor taking all arguments, th Spring Data adapts specifics of Kotlin to allow object creation and mutation. +[[mapping.fundamentals.kotlin.object-creation]] === Kotlin object creation Kotlin classes are supported to be instantiated , all classes are immutable by default and require explicit property declarations to define mutable properties. @@ -265,6 +267,7 @@ data class Person(var id: String, val name: String = "unknown") Every time the `name` parameter is either not part of the result or its value is `null`, then the `name` defaults to `unknown`. +[[mapping.fundamentals.kotlin.property-population]] === Property population of Kotlin data classes In Kotlin, all classes are immutable by default and require explicit property declarations to define mutable properties. diff --git a/src/main/asciidoc/testing/testing.adoc b/src/main/asciidoc/testing/testing.adoc index 7a0663164..4c2d22d0e 100644 --- a/src/main/asciidoc/testing/testing.adoc +++ b/src/main/asciidoc/testing/testing.adoc @@ -1,6 +1,7 @@ -[[testing]] +[[sdn.testing]] = Testing +[[sdn.testing.without.spring-boot]] == Without Spring Boot We work a lot with our abstract base classes for configuration in our own integration tests. They can be used like this: @@ -339,6 +340,7 @@ class MovieRepositoryTCTest { } ---- +[[dataneo4jtest-dynamicpropertysource-alternatives]] === Alternatives to a `@DynamicPropertySource` There are some scenarios in which the above annotation does not fit your usecase.