@@ -140,7 +140,9 @@ This is our canonical movie example with the imperative template:
140140[[imperative-template-example]]
141141.TemplateExampleTest.java
142142----
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]
144146----
145147
146148And 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:
149151[[reactive-template-example]]
150152.ReactiveTemplateExampleTest.java
151153----
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]
153157----
154158
159+ Please note that both examples use `@DataNeo4jTest` from Spring Boot.
160+
155161[[faq.custom-queries-with-page-and-slice]]
156162== How do I use custom queries with repository methods returning `Page<T>` or `Slice<T>`?
157163
@@ -201,6 +207,178 @@ public interface MyPersonRepository extends Neo4jRepository<Person, Long> {
201207 Therefore you must specify an additional count query.
202208 All other restrictions from the second method apply.
203209
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+
204382[[faq.spel.custom-query]]
205383== How do I use Spring Expression Language in custom queries?
206384
0 commit comments