|
| 1 | +[[embedded-bolt-connection]] |
| 2 | += Using an embedded database with the Driver starter |
| 3 | + |
| 4 | +This example demonstrates a couple things: |
| 5 | + |
| 6 | +1. How to provide an instance of the Neo4j `DatabaseManagementService` as a managed bean into a Spring context. |
| 7 | +1. How to keep the configuration for the driver and the embedded instance in sync. |
| 8 | +1. This starter can connect against an embedded instance with the Bolt connector enabled. |
| 9 | + |
| 10 | +While the <<testing-with-neo4j-harness,test harness example>> focuses on having an embedded instance through the harness in |
| 11 | +your context, this example focuses on having the embedded API for whatever reasons in your context. |
| 12 | + |
| 13 | +One of those reasons might be that you want to be able to use the `GraphDatabaseService` |
| 14 | +to execute Cypher without Bolt involved or traverse nodes manually. |
| 15 | +Here is one made up example that uses the `GraphDatabaseService` to find all nodes with a label `Movie`: |
| 16 | + |
| 17 | +[[GraphDatabaseService-usecase]] |
| 18 | +[source,java,indent=0] |
| 19 | +.Accessing the `GraphDatabaseService` from a service |
| 20 | +---- |
| 21 | +try(var tx = databaseManagementService.database("neo4j").beginTx()) { |
| 22 | + return tx.findNodes(Label.label("Movie")) |
| 23 | + .stream() |
| 24 | + .map(n -> n.getProperty("title")) |
| 25 | + .map(String.class::cast) |
| 26 | + .collect(Collectors.toList()); |
| 27 | +} |
| 28 | +---- |
| 29 | + |
| 30 | +The `MovieService` in the sources of this example however is exactly the same as the one in the other examples. |
| 31 | +It is not aware against what kind of server it is connected and just gets a Driver `Session` with `var session = driver.session()`. |
| 32 | + |
| 33 | +== Provide a managed, embedded `DatabaseManagementService` and keep it in sync with the driver. |
| 34 | + |
| 35 | +First, provide the necessary dependencies: |
| 36 | + |
| 37 | +[source,xml,subs="verbatim,attributes"] |
| 38 | +---- |
| 39 | +<dependencies> |
| 40 | + <!-- Embbedded instance --> |
| 41 | + <dependency> |
| 42 | + <groupId>org.neo4j</groupId> |
| 43 | + <artifactId>neo4j</artifactId> |
| 44 | + <version>{neo4j_version}</version> |
| 45 | + </dependency> |
| 46 | + <!-- Necessary bolt server --> |
| 47 | + <dependency> |
| 48 | + <groupId>org.neo4j</groupId> |
| 49 | + <artifactId>neo4j-bolt</artifactId> |
| 50 | + <version>{neo4j_version}</version> |
| 51 | + </dependency> |
| 52 | +</dependencies> |
| 53 | +---- |
| 54 | + |
| 55 | +Depending on your setup, you might have to exclude the following artifact from both dependencies above: `org.slf4j:slf4j-nop`, |
| 56 | +as it clashes with Spring Boots default logger. |
| 57 | + |
| 58 | +The enterprise edition lives under different coordinates |
| 59 | +(`com.neo4j:neo4j-enterprise`, which are not available in Maven central but only to customers). |
| 60 | + |
| 61 | +Configuration is done with the following components: |
| 62 | + |
| 63 | +[[EmbeddedConfig-Properties]] |
| 64 | +[source,java,indent=0] |
| 65 | +.DatabaseManagementServiceProperties.java |
| 66 | +---- |
| 67 | +include::src/main/java/org/neo4j/doc/driver/springframework/boot/embedded/DatabaseManagementServiceProperties.java[tags=EmbeddedConfig-Properties] |
| 68 | +---- |
| 69 | +<.> Make this class a component containing configurational properties under the prefix `org.neo4j.database-management-service`. |
| 70 | + This prefix is the canonical form. Camel- and Snakecases (`databaseManagementService` respectivly `DATABASE_MANAGEMENT_SERVICE` |
| 71 | + work as well. |
| 72 | + These properties can come from `application.properties` or `application.yml` as well as from the environment and of course, |
| 73 | + config servers. |
| 74 | +<.> This configuration is needed for the embedded instance. The embedded instance needs to store its data somewhere. |
| 75 | + |
| 76 | +With a properties class like this, you can have all the relevant configuration properties in one place, instead of |
| 77 | +manually juggling with `@Value` and related. |
| 78 | + |
| 79 | + |
| 80 | +[[EmbeddedConfig]] |
| 81 | +[source,java,indent=0] |
| 82 | +.Configuring the embedded instance with the driver in mind |
| 83 | +---- |
| 84 | +include::src/main/java/org/neo4j/doc/driver/springframework/boot/embedded/Neo4jConfig.java[tags=EmbeddedConfig] |
| 85 | +---- |
| 86 | +<.> Mark this class as a configurational bean. |
| 87 | + We can make the startup of the Spring context a bit fast, as we don't need to proxy the methods. |
| 88 | +<.> Define the class that represents the configuration properties. |
| 89 | +<.> This marks the returned value as Spring bean for the context |
| 90 | +<.> We only fire up the embedded instance in some conditions. |
| 91 | + This is optional, but makes perfect sense in a setup where both the embedded instance and the driver should be available. |
| 92 | +<.> We check if someone had configured the home directory for the instance. |
| 93 | + If so, we use that, otherwise we default to something random. |
| 94 | +<.> Here, we use the same port that has been given to the driver for the port the embedded bolt listens to. |
| 95 | + Choose any other port you want (or a random free one), but keep in mind, |
| 96 | + that you have to reconfigure the driver as well then (through `org.neo4j.driver.uri`). |
| 97 | +<.> This is a `Condition` to be used with `@Conditional`. |
| 98 | + It makes sure in 4. that we bring up an embedded instance only when the driver actually targets something on localhost via the bolt protocol. |
| 99 | + This later step is completely optional, but keeps you from funny surprise. |
| 100 | + |
| 101 | +== Using the driver against the embedded instance |
| 102 | + |
| 103 | +To the outside world, nothing indicates that the service runs against embedded: |
| 104 | + |
| 105 | +[[MovieService]] |
| 106 | +[source,java,indent=0] |
| 107 | +.Accessing the `GraphDatabaseService` from a service |
| 108 | +---- |
| 109 | +include::src/main/java/org/neo4j/doc/driver/springframework/boot/embedded/MoviesService.java[tags=usage] |
| 110 | +---- |
| 111 | + |
| 112 | +You can of course inject the `DatabaseManagementService` or - if you provide those as beans as well - dedicated |
| 113 | +`GraphDatabaseService`-instances for the databases you want to work with. |
| 114 | + |
| 115 | +Let's walk through a test setup. |
| 116 | +The tests prepares the data directly against the embedded instance and than tests the service: |
| 117 | + |
| 118 | +[source,java,indent=0] |
| 119 | +.MoviesServiceTest.java |
| 120 | +---- |
| 121 | +include::src/test/java/org/neo4j/doc/driver/springframework/boot/embedded/MoviesServiceTest.java[tags=test] |
| 122 | +---- |
| 123 | +<.> Make it a standard, full blown `@SpringBootTest`, applying the same configuration found from walking the packages |
| 124 | + starting at the `@SpringBootApplication`. |
| 125 | +<.> This brings in dynamic test properties in the `registry`. |
| 126 | + It is available since Spring Boot 2.2.6. |
| 127 | +<.> We use `SocketUtils` to find a random free port. |
| 128 | + We give that random free port to the driver URI which is in turn used by <<EmbeddedConfig, the embedded config>> to configure the published Bolt port. |
| 129 | + This is necessary to not clash with any running databases on the test machine. |
| 130 | +<.> Uncommenting the next line gives you the opportunity to point the embedded instance to a directory containing pre-seeded |
| 131 | + data during test for example. |
| 132 | +<.> Springs JUnit 5 integration allows beans to be injected in all phases of a JUnit 5 test. |
| 133 | + Here we use the `@BeforeAll` phase to access the embedded database to create test data. |
| 134 | +<.> We want to test the service shown in <<MovieService,MovieService.java>>, which works with the driver. |
0 commit comments