@@ -140,7 +140,9 @@ This is our canonical movie example with the imperative template:
140
140
[[imperative-template-example]]
141
141
.TemplateExampleTest.java
142
142
----
143
- include::../../../../src/test/java/org/springframework/data/neo4j/documentation/spring_boot/TemplateExampleTest.java[tags=faq.template-imperative]
143
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/spring_boot/TemplateExampleTest.java[tags=faq.template-imperative-pt1]
144
+ @DataNeo4jTest
145
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/spring_boot/TemplateExampleTest.java[tags=faq.template-imperative-pt2]
144
146
----
145
147
146
148
And here is the reactive version, omitting the setup for brevity:
@@ -149,9 +151,13 @@ And here is the reactive version, omitting the setup for brevity:
149
151
[[reactive-template-example]]
150
152
.ReactiveTemplateExampleTest.java
151
153
----
152
- include::../../../../src/test/java/org/springframework/data/neo4j/documentation/spring_boot/ReactiveTemplateExampleTest.java[tags=faq.template-reactive]
154
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/spring_boot/ReactiveTemplateExampleTest.java[tags=faq.template-reactive-pt1]
155
+ @DataNeo4jTest
156
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/spring_boot/ReactiveTemplateExampleTest.java[tags=faq.template-reactive-pt2]
153
157
----
154
158
159
+ Please note that both examples use `@DataNeo4jTest` from Spring Boot.
160
+
155
161
[[faq.custom-queries-with-page-and-slice]]
156
162
== How do I use custom queries with repository methods returning `Page<T>` or `Slice<T>`?
157
163
@@ -201,6 +207,178 @@ public interface MyPersonRepository extends Neo4jRepository<Person, Long> {
201
207
Therefore you must specify an additional count query.
202
208
All other restrictions from the second method apply.
203
209
210
+ [[faq.custom-queries-and-custom-mappings]]
211
+ == Is `@Query` the only way to use custom queries?
212
+
213
+ No, `@Query` is *not* the only way to run custom queries.
214
+ The annotation is comfortable in situations in which your custom query fills your domain completely.
215
+ Please remember that SDN 6 assumes your mapped domain model to be the truth.
216
+ That means if you use a custom query via `@Query` that only fills a model partially, you are in danger of using the same
217
+ object to write the data back which will eventually erase or overwrite data you didn't consider in your query.
218
+
219
+ So, please use repositories and declarative methods with `@Query` in all cases where the result is shaped like your domain
220
+ model or you are sure you don't use a partially mapped model for write commands.
221
+
222
+ What are the alternatives? First, please read up on two things: <<repositories.custom-implementations,custom repository fragments>>
223
+ the <<sdn-building-blocks,levels of abstractions>> we offer in SDN 6.
224
+
225
+ Why speaking about custom repository fragments?
226
+
227
+ * You might want to use the Cypher-DSL for building type-safe queries
228
+ * You might have more complex situation in which a dynamic query is required, but the query still belongs
229
+ conceptually in a repository and not in the service layer
230
+ * Your custom query returns a graph shaped result that fits not quite to your domain model
231
+ and therefore the custom query should be accomponied by a custom mapping as well
232
+ * You have the need for interacting with the driver, i.e. for bulk loads that should not go through object mapping.
233
+
234
+ Assume the following repository _declaration_ that basically aggregates one base repository plus 3 fragments:
235
+
236
+ [source,java,indent=0,tabsize=4]
237
+ [[aggregating-repository]]
238
+ .A repository composed from several fragments
239
+ ----
240
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/repositories/custom_queries/MovieRepository.java[tags=aggregating-interface]
241
+ ----
242
+
243
+ The repository contains <<movie-entity, Movies>> as shown in <<example-node-spring-boot-project,the getting started section>>.
244
+
245
+ The additional interface from which the repository extends (`DomainResults`, `NonDomainResults` and `LowlevelInteractions`)
246
+ are the fragments that addresses all the concerncs above.
247
+
248
+ === Using complex, dynamic custom queries and but still returning domain types
249
+
250
+ The fragment `DomainResults` declares one additional method `findMoviesAlongShortestPath`:
251
+
252
+ [source,java,indent=0,tabsize=4]
253
+ [[domain-results]]
254
+ .DomainResults fragment
255
+ ----
256
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/repositories/custom_queries/MovieRepository.java[tags=domain-results]
257
+ ----
258
+
259
+ This method is annotated with `@Transactional(readOnly = true)` to indicate that readers can answer it.
260
+ It cannot be derived by SDN but would need a custom query.
261
+ This custom query is provided by the one implementation of that interface.
262
+ The implementation has the same name with the suffix `Impl`:
263
+
264
+ [source,java,indent=0,tabsize=4]
265
+ [[domain-results-impl]]
266
+ .A fragment implementation using the Neo4jTemplate
267
+ ----
268
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/repositories/custom_queries/MovieRepository.java[tags=domain-results-impl]
269
+ ----
270
+ <.> The `Neo4jTemplate` is injected by the runtime through the constructor of `DomainResultsImpl`. No need for `@Autowired`.
271
+ <.> The Cypher-DSL is used to build a complex statement (pretty much the same as shown in <<faq.path-mapping,path mapping>>.)
272
+ The statement can be passed directly to the template.
273
+
274
+ The template has overloads for String-based queries as well, so you could write down the query as String as well.
275
+ The important takeaway here is:
276
+
277
+ * The template "knows" your domain objects and maps them accordingly
278
+ * `@Query` is not the only option to define custom queries
279
+ * The can be generated in various ways
280
+ * The `@Transactional` annotation is respected
281
+
282
+ === Using custom queries and custom mappings
283
+
284
+ Often times a custom query indicates custom results.
285
+ Should all of those results be mapepd as `@Node`? Of course not! Many times those objects represents read commands
286
+ and are not meant to be used as write commands.
287
+ It is also not unlikely that SDN 6 cannot or want not map everything that is possible with Cypher.
288
+ It does however offer several hooks to run your own mapping: On the `Neo4jClient`.
289
+ The benefit of using the SDN 6 `Neo4jClient` over the driver:
290
+
291
+ * The `Neo4jClient` is integrated with Springs transaction management
292
+ * It has a fluent API for binding parameters
293
+ * It has a fluent API exposing both the records and the Neo4j typesystem so that you can access
294
+ everything in your result to execute the mapping
295
+
296
+ Declaring the fragment is exactly the same as before:
297
+
298
+ [source,java,indent=0,tabsize=4]
299
+ [[non-domain-results]]
300
+ .A fragment declaring non-domain-type results
301
+ ----
302
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/repositories/custom_queries/MovieRepository.java[tags=non-domain-results]
303
+ ----
304
+ <.> This is a made up non-domain result. A real world query result would probably look more complex.
305
+ <.> The method this fragment adds. Again, the method is annotated with Spring's `@Transactional`
306
+
307
+ Without an implementation for that fragment, startup would fail, so here it is:
308
+
309
+ [source,java,indent=0,tabsize=4]
310
+ [[non-domain-results-impl]]
311
+ .A fragment implementation using the Neo4jClient
312
+ ----
313
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/repositories/custom_queries/MovieRepository.java[tags=non-domain-results-impl]
314
+ ----
315
+ <.> Here we use the `Neo4jClient`, as provided by the infrastructure.
316
+ <.> The client takes only in Strings, but the Cypher-DSL can still be used when rendering into a String
317
+ <.> Bind one single value to a named parameter. There's also an overload to bind a whole map of parameters
318
+ <.> This is the type of the result you want
319
+ <.> And finally, the `mappedBy` method, exposing one `Record` for each entry in the result plus the drivers typesystem if needed.
320
+ This is the API in which you hook in for your custom mappings
321
+
322
+ The whole query runs in the context of a Spring transaction, in this case, a read-only one.
323
+
324
+ ==== Low level interactions
325
+
326
+ Sometimes you might want to do bulk loadings from a repository or delete whole subgraphs or interact in very specific ways
327
+ with the Neo4j Java-Driver. This is possible as well. The following example shows how:
328
+
329
+ [source,java,indent=0,tabsize=4]
330
+ [[low-level-interactions]]
331
+ .Fragments using the plain driver
332
+ ----
333
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/repositories/custom_queries/MovieRepository.java[tags=lowlevel-interactions]
334
+ ----
335
+ <.> Work with the driver directly. As with all the examples: There is no need for `@Autowired` magic. All the fragments
336
+ are actually testable on their own.
337
+ <.> The usecase is made up. Here we use a driver managed transaction deleting the whole graph and return the number of
338
+ deleted nodes and relationships
339
+
340
+ This interaction does of course not run in a Spring transaction, as the driver does not know about Spring.
341
+
342
+ Putting it all together, this test succeeds:
343
+
344
+ [source,java,indent=0,tabsize=4]
345
+ [[custom-queries-test]]
346
+ .Testing the composed repository
347
+ ----
348
+ include::../../../../src/test/java/org/springframework/data/neo4j/documentation/repositories/custom_queries/CustomQueriesIT.java[tags=custom-queries-test]
349
+ ----
350
+
351
+ As a final word: All three interfaces and implementations are picked up by Spring Data Neo4j automatically.
352
+ There is no need for further configuration.
353
+ Also, the same overall repository could have been created with only one additional fragment (the interface defining all three methods)
354
+ and one implementation. The implementation would than have had all three abstractions injected (template, client and driver).
355
+
356
+ All of this applies of course to reactive repositories as well.
357
+ They would work with the `ReactiveNeo4jTemplate` and `ReactiveNeo4jClient` and the reactive session provided by the driver.
358
+
359
+ If you have recuring methods for all repositories, you could swap out the default repository implementation.
360
+
361
+ [[faq.custom-base-repositories]]
362
+ == How do I use custom Spring Data Neo4j base repositories?
363
+
364
+ Basically the same ways as the shared Spring Data Commons documentation shows for Spring Data JPA in <<repositories.customize-base-repository>>.
365
+ Only that in our case you would extend from
366
+
367
+ [source,java,indent=0,tabsize=4]
368
+ [[custom-base-repository]]
369
+ .Custom base repository
370
+ ----
371
+ include::../../../../src/test/java/org/springframework/data/neo4j/integration/imperative/CustomBaseRepositoryIT.java[tags=custom-base-repository]
372
+ ----
373
+ <.> This signature is required by the base class. Take the `Neo4jOperations` (the actual specification of the `Neo4jTemplate`)
374
+ and the entity information and store them on an attribute if needed.
375
+
376
+ In this example we forbide the use of the `findAll` method.
377
+ You could add methods taking in a fetch depth and run custom queries based on that depth.
378
+ One way to do this is shown in <<domain-results>>.
379
+
380
+ To enable this base repository for all declared repesotiries enable Neo4j repositories with: `@EnableNeo4jRepositories(repositoryBaseClass = MyRepositoryImpl.class)`.
381
+
204
382
[[faq.spel.custom-query]]
205
383
== How do I use Spring Expression Language in custom queries?
206
384
0 commit comments