DATAGRAPH-1333 - Migrate SDN/RX to SDN
Replace SDN with the new library Spring Data Neo4j RX as the new Spring Data Neo4j 6.0.
This commit is contained in:
committed by
Gerrit Meier
parent
915c5278c8
commit
070d664c8a
20
Jenkinsfile
vendored
20
Jenkinsfile
vendored
@@ -24,12 +24,15 @@ pipeline {
|
||||
image 'adoptopenjdk/openjdk8:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'mkdir -p /tmp/jenkins-home'
|
||||
sh 'chown -R 1001:1001 .'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list verify -Dsort -U -B'
|
||||
sh 'chown -R 1001:1001 .'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,12 +50,15 @@ pipeline {
|
||||
image 'adoptopenjdk/openjdk11:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'mkdir -p /tmp/jenkins-home'
|
||||
sh 'chown -R 1001:1001 .'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list verify -Dsort -U -B'
|
||||
sh 'chown -R 1001:1001 .'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +68,15 @@ pipeline {
|
||||
image 'adoptopenjdk/openjdk14:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'mkdir -p /tmp/jenkins-home'
|
||||
sh 'chown -R 1001:1001 .'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list verify -Dsort -U -B'
|
||||
sh 'chown -R 1001:1001 .'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +93,7 @@ pipeline {
|
||||
docker {
|
||||
image 'adoptopenjdk/openjdk8:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
args '-v $HOME/.m2:/tmp/jenkins-home/.m2'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
@@ -94,8 +103,9 @@ pipeline {
|
||||
}
|
||||
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'mkdir -p /tmp/jenkins-home'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' +
|
||||
'-Drunning-on-ci ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
|
||||
|
||||
179
README.adoc
179
README.adoc
@@ -1,27 +1,35 @@
|
||||
= Spring Data Neo4j⚡️RX
|
||||
image:https://spring.io/badges/spring-data-neo4j/ga.svg[Spring Data Neo4j,link=https://projects.spring.io/spring-data-neo4j#quick-start] image:https://spring.io/badges/spring-data-neo4j/snapshot.svg[Spring Data Neo4j,link=https://projects.spring.io/spring-data-neo4j#quick-start]
|
||||
|
||||
= Spring Data Neo4j image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-neo4j%2Fmaster&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-neo4j/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]]
|
||||
:sectanchors:
|
||||
|
||||
// tag::properties[]
|
||||
:groupId: org.neo4j.springframework.data
|
||||
:artifactId: spring-data-neo4j-rx
|
||||
:neo4jGroupId: org.neo4j.springframework.data
|
||||
:springGroupId: org.springframework.data.neo4j
|
||||
:artifactId: spring-data-neo4j
|
||||
:artifactIdStarter: spring-data-neo4j-rx-spring-boot-starter
|
||||
|
||||
:neo4j-version: 4.0.4
|
||||
:spring-boot-version: 2.3.0.RELEASE
|
||||
:spring-data-neo4j-rx-version: 1.1.1
|
||||
:spring-data-neo4j-version: 6.0.0
|
||||
// end::properties[]
|
||||
|
||||
image:https://img.shields.io/maven-central/v/org.neo4j.springframework.data/spring-data-neo4j-rx.svg[Maven Central,link=http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.neo4j.springframework.data%22%20AND%20a%3A%22spring-data-neo4j-rx%22]
|
||||
|
||||
[abstract]
|
||||
--
|
||||
Spring Data Neo4j⚡️RX - or in short _SDN/RX_ - is an ongoing effort to create the next generation of Spring Data Neo4j, with full reactive support and lightweight mapping.
|
||||
SDN/RX will work with immutable entities, regardless whether written in Java or Kotlin.
|
||||
Spring Data Neo4j - or in short _SDN_ - is an ongoing effort to create the next generation of Spring Data Neo4j, with full reactive support and lightweight mapping.
|
||||
SDN will work with immutable entities, regardless whether written in Java or Kotlin.
|
||||
--
|
||||
|
||||
The primary goal of the https://projects.spring.io/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
|
||||
|
||||
The SDN/RX project aims to provide a familiar and consistent Spring-based programming model for integrating with the https://neo4j.com/[Neo4j] Graph Database.
|
||||
The SDN project aims to provide a familiar and consistent Spring-based programming model for integrating with the https://neo4j.com/[Neo4j] Graph Database.
|
||||
|
||||
== Code of Conduct
|
||||
|
||||
This project is governed by the link:https://github.com/spring-projects/.github/blob/master/CODE_OF_CONDUCT.md[Spring Code of Conduct].
|
||||
By participating, you are expected to uphold this code of conduct.
|
||||
Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
== Manual
|
||||
|
||||
@@ -30,6 +38,75 @@ https://neo4j.github.io/sdn-rx[Manual].
|
||||
|
||||
== Getting Started
|
||||
|
||||
=== Maven configuration
|
||||
|
||||
==== With Spring Boot
|
||||
|
||||
If you are on https://spring.io/projects/spring-boot[Spring Boot], all you have to do is to add our starter:
|
||||
|
||||
[source,xml,subs="verbatim,attributes"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>{neo4jgroupId}</groupId>
|
||||
<artifactId>{artifactIdStarter}</artifactId>
|
||||
<version>{spring-data-neo4j-rx-version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
and configure your database connection:
|
||||
|
||||
[source,properties]
|
||||
----
|
||||
org.neo4j.driver.uri=bolt://localhost:7687
|
||||
org.neo4j.driver.authentication.username=neo4j
|
||||
org.neo4j.driver.authentication.password=secret
|
||||
----
|
||||
|
||||
Please have a look at our https://neo4j.github.io/sdn-rx[manual] for an overview about the architecture, how to define
|
||||
mappings and more.
|
||||
|
||||
==== Without Spring Boot
|
||||
|
||||
If you are using a plain Spring Framework project without Spring Boot, please add this Maven dependency:
|
||||
|
||||
[source,xml,subs="verbatim,attributes"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>{springGroupId}</groupId>
|
||||
<artifactId>{artifactId}</artifactId>
|
||||
<version>{spring-data-neo4j-version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
and configure SDN for reactive database access like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableReactiveNeo4jRepositories
|
||||
@EnableTransactionManagement
|
||||
class MyConfiguration extends AbstractReactiveNeo4jConfig {
|
||||
|
||||
@Bean
|
||||
public Driver driver() {
|
||||
return GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "secret"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getMappingBasePackages() {
|
||||
return Collections.singletonList(Person.class.getPackage().getName());
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The imperative version looks pretty much the same but uses `EnableNeo4jRepositories` and `AbstractNeo4jConfig`.
|
||||
|
||||
IMPORTANT: We recommend Spring Boot, the automatic configuration and especially the dependency management
|
||||
through the Starters in contrast to the manual work of managing dependencies and configuration.
|
||||
+
|
||||
Please consult our https://neo4j.github.io/sdn-rx[manual] for more information.
|
||||
|
||||
|
||||
Here is a quick teaser of a reactive application using Spring Data Repositories in Java:
|
||||
|
||||
[source,java]
|
||||
@@ -71,77 +148,33 @@ class MyService {
|
||||
}
|
||||
----
|
||||
|
||||
TIP: SDN/RX is not only about reactive support, all features are available in both ways: Imperative and reactive, we
|
||||
only prefer to showcase the new reactive database access support here.
|
||||
=== Building SDN
|
||||
|
||||
=== Maven configuration
|
||||
Please have a look at the documentation: https://neo4j.github.io/sdn-rx/current/#building-sdn-rx[Building SDN].
|
||||
|
||||
==== With Spring Boot
|
||||
== Getting Help
|
||||
|
||||
If you are on https://spring.io/projects/spring-boot[Spring Boot], all you have to do is to add our starter:
|
||||
Having trouble with Spring Data? We’d love to help!
|
||||
|
||||
[source,xml,subs="verbatim,attributes"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>{groupId}</groupId>
|
||||
<artifactId>{artifactIdStarter}</artifactId>
|
||||
<version>{spring-data-neo4j-rx-version}</version>
|
||||
</dependency>
|
||||
----
|
||||
* Check the
|
||||
https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/[reference documentation], and https://docs.spring.io/spring-data/neo4j/docs/current/api/[Javadocs].
|
||||
* Learn the Spring basics – Spring Data builds on Spring Framework, check the https://spring.io[spring.io] web-site for a wealth of reference documentation.
|
||||
If you are just starting out with Spring, try one of the https://spring.io/guides[guides].
|
||||
* If you are upgrading, check out the https://docs.spring.io/spring-data/neo4j/docs/current/changelog.txt[changelog] for "`new and noteworthy`" features.
|
||||
* Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/questions/tagged/spring-data-neo4j[spring-data-neo4j].
|
||||
* Report bugs with Spring Data Neo4j at https://jira.spring.io/browse/DATAGRAPH[jira.spring.io/browse/DATAGRAPH].
|
||||
|
||||
and configure your database connection:
|
||||
== Reporting Issues
|
||||
|
||||
[source,properties]
|
||||
----
|
||||
org.neo4j.driver.uri=bolt://localhost:7687
|
||||
org.neo4j.driver.authentication.username=neo4j
|
||||
org.neo4j.driver.authentication.password=secret
|
||||
----
|
||||
Spring Data uses JIRA as issue tracking system to record bugs and feature requests. If you want to raise an issue, please follow the recommendations below:
|
||||
|
||||
Please have a look at our https://neo4j.github.io/sdn-rx[manual] for an overview about the architecture, how to define
|
||||
mappings and more.
|
||||
* Before you log a bug, please search the
|
||||
https://jira.spring.io/browse/DATAGRAPH[issue tracker] to see if someone has already reported the problem.
|
||||
* If the issue doesn’t already exist, https://jira.spring.io/browse/DATAGRAPH[create a new issue].
|
||||
* Please provide as much information as possible with the issue report, we like to know the version of Spring Data that you are using and JVM version.
|
||||
* If you need to paste code, or include a stack trace use JIRA `{code}…{code}` escapes before and after your text.
|
||||
* If possible try to create a test-case or project that replicates the issue. Attach a link to your code or a compressed file containing your code.
|
||||
|
||||
==== Without Spring Boot
|
||||
== License
|
||||
|
||||
If you are using a plain Spring Framework project without Spring Boot, please add this Maven dependency:
|
||||
|
||||
[source,xml,subs="verbatim,attributes"]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>{groupId}</groupId>
|
||||
<artifactId>{artifactId}</artifactId>
|
||||
<version>{spring-data-neo4j-rx-version}</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
and configure SDN/RX for reactive database access like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableReactiveNeo4jRepositories
|
||||
@EnableTransactionManagement
|
||||
class MyConfiguration extends AbstractReactiveNeo4jConfig {
|
||||
|
||||
@Bean
|
||||
public Driver driver() {
|
||||
return GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "secret"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getMappingBasePackages() {
|
||||
return Collections.singletonList(Person.class.getPackage().getName());
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The imperative version looks pretty much the same but uses `EnableNeo4jRepositories` and `AbstractNeo4jConfig`.
|
||||
|
||||
IMPORTANT: We recommend Spring Boot, the automatic configuration and especially the dependency management
|
||||
through the Starters in contrast to the manual work of managing dependencies and configuration.
|
||||
+
|
||||
Please consult our https://neo4j.github.io/sdn-rx[manual] for more information.
|
||||
|
||||
=== Building SDN/RX
|
||||
|
||||
Please have a look at the documentation: https://neo4j.github.io/sdn-rx/current/#building-sdn-rx[Building SDN/RX].
|
||||
Spring Data Neo4j is Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<module name="Checker">
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
|
||||
<module name="RegexpHeader">
|
||||
<property name="headerFile" value="${checkstyle.header.file}" />
|
||||
<property name="fileExtensions" value="java" />
|
||||
</module>
|
||||
|
||||
<module name="NewlineAtEndOfFile"/>
|
||||
|
||||
<module name="SuppressWarningsFilter" />
|
||||
|
||||
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck" />
|
||||
</module>
|
||||
@@ -1,17 +0,0 @@
|
||||
^\Q/*\E$
|
||||
^\Q * Copyright 2011-2020 the original author or authors.\E$
|
||||
^\Q *\E$
|
||||
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
|
||||
^\Q * you may not use this file except in compliance with the License.\E$
|
||||
^\Q * You may obtain a copy of the License at\E$
|
||||
^\Q *\E$
|
||||
^\Q * https://www.apache.org/licenses/LICENSE-2.0\E$
|
||||
^\Q *\E$
|
||||
^\Q * Unless required by applicable law or agreed to in writing, software\E$
|
||||
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
|
||||
^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$
|
||||
^\Q * See the License for the specific language governing permissions and\E$
|
||||
^\Q * limitations under the License.\E$
|
||||
^\Q */\E$
|
||||
^\Qpackage\E .+;$
|
||||
^.*$
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress checks="RegexpHeader" files="package-info\.java"/>
|
||||
</suppressions>
|
||||
@@ -1,14 +1,11 @@
|
||||
^\Q/*\E$
|
||||
^\Q * Copyright (c) 2019-2020 "Neo4j,"\E$
|
||||
^\Q * Neo4j Sweden AB [https://neo4j.com]\E$
|
||||
^\Q *\E$
|
||||
^\Q * This file is part of Neo4j.\E$
|
||||
^\Q * Copyright 2011-20\E\d\d\Q the original author or authors.\E$
|
||||
^\Q *\E$
|
||||
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
|
||||
^\Q * you may not use this file except in compliance with the License.\E$
|
||||
^\Q * You may obtain a copy of the License at\E$
|
||||
^\Q *\E$
|
||||
^\Q * https://www.apache.org/licenses/LICENSE-2.0\E$
|
||||
^\Q * https://www.apache.org/licenses/LICENSE-2.0\E$
|
||||
^\Q *\E$
|
||||
^\Q * Unless required by applicable law or agreed to in writing, software\E$
|
||||
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress checks="RegexpHeader" files="package-info\.java"/>
|
||||
</suppressions>
|
||||
|
||||
@@ -5,12 +5,12 @@ The following naming conventions are used throughout the project:
|
||||
|
||||
[[naming:TypeNameMustBeginWithGroupId]]
|
||||
[source,cypher,role=constraint]
|
||||
.All Java types must be located in packages that start with `org.neo4j.springframework.data`.
|
||||
.All Java types must be located in packages that start with `org.springframework.data.neo4j`.
|
||||
----
|
||||
MATCH
|
||||
(project:Maven:Project)-[:CREATES]->(:Artifact)-[:CONTAINS]->(type:Type)
|
||||
WHERE
|
||||
NOT type.fqn starts with 'org.neo4j.springframework.data'
|
||||
NOT type.fqn starts with 'org.springframework.data.neo4j'
|
||||
RETURN
|
||||
project as Project, collect(type) as TypeWithWrongName
|
||||
----
|
||||
|
||||
@@ -8,10 +8,10 @@ Most of the time, the package structure under `org.neo4j.springframework.data` s
|
||||
.The mapping package must not depend on any other SDN/RX packages than `schema` and `convert`
|
||||
----
|
||||
MATCH (a:Main:Artifact)
|
||||
OPTIONAL MATCH (a) -[:CONTAINS]-> (s:Package) WHERE s.fqn in ['org.neo4j.springframework.data.core.schema', 'org.neo4j.springframework.data.core.convert']
|
||||
OPTIONAL MATCH (a) -[:CONTAINS]-> (s:Package) WHERE s.fqn in ['org.springframework.data.neo4j.core.schema', 'org.springframework.data.neo4j.core.convert']
|
||||
WITH collect(s) as allowed, a
|
||||
MATCH (a) -[:CONTAINS]-> (p1:Package) -[:DEPENDS_ON]-> (p2:Package) <-[:CONTAINS]- (a)
|
||||
WHERE p1.fqn = 'org.neo4j.springframework.data.core.mapping'
|
||||
WHERE p1.fqn = 'org.springframework.data.neo4j.core.mapping'
|
||||
AND NOT (p2 in allowed OR (p1) -[:CONTAINS]-> (p2))
|
||||
return p1,p2
|
||||
----
|
||||
|
||||
347
pom.xml
347
pom.xml
@@ -1,10 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
| Copyright (c) 2019-2020 "Neo4j,"
|
||||
| Neo4j Sweden AB [https://neo4j.com]
|
||||
|
|
||||
| This file is part of Neo4j.
|
||||
|
|
||||
| Copyright 2011-2020 the original author or authors.
|
||||
|
|
||||
| Licensed under the Apache License, Version 2.0 (the "License");
|
||||
| you may not use this file except in compliance with the License.
|
||||
| You may obtain a copy of the License at
|
||||
@@ -17,22 +14,22 @@
|
||||
| See the License for the specific language governing permissions and
|
||||
| limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.3.1.RELEASE</version>
|
||||
<version>2.4.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.neo4j.springframework.data</groupId>
|
||||
<artifactId>spring-data-neo4j-rx-parent</artifactId>
|
||||
<version>${revision}${sha1}${changelist}</version>
|
||||
<packaging>pom</packaging>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-neo4j</artifactId>
|
||||
<version>6.0.0-SNAPSHOT</version>
|
||||
|
||||
<name>Spring Data Neo4j RX</name>
|
||||
<name>Spring Data Neo4j</name>
|
||||
<description>Next generation Object-Graph-Mapping for Spring Data.</description>
|
||||
<url>https://projects.spring.io/spring-data-neo4j</url>
|
||||
<inceptionYear>2019</inceptionYear>
|
||||
<organization>
|
||||
<name>Neo4j, Neo4j Sweden AB</name>
|
||||
@@ -52,7 +49,7 @@
|
||||
<name>Gerrit Meier</name>
|
||||
<email>gerrit.meier at neo4j.com</email>
|
||||
<organization>Neo Technology</organization>
|
||||
<organizationUrl>http://www.neotechnology.com</organizationUrl>
|
||||
<organizationUrl>https://www.neotechnology.com</organizationUrl>
|
||||
<roles>
|
||||
<role>Project Lead</role>
|
||||
</roles>
|
||||
@@ -63,7 +60,7 @@
|
||||
<name>Michael Simons</name>
|
||||
<email>michael.simons at neo4j.com</email>
|
||||
<organization>Neo Technology</organization>
|
||||
<organizationUrl>http://www.neotechnology.com</organizationUrl>
|
||||
<organizationUrl>https://www.neotechnology.com</organizationUrl>
|
||||
<roles>
|
||||
<role>Project Lead</role>
|
||||
</roles>
|
||||
@@ -71,19 +68,9 @@
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<modules>
|
||||
<module>spring-data-neo4j-rx</module>
|
||||
<module>spring-data-neo4j-rx-spring-boot-starter-parent</module>
|
||||
<module>examples/reactive-web</module>
|
||||
<module>examples/imperative-web</module>
|
||||
<module>examples/mapping</module>
|
||||
<module>examples/multi-database</module>
|
||||
<module>examples/rest</module>
|
||||
<module>examples/docs</module>
|
||||
<module>examples/kotlin</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<wurstsalat>ja</wurstsalat>
|
||||
|
||||
<apiguardian.version>1.1.0</apiguardian.version>
|
||||
<asciidoctorj-diagram.version>2.0.1</asciidoctorj-diagram.version>
|
||||
<asciidoctor-maven-plugin.version>1.6.0</asciidoctor-maven-plugin.version>
|
||||
@@ -92,13 +79,17 @@
|
||||
<changelist>-SNAPSHOT</changelist>
|
||||
<checkstyle.version>8.29</checkstyle.version>
|
||||
<cypher-dsl.version>2020.0.0</cypher-dsl.version>
|
||||
<dist.id>spring-data-neo4j</dist.id>
|
||||
<dist.key>SDNEO4J</dist.key>
|
||||
<flatten-maven-plugin.version>1.2.1</flatten-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.5</jacoco-maven-plugin.version>
|
||||
<java.version>1.8</java.version>
|
||||
<java-module-name />
|
||||
<java-module-name>spring.data.neo4j</java-module-name>
|
||||
<jaxb.version>2.3.1</jaxb.version> <!-- Align with testcontainers' version -->
|
||||
<jqassistant-dashboard-plugin.version>1.8.0</jqassistant-dashboard-plugin.version>
|
||||
<jqassistant.plugin.version>1.8.0</jqassistant.plugin.version>
|
||||
<jqassistant.version>1.8.0</jqassistant.version>
|
||||
<junit-cc-testcontainer>4.0.2.2</junit-cc-testcontainer>
|
||||
<junit-jupiter.version>5.6.1</junit-jupiter.version>
|
||||
<maven-checkstyle-plugin.version>3.1.0</maven-checkstyle-plugin.version>
|
||||
<maven-deploy-plugin.version>3.0.0-M1</maven-deploy-plugin.version>
|
||||
@@ -118,13 +109,12 @@
|
||||
<objenesis.version>3.0.1</objenesis.version> <!-- mockk requires objenesis >= 3 -->
|
||||
<project.build.docs>${project.build.directory}/docs</project.build.docs>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<r2dbc.releasetrain>Arabba-RELEASE</r2dbc.releasetrain>
|
||||
<r2dbc.releasetrain>Arabba-SR5</r2dbc.releasetrain>
|
||||
<reactive-streams.version>1.2.1</reactive-streams.version>
|
||||
<revision>1.1</revision>
|
||||
<rxjava.version>1.3.8</rxjava.version>
|
||||
<rxjava2.version>2.2.5</rxjava2.version>
|
||||
<sha1></sha1>
|
||||
<springdata-commons.version>2.3.1.RELEASE</springdata-commons.version>
|
||||
<springdata-commons.version>2.4.0-SNAPSHOT</springdata-commons.version>
|
||||
<testcontainers.version>1.13.0</testcontainers.version>
|
||||
|
||||
<skipUnitTests>${skipTests}</skipUnitTests>
|
||||
@@ -153,6 +143,12 @@
|
||||
<artifactId>objenesis</artifactId>
|
||||
<version>${objenesis.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>${jaxb.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
@@ -253,9 +249,162 @@
|
||||
<version>${testcontainers.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.michael-simons.neo4j</groupId>
|
||||
<artifactId>junit-jupiter-causal-cluster-testcontainer-extension</artifactId>
|
||||
<version>${junit-cc-testcontainer}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava-reactive-streams</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex.rxjava2</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>jta</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-r2dbc</artifactId>
|
||||
<version>1.0.0.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.r2dbc</groupId>
|
||||
<artifactId>r2dbc-h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.neo4j</groupId>
|
||||
<artifactId>neo4j-cypher-dsl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apiguardian</groupId>
|
||||
<artifactId>apiguardian-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.neo4j.driver</groupId>
|
||||
<artifactId>neo4j-java-driver</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-reactor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.mockk</groupId>
|
||||
<artifactId>mockk</artifactId>
|
||||
<version>${mockk}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>neo4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.michael-simons.neo4j</groupId>
|
||||
<artifactId>junit-jupiter-causal-cluster-testcontainer-extension</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Reduce scope of Lombok to test (it is defined by Spring Data Parent) -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok}</version>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
@@ -280,26 +429,6 @@
|
||||
<createBackupFile>false</createBackupFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>${maven-checkstyle-plugin.version}</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>${checkstyle.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<configLocation>etc/checkstyle/config.xml</configLocation>
|
||||
<suppressionsLocation>etc/checkstyle/suppressions.xml</suppressionsLocation>
|
||||
<headerLocation>etc/checkstyle/java-header.txt</headerLocation>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.buschmais.jqassistant</groupId>
|
||||
<artifactId>jqassistant-maven-plugin</artifactId>
|
||||
@@ -341,11 +470,6 @@
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>${maven-install-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
@@ -377,16 +501,8 @@
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
@@ -411,6 +527,12 @@
|
||||
<destFile>${project.build.directory}/jacoco.exec</destFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -541,6 +663,101 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.buschmais.jqassistant</groupId>
|
||||
<artifactId>jqassistant-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jqassistant-scan</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>scan</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<scanProperties>
|
||||
<jqassistant.plugin.jacoco.filename>jacoco.xml</jqassistant.plugin.jacoco.filename>
|
||||
</scanProperties>
|
||||
<scanIncludes>
|
||||
<scanInclude>
|
||||
<path>${project.basedir}/.git</path>
|
||||
</scanInclude>
|
||||
<scanInclude>
|
||||
<path>${project.reporting.outputDirectory}/jacoco</path>
|
||||
</scanInclude>
|
||||
</scanIncludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>jqassistant-analyze</id>
|
||||
<goals>
|
||||
<goal>analyze</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<failOnSeverity>MINOR</failOnSeverity>
|
||||
<groups>
|
||||
<group>default</group>
|
||||
<group>jqassistant-dashboard:Default</group>
|
||||
</groups>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-plugins-release</id>
|
||||
<url>https://repo.spring.io/plugins-release</url>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-libs-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>always-but-ci</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!running-on-ci</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<configLocation>${project.basedir}/etc/checkstyle/config.xml</configLocation>
|
||||
<suppressionsLocation>${project.basedir}/etc/checkstyle/suppressions.xml</suppressionsLocation>
|
||||
<headerLocation>${project.basedir}/etc/checkstyle/java-header.txt</headerLocation>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>true</failsOnError>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-data-neo4j-distribution</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Spring Data Neo4j - Distribution</name>
|
||||
<description>Distribution build for Spring Data Neo4j</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-neo4j-parent</artifactId>
|
||||
<version>5.4.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.root>${basedir}/..</project.root>
|
||||
<dist.id>spring-data-neo4j</dist.id>
|
||||
<dist.key>SDNEO4J</dist.key>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>wagon-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
| Copyright (c) 2019-2020 "Neo4j,"
|
||||
| Neo4j Sweden AB [https://neo4j.com]
|
||||
|
|
||||
| This file is part of Neo4j.
|
||||
|
|
||||
| Licensed under the Apache License, Version 2.0 (the "License");
|
||||
| you may not use this file except in compliance with the License.
|
||||
| You may obtain a copy of the License at
|
||||
|
|
||||
| https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
|
||||
| Unless required by applicable law or agreed to in writing, software
|
||||
| distributed under the License is distributed on an "AS IS" BASIS,
|
||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
| See the License for the specific language governing permissions and
|
||||
| limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>spring-data-neo4j-rx-parent</artifactId>
|
||||
<groupId>org.neo4j.springframework.data</groupId>
|
||||
<version>${revision}${sha1}${changelist}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-neo4j-rx</artifactId>
|
||||
|
||||
<name>SDN⚡️RX</name>
|
||||
<description>Core module of SDN.rx.</description>
|
||||
|
||||
<properties>
|
||||
<java-module-name>spring.data.neo4j.rx</java-module-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex</groupId>
|
||||
<artifactId>rxjava-reactive-streams</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.reactivex.rxjava2</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>jta</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-r2dbc</artifactId>
|
||||
<version>1.0.0.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.r2dbc</groupId>
|
||||
<artifactId>r2dbc-h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.neo4j</groupId>
|
||||
<artifactId>neo4j-cypher-dsl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apiguardian</groupId>
|
||||
<artifactId>apiguardian-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.neo4j.driver</groupId>
|
||||
<artifactId>neo4j-java-driver</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-reactor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.mockk</groupId>
|
||||
<artifactId>mockk</artifactId>
|
||||
<version>${mockk}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>neo4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.michael-simons.neo4j</groupId>
|
||||
<artifactId>junit-jupiter-causal-cluster-testcontainer-extension</artifactId>
|
||||
<version>4.0.2.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Reduce scope of Lombok to test (it is defined by Spring Data Parent) -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok}</version>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.buschmais.jqassistant</groupId>
|
||||
<artifactId>jqassistant-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jqassistant-scan</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>scan</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<scanProperties>
|
||||
<jqassistant.plugin.jacoco.filename>jacoco.xml</jqassistant.plugin.jacoco.filename>
|
||||
</scanProperties>
|
||||
<scanIncludes>
|
||||
<scanInclude>
|
||||
<path>${project.basedir}/../.git</path>
|
||||
</scanInclude>
|
||||
<scanInclude>
|
||||
<path>${project.reporting.outputDirectory}/jacoco</path>
|
||||
</scanInclude>
|
||||
</scanIncludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>jqassistant-analyze</id>
|
||||
<goals>
|
||||
<goal>analyze</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<failOnSeverity>MINOR</failOnSeverity>
|
||||
<groups>
|
||||
<group>default</group>
|
||||
<group>jqassistant-dashboard:Default</group>
|
||||
</groups>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.buschmais.jqassistant</groupId>
|
||||
<artifactId>jqassistant-maven-plugin</artifactId>
|
||||
<version>${jqassistant.version}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</project>
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.annotation.QueryAnnotation;
|
||||
|
||||
/**
|
||||
* Specifies the depth of the query. Used with derived finders.
|
||||
*
|
||||
* @author Luanne Misquitta
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.PARAMETER, ElementType.METHOD })
|
||||
@QueryAnnotation
|
||||
@Documented
|
||||
public @interface Depth {
|
||||
|
||||
int value() default 1;
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.neo4j.bookmark.BookmarkManagementConfiguration;
|
||||
|
||||
/**
|
||||
* Enables bookmark management
|
||||
* <p>
|
||||
* Bean implementing {@link org.springframework.data.neo4j.bookmark.BookmarkManager} interface needs to exist in the
|
||||
* context. Default implementation {@link org.springframework.data.neo4j.bookmark.CaffeineBookmarkManager} exists. Use
|
||||
* scope of the bean to control how bookmarks are managed. singleton - suitable for application wide bookmarks, e.g. fat
|
||||
* clients request,session - suitable for web applications
|
||||
* <p>
|
||||
* NOTE: Only usable with OGM Bolt driver.
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
* @see UseBookmark
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = ElementType.TYPE)
|
||||
@Documented
|
||||
@Import(BookmarkManagementConfiguration.class)
|
||||
public @interface EnableBookmarkManagement {
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.auditing.DateTimeProvider;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.neo4j.repository.config.Neo4jAuditingRegistrar;
|
||||
|
||||
/**
|
||||
* Annotation to enable auditing in Neo4j via annotation configuration.
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(Neo4jAuditingRegistrar.class)
|
||||
public @interface EnableNeo4jAuditing {
|
||||
|
||||
/**
|
||||
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String auditorAwareRef() default "";
|
||||
|
||||
/**
|
||||
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean setDates() default true;
|
||||
|
||||
/**
|
||||
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean modifyOnCreate() default true;
|
||||
|
||||
/**
|
||||
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
|
||||
* used for setting creation and modification dates.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String dateTimeProviderRef() default "";
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Annotation to declare a {@link Query} with a value containing an exists custom query. The result will then be used for
|
||||
* boolean projection.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@Documented
|
||||
@Query(exists = true)
|
||||
public @interface ExistsQuery {
|
||||
|
||||
/**
|
||||
* The cypher query.
|
||||
*
|
||||
* @see Query#value()
|
||||
* @since 5.2
|
||||
*/
|
||||
@AliasFor(annotation = Query.class)
|
||||
String value() default "";
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.annotation.QueryAnnotation;
|
||||
|
||||
/**
|
||||
* Annotation to declare finder queries directly on repository methods.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @author Luanne Misquitta
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
|
||||
@QueryAnnotation
|
||||
@Documented
|
||||
public @interface Query {
|
||||
|
||||
/**
|
||||
* Defines the Cypher query to be executed when the annotated method is called.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* @return simpler count-query to be executed for @{see Pageable}-support
|
||||
*/
|
||||
String countQuery() default "";
|
||||
|
||||
/**
|
||||
* Returns whether the defined query should be executed as an exists projection.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
boolean exists() default false;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to be applied to interfaces and classes onto which arbitrary Cypher query results are to be mapped.
|
||||
*
|
||||
* @author Adam George
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
public @interface QueryResult {
|
||||
|
||||
// no annotation properties needed
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a transactional operation will use bookmarks that are currently stored in BookmarkManager when
|
||||
* creating Neo4j session. Must be used on a method with @Transactional annotation. May be used on class - applies to
|
||||
* all methods or on specific methods.
|
||||
* <p>
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
* @see EnableBookmarkManagement
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Documented
|
||||
public @interface UseBookmark {
|
||||
|
||||
/**
|
||||
* If the annotated method should set bookmark
|
||||
*/
|
||||
boolean value() default true;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* A set of Neo4j specific annotations (especially Depth and Bookmark support).
|
||||
*/
|
||||
package org.springframework.data.neo4j.annotation;
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Advisor for BookmarkManagement
|
||||
* <p>
|
||||
* Used to setup BookmarkInfo for methods marked with @{@link org.springframework.data.neo4j.annotation.UseBookmark}
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class BeanFactoryBookmarkOperationAdvisor extends AbstractBeanFactoryPointcutAdvisor {
|
||||
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return new BookmarkOperationPointcut();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Bookmark info stored as thread local
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
public class BookmarkInfo implements Serializable {
|
||||
|
||||
private boolean useBookmark;
|
||||
private Collection<String> bookmarks;
|
||||
|
||||
public BookmarkInfo() {}
|
||||
|
||||
public BookmarkInfo(boolean useBookmark) {
|
||||
this.useBookmark = true;
|
||||
}
|
||||
|
||||
public boolean shouldUseBookmark() {
|
||||
return useBookmark;
|
||||
}
|
||||
|
||||
public void setUseBookmark(boolean useBookmark) {
|
||||
this.useBookmark = useBookmark;
|
||||
}
|
||||
|
||||
public Collection<String> getBookmarks() {
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
public void setBookmarks(Collection<String> bookmarks) {
|
||||
this.bookmarks = bookmarks;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
/**
|
||||
* Interceptor that sets {@link BookmarkInfo} as a ThreadLocal
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
public class BookmarkInterceptor extends BookmarkSupport implements MethodInterceptor {
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
BookmarkInfo previousValue;
|
||||
|
||||
if (bookmarkInfoHolder.get() == null) {
|
||||
previousValue = null;
|
||||
} else {
|
||||
previousValue = bookmarkInfoHolder.get();
|
||||
}
|
||||
bookmarkInfoHolder.set(new BookmarkInfo(true));
|
||||
|
||||
try {
|
||||
return invocation.proceed();
|
||||
} finally {
|
||||
bookmarkInfoHolder.set(previousValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
|
||||
/**
|
||||
* Configuration used by @{@link org.springframework.data.neo4j.annotation.EnableBookmarkManagement}
|
||||
* <p>
|
||||
* Note on bookmark management implementation: The implementation closely follows implementation
|
||||
* of @EnableTransactionManagement or @EnableCaching, with simplified pointcut.
|
||||
* <p>
|
||||
* The bookmark interceptor will set BookmarkInfo thread local when a methods is annotated with @UseBookmark. It is
|
||||
* executed before transactional advice (see setOrder(0) ). Neo4j transaction manager then uses {@link BookmarkManager}
|
||||
* bean to retrieve currently stored bookmarks and begins new transaction using these bookmarks. After commit new
|
||||
* bookmark is stored in the BookmarkManager, replacing the bookmarks used to begin the transaction. The user needs to
|
||||
* provide the BookmarkManager bean.
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
@Configuration
|
||||
public class BookmarkManagementConfiguration {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public BeanFactoryBookmarkOperationAdvisor bookmarkAdvisor() {
|
||||
BeanFactoryBookmarkOperationAdvisor advisor = new BeanFactoryBookmarkOperationAdvisor();
|
||||
advisor.setAdvice(bookmarkInterceptor());
|
||||
advisor.setOrder(0);
|
||||
return advisor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public BookmarkInterceptor bookmarkInterceptor() {
|
||||
return new BookmarkInterceptor();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Bookmark manager keeps track of Neo4j transaction bookmarks
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
* @see org.springframework.data.neo4j.annotation.EnableBookmarkManagement
|
||||
*/
|
||||
public interface BookmarkManager {
|
||||
|
||||
/**
|
||||
* Return stored bookmarks
|
||||
*
|
||||
* @return bookmarks
|
||||
*/
|
||||
Collection<String> getBookmarks();
|
||||
|
||||
/**
|
||||
* Stores bookmark to this manager
|
||||
*
|
||||
* @param bookmark new bookmark to store
|
||||
* @param previous previous bookmarks that are to be replaced by new bookmark, may be empty
|
||||
*/
|
||||
void storeBookmark(String bookmark, Collection<String> previous);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcut;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.neo4j.annotation.UseBookmark;
|
||||
|
||||
/**
|
||||
* Pointcut for methods marked with @{@link UseBookmark}
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
public class BookmarkOperationPointcut extends StaticMethodMatcherPointcut {
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass) {
|
||||
UseBookmark classAnnotation = AnnotatedElementUtils.findMergedAnnotation(targetClass, UseBookmark.class);
|
||||
UseBookmark methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, UseBookmark.class);
|
||||
|
||||
// method matches if @UseBookmark is on class, is true and not on method or
|
||||
// @UseBookmark is on method and is true
|
||||
// it is needed to cover @UseBookmark on class and @UseBookmark(false) on method
|
||||
return ((classAnnotation != null) && classAnnotation.value() && methodAnnotation == null)
|
||||
|| ((methodAnnotation != null) && methodAnnotation.value());
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
|
||||
/**
|
||||
* Helper class to access BookmarkInfo thread local
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
public class BookmarkSupport {
|
||||
|
||||
protected static final ThreadLocal<BookmarkInfo> bookmarkInfoHolder = new NamedThreadLocal<>("Bookmark info");
|
||||
|
||||
public static BookmarkInfo currentBookmarkInfo() {
|
||||
return bookmarkInfoHolder.get();
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
|
||||
/**
|
||||
* Implementation of the bookmark manager using Caffeine cache
|
||||
* <p>
|
||||
* When using this class you must ensure that caffeine dependency is on your class path.
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
public class CaffeineBookmarkManager implements BookmarkManager {
|
||||
|
||||
private final Cache<String, String> cache;
|
||||
|
||||
public CaffeineBookmarkManager() {
|
||||
cache = Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create instance of {@link CaffeineBookmarkManager} with provided cache
|
||||
*
|
||||
* @param cache cache
|
||||
*/
|
||||
public CaffeineBookmarkManager(Cache<String, String> cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getBookmarks() {
|
||||
// return defensive copy
|
||||
return new HashSet<>(cache.asMap().values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeBookmark(String bookmark, Collection<String> previous) {
|
||||
// first invalidate previous bookmarks, because bookmark may be in `previous` collection for
|
||||
// transaction, which didn't make any changes
|
||||
|
||||
cache.invalidateAll(previous);
|
||||
cache.put(bookmark, bookmark);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Support for Neo4js bookmarks for use with the java driver / bolt transport.
|
||||
*/
|
||||
package org.springframework.data.neo4j.bookmark;
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.conversion;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.neo4j.ogm.metadata.ClassInfo;
|
||||
import org.neo4j.ogm.metadata.FieldInfo;
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.typeconversion.AttributeConverter;
|
||||
import org.neo4j.ogm.typeconversion.ProxyAttributeConverter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
|
||||
/**
|
||||
* Specialisation of {@link GenericConversionService} that creates Spring-compatible converters from those known by the
|
||||
* mapping {@link MetaData}, allowing the OGM type converters to be reused throughout a Spring application.
|
||||
*
|
||||
* @author Adam George
|
||||
* @author Luanne Misquitta
|
||||
* @author Jasper Blues
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class MetaDataDrivenConversionService extends GenericConversionService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MetaDataDrivenConversionService.class);
|
||||
|
||||
/**
|
||||
* Constructs a new {@link MetaDataDrivenConversionService} based on the given {@link MetaData}.
|
||||
*
|
||||
* @param metaData The OGM {@link MetaData} from which to elicit type converters configured in the underlying
|
||||
* object-graph mapping layer
|
||||
*/
|
||||
public MetaDataDrivenConversionService(MetaData metaData) {
|
||||
|
||||
for (ClassInfo classInfo : metaData.persistentEntities()) {
|
||||
for (FieldInfo fieldInfo : classInfo.propertyFields()) {
|
||||
if (fieldInfo.hasPropertyConverter()) {
|
||||
addWrappedConverter(fieldInfo.getPropertyConverter());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void addWrappedConverter(final AttributeConverter attributeConverter) {
|
||||
|
||||
if (attributeConverter instanceof ProxyAttributeConverter) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityToGraphTypeMapping entityToGraphTypeMapping = getEntityToGraphTypeMapping(attributeConverter);
|
||||
|
||||
if (canConvert(entityToGraphTypeMapping.entityType, entityToGraphTypeMapping.graphType)
|
||||
&& canConvert(entityToGraphTypeMapping.graphType, entityToGraphTypeMapping.entityType)) {
|
||||
logger.debug("Not adding Spring-compatible converter for " + attributeConverter.getClass()
|
||||
+ " because one that does the same job has already been registered with the ConversionService.");
|
||||
} else {
|
||||
Converter toGraphConverter = attributeConverter::toGraphProperty;
|
||||
Converter toEntityConverter = attributeConverter::toEntityAttribute;
|
||||
|
||||
// It could be argued that this is wrong as it potentially overrides a registered converted that doesn't handle
|
||||
// both directions, but I've decided that it's better to ensure the same converter is used for load and save.
|
||||
addConverter(entityToGraphTypeMapping.entityType, entityToGraphTypeMapping.graphType, toGraphConverter);
|
||||
addConverter(entityToGraphTypeMapping.graphType, entityToGraphTypeMapping.entityType, toEntityConverter);
|
||||
}
|
||||
}
|
||||
|
||||
static class EntityToGraphTypeMapping {
|
||||
Class<?> entityType;
|
||||
Class<?> graphType;
|
||||
|
||||
private EntityToGraphTypeMapping(Class<?> entityType, Class<?> target) {
|
||||
this.entityType = entityType;
|
||||
this.graphType = target;
|
||||
}
|
||||
}
|
||||
|
||||
static EntityToGraphTypeMapping getEntityToGraphTypeMapping(AttributeConverter attributeConverter) {
|
||||
|
||||
ResolvableType resolvableType = ResolvableType.forClass(AttributeConverter.class,
|
||||
attributeConverter.getClass());
|
||||
|
||||
if (!resolvableType.hasGenerics()) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot resolve source and target types for the given attribute converter of class "
|
||||
+ attributeConverter.getClass());
|
||||
}
|
||||
|
||||
Class<?> sourceType = nestedTypeOrType(resolvableType.getGeneric(0));
|
||||
Class<?> targetType = nestedTypeOrType(resolvableType.getGeneric(1));
|
||||
|
||||
return new EntityToGraphTypeMapping(sourceType, targetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the type can be resolved to a collection that has generics, we extract the collection type, otherwise we return
|
||||
* the resolved type.
|
||||
*
|
||||
* @param type Type to resolve
|
||||
* @return The types resolved class or in case of a generic collections, the collections elements class.
|
||||
*/
|
||||
private static Class<?> nestedTypeOrType(ResolvableType type) {
|
||||
return Optional.ofNullable(type.asCollection()).filter(ResolvableType::hasGenerics).map(r -> r.getGeneric(0))
|
||||
.orElse(type).resolve(Object.class);
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.conversion;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.neo4j.ogm.metadata.reflect.EntityAccessManager;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implements OGM instantiation callback in order to user Spring Data Commons infrastructure for instantiation.
|
||||
*
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jOgmEntityInstantiatorAdapter implements org.neo4j.ogm.session.EntityInstantiator {
|
||||
|
||||
private final Neo4jMappingContext context;
|
||||
private ConversionService conversionService;
|
||||
|
||||
public Neo4jOgmEntityInstantiatorAdapter(MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> context,
|
||||
@Nullable ConversionService conversionService) {
|
||||
Assert.notNull(context, "MappingContext cannot be null");
|
||||
|
||||
this.context = (Neo4jMappingContext) context;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Override
|
||||
public <T> T createInstance(Class<T> clazz, Map<String, Object> propertyValues) {
|
||||
|
||||
Neo4jPersistentEntity<T> persistentEntity = (Neo4jPersistentEntity<T>) context.getRequiredPersistentEntity(clazz);
|
||||
EntityInstantiator sdnInstantiator = context.getInstantiatorFor(persistentEntity);
|
||||
|
||||
return sdnInstantiator.createInstance(persistentEntity, getParameterProvider(propertyValues, conversionService));
|
||||
}
|
||||
|
||||
private ParameterValueProvider<Neo4jPersistentProperty> getParameterProvider(Map<String, Object> propertyValues,
|
||||
ConversionService conversionService) {
|
||||
return new Neo4jPropertyValueProvider(propertyValues, conversionService);
|
||||
}
|
||||
|
||||
private static class Neo4jPropertyValueProvider implements ParameterValueProvider<Neo4jPersistentProperty> {
|
||||
|
||||
private Map<String, Object> propertyValues;
|
||||
private ConversionService conversionService;
|
||||
|
||||
Neo4jPropertyValueProvider(Map<String, Object> propertyValues, ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
Assert.notNull(propertyValues, "Properties cannot be null");
|
||||
this.propertyValues = propertyValues;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getParameterValue(PreferredConstructor.Parameter parameter) {
|
||||
|
||||
Object value = extractParameterValue(parameter);
|
||||
if (value == null || conversionService == null) {
|
||||
return value;
|
||||
} else {
|
||||
return conversionService.convert(value, parameter.getType().getType());
|
||||
}
|
||||
}
|
||||
|
||||
private Object extractParameterValue(PreferredConstructor.Parameter parameter) {
|
||||
|
||||
Object value = propertyValues.get(parameter.getName());
|
||||
|
||||
// This recreates the behaviour of
|
||||
// org.neo4j.ogm.context.SingleUseEntityMapper.writeProperty and
|
||||
// org.neo4j.ogm.context.GraphEntityMapper.writeProperty
|
||||
// As sad as it is to have in 3 places, this is the least invasive solution over both Neo4j-OGM and SDN
|
||||
if (value != null && value.getClass().isArray()) {
|
||||
value = Arrays.asList((Object[]) value);
|
||||
}
|
||||
TypeInformation typeInformation = parameter.getType();
|
||||
if (typeInformation.isCollectionLike()) {
|
||||
Class<?> collectionType = typeInformation.getType();
|
||||
Class<?> elementType = typeInformation.getComponentType().getType();
|
||||
value = collectionType.isArray()
|
||||
? EntityAccessManager.merge(collectionType, value, new Object[] {}, elementType)
|
||||
: EntityAccessManager.merge(collectionType, value, Collections.EMPTY_LIST, elementType);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.conversion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.neo4j.ogm.typeconversion.CompositeAttributeConverter;
|
||||
import org.springframework.data.geo.Point;
|
||||
|
||||
/**
|
||||
* Converts latitude and longitude properties on a node entity to an instance of Point and vice-versa.
|
||||
*
|
||||
* @see Point
|
||||
* @author Jasper Blues
|
||||
*/
|
||||
public class PointConverter implements CompositeAttributeConverter<Point> {
|
||||
|
||||
@Override
|
||||
public Map<String, ?> toGraphProperties(Point point) {
|
||||
Map<String, Double> properties = new HashMap<>();
|
||||
if (point != null) {
|
||||
properties.put("latitude", point.getX());
|
||||
properties.put("longitude", point.getY());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point toEntityAttribute(Map<String, ?> map) {
|
||||
Double latitude = (Double) map.get("latitude");
|
||||
Double longitude = (Double) map.get("longitude");
|
||||
if (latitude != null && longitude != null) {
|
||||
return new Point(latitude, longitude);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Adapter between Springs {@link org.springframework.core.convert.ConversionService conversion service infrastructure}
|
||||
* and OGM infrastructure.
|
||||
*/
|
||||
package org.springframework.data.neo4j.conversion;
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.exception;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.springframework.dao.*;
|
||||
|
||||
/**
|
||||
* Conversion table for Neo4j Errors to Spring Data Exception hierarchy.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
*/
|
||||
public class Neo4jErrorStatusCodes {
|
||||
|
||||
private static HashMap<String, Class<? extends DataAccessException>> errors;
|
||||
|
||||
static {
|
||||
errors = new HashMap<>();
|
||||
errors.put("Neo.ClientError.General.ForbiddenOnReadOnlyDatabase", PermissionDeniedDataAccessException.class);
|
||||
errors.put("Neo.ClientError.LegacyIndex.LegacyIndexNotFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Procedure.ProcedureCallFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Procedure.ProcedureNotFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Procedure.ProcedureRegistrationFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Procedure.TypeError", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Request.Invalid", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Request.InvalidFormat", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Request.TransactionRequired", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Schema.ConstraintAlreadyExists", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Schema.ConstraintNotFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Schema.ConstraintValidationFailed", DataIntegrityViolationException.class);
|
||||
errors.put("Neo.ClientError.Schema.ConstraintVerificationFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Schema.ForbiddenOnConstraintIndex", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Schema.IndexAlreadyExists", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Schema.IndexNotFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Schema.TokenNameError", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Security.AuthenticationRateLimit", PermissionDeniedDataAccessException.class);
|
||||
errors.put("Neo.ClientError.Security.CredentialsExpired", PermissionDeniedDataAccessException.class);
|
||||
errors.put("Neo.ClientError.Security.EncryptionRequired", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Security.Forbidden", PermissionDeniedDataAccessException.class);
|
||||
errors.put("Neo.ClientError.Security.Unauthorized", PermissionDeniedDataAccessException.class);
|
||||
errors.put("Neo.ClientError.Statement.ArgumentError", InvalidDataAccessApiUsageException.class);
|
||||
errors.put("Neo.ClientError.Statement.ArithmeticError", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Statement.ConstraintVerificationFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Statement.EntityNotFound", EmptyResultDataAccessException.class);
|
||||
errors.put("Neo.ClientError.Statement.ExternalResourceFailed", NonTransientDataAccessResourceException.class);
|
||||
errors.put("Neo.ClientError.Statement.LabelNotFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Statement.ParameterMissing", InvalidDataAccessApiUsageException.class);
|
||||
errors.put("Neo.ClientError.Statement.PropertyNotFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Statement.SemanticError", NonTransientDataAccessException.class);
|
||||
errors.put("Neo.ClientError.Statement.SyntaxError", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Statement.TypeError", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Transaction.ForbiddenDueToTransactionType",
|
||||
InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Transaction.TransactionAccessedConcurrently",
|
||||
InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Transaction.TransactionEventHandlerFailed",
|
||||
InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Transaction.TransactionHookFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Transaction.TransactionMarkedAsFailed", ConcurrencyFailureException.class);
|
||||
errors.put("Neo.ClientError.Transaction.TransactionNotFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Transaction.TransactionTerminated", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.ClientError.Transaction.TransactionValidationFailed",
|
||||
InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.General.IndexCorruptionDetected", DataAccessResourceFailureException.class);
|
||||
errors.put("Neo.DatabaseError.General.SchemaCorruptionDetected", DataAccessResourceFailureException.class);
|
||||
errors.put("Neo.DatabaseError.General.UnknownError", UncategorizedDataAccessException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.ConstraintCreationFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.ConstraintDropFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.IndexCreationFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.IndexDropFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.LabelAccessFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.LabelLimitReached", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.PropertyKeyAccessFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.RelationshipTypeAccessFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.SchemaRuleAccessFailed", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Schema.SchemaRuleDuplicateFound", InvalidDataAccessResourceUsageException.class);
|
||||
errors.put("Neo.DatabaseError.Statement.ExecutionFailed", TransientDataAccessResourceException.class);
|
||||
errors.put("Neo.DatabaseError.Transaction.TransactionCommitFailed", TransientDataAccessResourceException.class);
|
||||
errors.put("Neo.DatabaseError.Transaction.TransactionLogError", TransientDataAccessResourceException.class);
|
||||
errors.put("Neo.DatabaseError.Transaction.TransactionRollbackFailed", TransientDataAccessResourceException.class);
|
||||
errors.put("Neo.DatabaseError.Transaction.TransactionStartFailed", TransientDataAccessResourceException.class);
|
||||
errors.put("Neo.TransientError.General.DatabaseUnavailable", TransientDataAccessResourceException.class);
|
||||
errors.put("Neo.TransientError.General.OutOfMemoryError", NonTransientDataAccessResourceException.class);
|
||||
errors.put("Neo.TransientError.General.StackOverFlowError", NonTransientDataAccessResourceException.class);
|
||||
errors.put("Neo.TransientError.Network.CommunicationError", DataAccessResourceFailureException.class);
|
||||
errors.put("Neo.TransientError.Schema.SchemaModifiedConcurrently", ConcurrencyFailureException.class);
|
||||
errors.put("Neo.TransientError.Security.ModifiedConcurrently", ConcurrencyFailureException.class);
|
||||
errors.put("Neo.TransientError.Transaction.ConstraintsChanged", ConcurrencyFailureException.class);
|
||||
errors.put("Neo.TransientError.Transaction.DeadlockDetected", ConcurrencyFailureException.class);
|
||||
errors.put("Neo.TransientError.Transaction.InstanceStateChanged", ConcurrencyFailureException.class);
|
||||
errors.put("Neo.TransientError.Transaction.LockClientStopped", NonTransientDataAccessResourceException.class);
|
||||
errors.put("Neo.TransientError.Transaction.LockSessionExpired", DataAccessResourceFailureException.class);
|
||||
errors.put("Neo.TransientError.Transaction.Outdated", TransientDataAccessResourceException.class);
|
||||
errors.put("Neo.TransientError.Transaction.Terminated", TransientDataAccessResourceException.class);
|
||||
}
|
||||
|
||||
public static Class<? extends DataAccessException> translate(String errorCode) {
|
||||
return errors.get(errorCode);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.exception;
|
||||
|
||||
import org.springframework.dao.UncategorizedDataAccessException;
|
||||
|
||||
/**
|
||||
* Exception for unrecognised Neo4j OGM errors.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
*/
|
||||
public class UncategorizedNeo4jException extends UncategorizedDataAccessException {
|
||||
|
||||
public UncategorizedNeo4jException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Contains infrastructure for mapping Neo4j errors to Spring Data Exception hierarchy.
|
||||
*/
|
||||
package org.springframework.data.neo4j.exception;
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.mapping;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
|
||||
/**
|
||||
* The only meta-data provider currently is a Neo4j-OGM {@link org.neo4j.ogm.session.SessionFactory} by proxy. The
|
||||
* meta-data is needed during execution of graph-repository-queries to lookup mappings etc.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
* @since 5.1.2
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MetaDataProvider {
|
||||
MetaData getMetaData();
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.mapping;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.neo4j.ogm.annotation.typeconversion.Convert;
|
||||
import org.neo4j.ogm.metadata.ClassInfo;
|
||||
import org.neo4j.ogm.metadata.FieldInfo;
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.typeconversion.AttributeConverter;
|
||||
import org.neo4j.ogm.typeconversion.ConverterBasedCollectionConverter;
|
||||
import org.neo4j.ogm.types.spatial.CartesianPoint2d;
|
||||
import org.neo4j.ogm.types.spatial.GeographicPoint2d;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.context.AbstractMappingContext;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* This class implements Spring Data's MappingContext interface, scavenging the required data from the OGM's metadata in
|
||||
* order to for SDN to play nicely with Spring Data REST. The main thing to note is that this class is effectively a
|
||||
* container shim for {@code ClassInfo} objects. We don't reload all the mapping information again.
|
||||
*
|
||||
* @author Vince Bickers
|
||||
* @author Adam George
|
||||
* @author Mark Paluch
|
||||
* @author Michael J. Simons
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Neo4jMappingContext.class);
|
||||
|
||||
// Instantiation of the EntityInstantiators must happen only once, otherwise
|
||||
// the org.springframework.data.convert.ClassGeneratingEntityInstantiator will
|
||||
// be created every time an instantiator is requested and thus the whole thing
|
||||
// will dynamically create classes like there is no tomorrow.
|
||||
private final EntityInstantiators instantiators = new EntityInstantiators();
|
||||
private final MetaData metaData;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Neo4jMappingContext} based on the persistent entities in the given {@link MetaData}.
|
||||
*
|
||||
* @param metaData The OGM {@link MetaData} from which to extract the persistent entities
|
||||
*/
|
||||
public Neo4jMappingContext(MetaData metaData) {
|
||||
this.metaData = metaData;
|
||||
metaData.persistentEntities().stream().filter(k -> k.getUnderlyingClass() != null)
|
||||
.forEach(k -> addPersistentEntity(k.getUnderlyingClass()));
|
||||
|
||||
installDefaultConverter();
|
||||
|
||||
logger.info("Neo4jMappingContext initialisation completed");
|
||||
}
|
||||
|
||||
private void installDefaultConverter() {
|
||||
|
||||
Method setPropertyConverter = ReflectionUtils.findMethod(FieldInfo.class, "setPropertyConverter",
|
||||
AttributeConverter.class);
|
||||
|
||||
if (setPropertyConverter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPropertyConverter.setAccessible(true);
|
||||
|
||||
NativePointConverter nativePointConverter = new NativePointConverter();
|
||||
NativePointArrayConverter nativePointArrayConverter = new NativePointArrayConverter();
|
||||
|
||||
metaData.persistentEntities().stream().flatMap(classInfo -> Stream
|
||||
.concat(classInfo.findFields(Point.class).stream(), classInfo.findIterableFields(Point.class).stream()))
|
||||
.distinct().forEach(fieldInfo -> {
|
||||
|
||||
AttributeConverter<?, ?> propertyConverter;
|
||||
if (fieldInfo.isArray()) {
|
||||
propertyConverter = nativePointArrayConverter;
|
||||
} else if (fieldInfo.isIterable()) {
|
||||
propertyConverter = new ConverterBasedCollectionConverter(fieldInfo.getField().getType(),
|
||||
nativePointConverter);
|
||||
} else {
|
||||
propertyConverter = nativePointConverter;
|
||||
}
|
||||
|
||||
ReflectionUtils.invokeMethod(setPropertyConverter, fieldInfo, propertyConverter);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Neo4jPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
logger.debug("Creating Neo4jPersistentEntity from type information: {}", typeInformation);
|
||||
return new Neo4jPersistentEntity<>(typeInformation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Neo4jPersistentProperty createPersistentProperty(Property property, Neo4jPersistentEntity<?> owner,
|
||||
SimpleTypeHolder simpleTypeHolder) {
|
||||
|
||||
ClassInfo owningClassInfo = this.metaData.classInfo(owner.getType().getName());
|
||||
|
||||
Field propertyField = property.getField().orElse(null);
|
||||
|
||||
if (!property.isFieldBacked() && owningClassInfo != null) {
|
||||
FieldInfo fieldInfo = owningClassInfo.propertyFieldByName(property.getName());
|
||||
if (fieldInfo == null) {
|
||||
fieldInfo = owningClassInfo.relationshipFieldByName(property.getName());
|
||||
}
|
||||
if (fieldInfo != null) {
|
||||
propertyField = owningClassInfo.getField(fieldInfo);
|
||||
} else {
|
||||
// there is no field, probably because descriptor gave us a field name derived from a getter
|
||||
logger.debug("Couldn't resolve a concrete field corresponding to property {} on {} ", property.getName(),
|
||||
owningClassInfo.name());
|
||||
}
|
||||
}
|
||||
|
||||
return new Neo4jPersistentProperty(owningClassInfo, property, owner,
|
||||
updateSimpleTypeHolder(simpleTypeHolder, propertyField));
|
||||
}
|
||||
|
||||
public EntityInstantiator getInstantiatorFor(PersistentEntity<?, ?> entity) {
|
||||
return instantiators.getInstantiatorFor(entity);
|
||||
}
|
||||
|
||||
private SimpleTypeHolder updateSimpleTypeHolder(SimpleTypeHolder currentSimpleTypeHolder, Field field) {
|
||||
if (field == null) {
|
||||
return currentSimpleTypeHolder;
|
||||
}
|
||||
|
||||
final Class<?> fieldType = field.getType().isArray() ? field.getType().getComponentType() : field.getType();
|
||||
|
||||
if (shouldUpdateSimpleTypes(currentSimpleTypeHolder, field, fieldType)) {
|
||||
SimpleTypeHolder updatedSimpleTypeHolder = new SimpleTypeHolder(singleton(fieldType), currentSimpleTypeHolder);
|
||||
setSimpleTypeHolder(updatedSimpleTypeHolder);
|
||||
return updatedSimpleTypeHolder;
|
||||
}
|
||||
return currentSimpleTypeHolder;
|
||||
}
|
||||
|
||||
private boolean shouldUpdateSimpleTypes(SimpleTypeHolder currentSimpleTypeHolder, Field field,
|
||||
Class<?> rawFieldType) {
|
||||
if (field.isAnnotationPresent(Convert.class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentSimpleTypeHolder.isSimpleType(rawFieldType) || rawFieldType.isInterface()) {
|
||||
return false;
|
||||
}
|
||||
if (this.metaData.classInfo(rawFieldType.getName()) == null) {
|
||||
logger.info("No class information found in OGM meta-data for {} so treating as simple type for SD Commons",
|
||||
rawFieldType);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used internally for automatic conversion of Spring Data Point to a fitting Neo4j point. While OGM native
|
||||
* types treat longitude as x and latitude as y, we apply the same behaviour as SDN: x/y -> lat/long. We need however
|
||||
* a geographic type, as all calculations expected from Spring Data are usually based on WGS-84 If you want to be
|
||||
* explicit which coordinate system you use, please use {@link org.neo4j.ogm.types.spatial.GeographicPoint2d} or
|
||||
* {@link CartesianPoint2d} in your domain model.
|
||||
*/
|
||||
private static class NativePointConverter implements AttributeConverter<Point, GeographicPoint2d> {
|
||||
|
||||
@Override
|
||||
public GeographicPoint2d toGraphProperty(Point value) {
|
||||
|
||||
return Optional.ofNullable(value).map(p -> new GeographicPoint2d(value.getX(), value.getY())).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point toEntityAttribute(GeographicPoint2d value) {
|
||||
|
||||
return Optional.ofNullable(value).map(p -> new Point(value.getLatitude(), value.getLongitude())).orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper delegating to {@link NativePointConverter} for arrays of points.
|
||||
*/
|
||||
private static class NativePointArrayConverter implements AttributeConverter<Point[], GeographicPoint2d[]> {
|
||||
|
||||
private final NativePointConverter delegate = new NativePointConverter();
|
||||
|
||||
@Override
|
||||
public GeographicPoint2d[] toGraphProperty(Point[] value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.stream(value).map(delegate::toGraphProperty).toArray(GeographicPoint2d[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point[] toEntityAttribute(GeographicPoint2d[] value) {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.stream(value).map(delegate::toEntityAttribute).toArray(Point[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.mapping;
|
||||
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty.PropertyType;
|
||||
import org.springframework.data.support.IsNewStrategy;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* This class implements Spring Data's PersistentEntity interface, scavenging the required data from the OGM's mapping
|
||||
* classes in order to for SDN to play nicely with Spring Data REST. The main thing to note is that this class is
|
||||
* effectively a shim for ClassInfo. We don't reload all the mapping information again.
|
||||
* <p>
|
||||
* These attributes do not appear to be used/needed for SDN 4 to inter-operate correctly with SD-REST:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>typeAlias</li>
|
||||
* <li>typeInformation</li>
|
||||
* <li>preferredConstructor (we always use the default constructor)</li>
|
||||
* <li>versionProperty</li>
|
||||
* </ul>
|
||||
* Consequently their associated getter methods always return default values of null or [true|false] However, because
|
||||
* these method calls are not expected, we also log a warning message if they get invoked
|
||||
*
|
||||
* @author Vince Bickers
|
||||
* @author Adam George
|
||||
* @author Mark Paluch
|
||||
* @author Oliver Gierke
|
||||
* @author Michael J. Simons
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class Neo4jPersistentEntity<T> extends BasicPersistentEntity<T, Neo4jPersistentProperty> {
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Neo4jPersistentEntity} based on the given type information.
|
||||
*
|
||||
* @param information The {@link TypeInformation} upon which to base this persistent entity.
|
||||
*/
|
||||
Neo4jPersistentEntity(TypeInformation<T> information) {
|
||||
super(information);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.BasicPersistentEntity#getFallbackIsNewStrategy()
|
||||
*/
|
||||
@Override
|
||||
protected IsNewStrategy getFallbackIsNewStrategy() {
|
||||
return new Neo4jIsNewStrategy(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.BasicPersistentEntity#returnPropertyIfBetterIdPropertyCandidateOrNull(PersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
protected Neo4jPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(Neo4jPersistentProperty property) {
|
||||
|
||||
if (!property.isIdProperty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Neo4jPersistentProperty existingIdProperty = this.getIdProperty();
|
||||
Neo4jPersistentProperty preferredIdProperty = existingIdProperty;
|
||||
|
||||
if (existingIdProperty == null) {
|
||||
preferredIdProperty = property;
|
||||
} else if (existingIdProperty.getPropertyType() == property.getPropertyType()) {
|
||||
throw new MappingException(
|
||||
String.format("Attempt to add id property %s but already have property %s registered "
|
||||
+ "as id. Check your mapping configuration!", property.getField(),
|
||||
existingIdProperty.getField()));
|
||||
} else if (existingIdProperty.getPropertyType() == PropertyType.INTERNAL_ID_PROPERTY && property.getPropertyType() == PropertyType.ID_PROPERTY) {
|
||||
preferredIdProperty = property;
|
||||
}
|
||||
return preferredIdProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link IsNewStrategy} to also consider entities with identifiers of negative Long values new.
|
||||
* See also DATAGRAPH-1031.
|
||||
*
|
||||
* @author Frantisek Hartman
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class Neo4jIsNewStrategy implements IsNewStrategy {
|
||||
|
||||
private final Neo4jPersistentEntity<?> entity;
|
||||
|
||||
private Neo4jIsNewStrategy(Neo4jPersistentEntity<?> entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.support.IsNewStrategy#isNew(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew(Object bean) {
|
||||
|
||||
PersistentProperty<? extends PersistentProperty<?>> property = entity.getRequiredIdProperty();
|
||||
Object value = entity.getPropertyAccessor(bean).getProperty(property);
|
||||
|
||||
return value == null || (value instanceof Long && ((Long) value) < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.mapping;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.neo4j.ogm.annotation.EndNode;
|
||||
import org.neo4j.ogm.annotation.Id;
|
||||
import org.neo4j.ogm.annotation.Relationship;
|
||||
import org.neo4j.ogm.annotation.StartNode;
|
||||
import org.neo4j.ogm.annotation.Version;
|
||||
import org.neo4j.ogm.metadata.ClassInfo;
|
||||
import org.neo4j.ogm.metadata.FieldInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.neo4j.annotation.QueryResult;
|
||||
|
||||
/**
|
||||
* This class implements Spring Data's PersistentProperty interface, scavenging the required data from the OGM's mapping
|
||||
* classes in order to for SDN to play nicely with Spring Data REST. The main thing to note is that this class is
|
||||
* effectively a shim for FieldInfo. We don't reload all the mapping information again. We do not yet support
|
||||
* getter/setter access to entity properties.
|
||||
* <p>
|
||||
* These attributes do not appear to be used/needed for SDN 4 to inter-operate correctly with SD-REST:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>mapValueType</li>
|
||||
* <li>typeInformation</li>
|
||||
* <li>isVersionProperty (there is no SDN versioning at this stage)</li>
|
||||
* <li>isTransient (we never supply transient classes to the Spring mapping context)</li>
|
||||
* <li>isWritable (we don't currently support read-only fields)</li>
|
||||
* </ul>
|
||||
* Consequently their associated getter methods always return default values of null or [true|false] However, because
|
||||
* these method calls are not expected, we also log a warning message if they get invoked
|
||||
*
|
||||
* @author Vince Bickers
|
||||
* @author Adam George
|
||||
* @author Luanne Misquitta
|
||||
* @author Mark Angrish
|
||||
* @author Mark Paluch
|
||||
* @author Michael J. Simons
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class Neo4jPersistentProperty extends AnnotationBasedPersistentProperty<Neo4jPersistentProperty> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Neo4jPersistentProperty.class);
|
||||
|
||||
enum PropertyType {
|
||||
REGULAR_PROPERTY(false), INTERNAL_ID_PROPERTY(true), ID_PROPERTY(true);
|
||||
|
||||
private final boolean idProperty;
|
||||
|
||||
PropertyType(boolean idProperty) {
|
||||
this.idProperty = idProperty;
|
||||
}
|
||||
}
|
||||
|
||||
private final PropertyType propertyType;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Neo4jPersistentProperty} based on the given arguments.
|
||||
*
|
||||
* @param owningClassInfo The {@link ClassInfo} of the object of which the property field is a member
|
||||
* @param property The property
|
||||
* @param owner The owning {@link PersistentEntity} that corresponds to the given {@code ClassInfo}
|
||||
* @param simpleTypeHolder The {@link SimpleTypeHolder} that dictates whether the type of this property is considered
|
||||
* simple or not
|
||||
*/
|
||||
public Neo4jPersistentProperty(ClassInfo owningClassInfo, Property property,
|
||||
PersistentEntity<?, Neo4jPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
super(property, owner, simpleTypeHolder);
|
||||
|
||||
if (owningClassInfo == null) {
|
||||
logger.warn("Owning ClassInfo is null for property: {}", property);
|
||||
}
|
||||
|
||||
if (owningClassInfo == null || owningClassIsSimple(owningClassInfo, simpleTypeHolder)
|
||||
|| owningClassDoesNotSupportIdProperties(owningClassInfo) || owningPropertyIsEnum(owner)) {
|
||||
this.propertyType = PropertyType.REGULAR_PROPERTY;
|
||||
} else if (isInternalIdentityField(owningClassInfo, property)) {
|
||||
this.propertyType = PropertyType.INTERNAL_ID_PROPERTY;
|
||||
} else if (isExplicitIdentityField(owningClassInfo, property)) {
|
||||
this.propertyType = PropertyType.ID_PROPERTY;
|
||||
} else {
|
||||
this.propertyType = PropertyType.REGULAR_PROPERTY;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean owningPropertyIsEnum(PersistentEntity<?, Neo4jPersistentProperty> owner) {
|
||||
return owner.getType().isEnum();
|
||||
}
|
||||
|
||||
private static boolean owningClassIsSimple(ClassInfo owningClassInfo, SimpleTypeHolder simpleTypeHolder) {
|
||||
|
||||
return owningClassInfo.getUnderlyingClass() != null
|
||||
&& simpleTypeHolder.isSimpleType(owningClassInfo.getUnderlyingClass());
|
||||
}
|
||||
|
||||
private static boolean owningClassDoesNotSupportIdProperties(ClassInfo owningClassInfo) {
|
||||
|
||||
return owningClassInfo.isInterface() || owningClassInfo.annotationsInfo().get(QueryResult.class.getName()) != null
|
||||
|| owningClassInfo.isEnum();
|
||||
}
|
||||
|
||||
private static boolean isInternalIdentityField(ClassInfo owningClassInfo, Property property) {
|
||||
|
||||
Optional<Field> optionalInternalIdentityField = Optional.ofNullable(owningClassInfo.identityFieldOrNull())
|
||||
.map(FieldInfo::getField);
|
||||
return property.getField().equals(optionalInternalIdentityField);
|
||||
}
|
||||
|
||||
private static boolean isExplicitIdentityField(ClassInfo owningClassInfo, Property property) {
|
||||
|
||||
// Cannot use owningClassInfo.propertyField() as those are not initialized yet. They will
|
||||
// be initialized on the call, but that would change behaviour: SDN fails late when there
|
||||
// are invalid properties atm. Even if'ts better to fail early, it means at least changing
|
||||
// a dozen tests in SDN itself.
|
||||
return property.getField().map(field -> AnnotatedElementUtils.findMergedAnnotation(field, Id.class)).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdProperty() {
|
||||
return propertyType.idProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this property describes the internal ID property.
|
||||
*/
|
||||
public boolean isInternalIdProperty() {
|
||||
return propertyType == PropertyType.INTERNAL_ID_PROPERTY;
|
||||
}
|
||||
|
||||
PropertyType getPropertyType() {
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVersionProperty() {
|
||||
return isAnnotationPresent(Version.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to force field access as opposed to getter method access for simplicity.
|
||||
*
|
||||
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#usePropertyAccess()
|
||||
*/
|
||||
@Override
|
||||
public boolean usePropertyAccess() {
|
||||
logger.debug("[property].usePropertyAccess() returns false");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not this property should be considered an association to another entity or whether it's just
|
||||
* a simple property that should be shown as a value.
|
||||
* <p>
|
||||
* This implementation works by looking for non-transient members annotated with <code>@Relationship</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return <code>true</code> if this property is an association to another entity, <code>false</code> if not
|
||||
*/
|
||||
@Override
|
||||
public boolean isAssociation() {
|
||||
return !isTransient() && (isAnnotationPresent(Relationship.class) || isAnnotationPresent(StartNode.class)
|
||||
|| isAnnotationPresent(EndNode.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Association<Neo4jPersistentProperty> createAssociation() {
|
||||
return new Association<Neo4jPersistentProperty>(this, null);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Neo4j specific support classes for the Spring Data mapping subsystem.
|
||||
*/
|
||||
package org.springframework.data.neo4j.mapping;
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
/**
|
||||
* Neo4j OGM specific extension of {@link org.springframework.data.repository.Repository}.
|
||||
*
|
||||
* @author Vince Bickers
|
||||
* @author Mark Angrish
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface Neo4jRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
|
||||
|
||||
<S extends T> S save(S s, int depth);
|
||||
|
||||
<S extends T> Iterable<S> save(Iterable<S> entities, int depth);
|
||||
|
||||
Optional<T> findById(ID id, int depth);
|
||||
|
||||
Iterable<T> findAll();
|
||||
|
||||
Iterable<T> findAll(int depth);
|
||||
|
||||
Iterable<T> findAll(Sort sort);
|
||||
|
||||
Iterable<T> findAll(Sort sort, int depth);
|
||||
|
||||
Iterable<T> findAllById(Iterable<ID> ids);
|
||||
|
||||
Iterable<T> findAllById(Iterable<ID> ids, int depth);
|
||||
|
||||
Iterable<T> findAllById(Iterable<ID> ids, Sort sort);
|
||||
|
||||
Iterable<T> findAllById(Iterable<ID> ids, Sort sort, int depth);
|
||||
|
||||
/**
|
||||
* Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
|
||||
* {@link Page#getTotalPages()} returns an estimation of the total number of pages and should not be relied upon for
|
||||
* accuracy.
|
||||
*
|
||||
* @param pageable
|
||||
* @return a page of entities
|
||||
*/
|
||||
Page<T> findAll(Pageable pageable);
|
||||
|
||||
/**
|
||||
* Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
|
||||
* {@link Page#getTotalPages()} returns an estimation of the total number of pages and should not be relied upon for
|
||||
* accuracy.
|
||||
*
|
||||
* @param pageable
|
||||
* @param depth
|
||||
* @return a page of entities
|
||||
*/
|
||||
Page<T> findAll(Pageable pageable, int depth);
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.cdi;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.enterprise.context.spi.CreationalContext;
|
||||
import javax.enterprise.inject.spi.Bean;
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactory;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryBean;
|
||||
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.data.repository.cdi.CdiRepositoryBean} to create Neo4j repository instances via CDI.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.2
|
||||
*/
|
||||
public class Neo4jCdiRepositoryBean<T> extends CdiRepositoryBean<T> {
|
||||
private final Bean<Session> sessionBean;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Neo4jCdiRepositoryBean}.
|
||||
*
|
||||
* @param sessionBean must not be {@literal null}.
|
||||
* @param qualifiers must not be {@literal null}.
|
||||
* @param repositoryType must not be {@literal null}.
|
||||
* @param beanManager must not be {@literal null}.
|
||||
* @param detector detector for the custom {@link org.springframework.data.repository.Repository} implementations
|
||||
* {@link CustomRepositoryImplementationDetector}, can be {@literal null}.
|
||||
*/
|
||||
public Neo4jCdiRepositoryBean(Bean<Session> sessionBean, Set<Annotation> qualifiers, Class<T> repositoryType,
|
||||
BeanManager beanManager, Optional<CustomRepositoryImplementationDetector> detector) {
|
||||
super(qualifiers, repositoryType, beanManager, detector);
|
||||
this.sessionBean = sessionBean;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(javax.enterprise.context.spi.CreationalContext, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {
|
||||
|
||||
Session session = getDependencyInstance(sessionBean, Session.class);
|
||||
|
||||
return create(() -> new Neo4jRepositoryFactory(session), repositoryType);
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.cdi;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.enterprise.event.Observes;
|
||||
import javax.enterprise.inject.UnsatisfiedResolutionException;
|
||||
import javax.enterprise.inject.spi.AfterBeanDiscovery;
|
||||
import javax.enterprise.inject.spi.Bean;
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
import javax.enterprise.inject.spi.ProcessBean;
|
||||
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryBean;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport;
|
||||
|
||||
/**
|
||||
* CDI extension to export Neo4j repositories.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.2
|
||||
*/
|
||||
public class Neo4jCdiRepositoryExtension extends CdiRepositoryExtensionSupport {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Neo4jCdiRepositoryExtension.class);
|
||||
private final Map<Set<Annotation>, Bean<Session>> sessions = new HashMap<Set<Annotation>, Bean<Session>>();
|
||||
|
||||
public Neo4jCdiRepositoryExtension() {
|
||||
LOG.info("Activating CDI extension for Spring Data Neo4j repositories.");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<X> void processBean(@Observes ProcessBean<X> processBean) {
|
||||
Bean<X> bean = processBean.getBean();
|
||||
|
||||
for (Type type : bean.getTypes()) {
|
||||
if (type instanceof Class<?> && Session.class.isAssignableFrom((Class<?>) type)) {
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Discovered %s with qualifiers %s.", Session.class.getName(), bean.getQualifiers()));
|
||||
}
|
||||
|
||||
sessions.put(new HashSet<Annotation>(bean.getQualifiers()), (Bean<Session>) bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
|
||||
|
||||
for (Entry<Class<?>, Set<Annotation>> entry : getRepositoryTypes()) {
|
||||
|
||||
Class<?> repositoryType = entry.getKey();
|
||||
Set<Annotation> qualifiers = entry.getValue();
|
||||
|
||||
// Create the bean representing the repository.
|
||||
CdiRepositoryBean<?> repositoryBean = createRepositoryBean(repositoryType, qualifiers, beanManager);
|
||||
if (LOG.isInfoEnabled()) {
|
||||
LOG.info(String.format("Registering bean for %s with qualifiers %s.", repositoryType.getName(), qualifiers));
|
||||
}
|
||||
|
||||
// Register the bean to the container.
|
||||
registerBean(repositoryBean);
|
||||
afterBeanDiscovery.addBean(repositoryBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Bean}.
|
||||
*
|
||||
* @param <T> The type of the repository.
|
||||
* @param repositoryType The class representing the repository.
|
||||
* @param beanManager The BeanManager instance.
|
||||
* @return The bean.
|
||||
*/
|
||||
private <T> CdiRepositoryBean<T> createRepositoryBean(Class<T> repositoryType, Set<Annotation> qualifiers,
|
||||
BeanManager beanManager) {
|
||||
Bean<Session> sessionBean = this.sessions.get(qualifiers);
|
||||
|
||||
if (sessionBean == null) {
|
||||
throw new UnsatisfiedResolutionException(
|
||||
String.format("Unable to resolve a bean for '%s' with qualifiers %s.", Session.class.getName(), qualifiers));
|
||||
}
|
||||
|
||||
return new Neo4jCdiRepositoryBean<T>(sessionBean, qualifiers, repositoryType, beanManager,
|
||||
Optional.of(getCustomImplementationDetector()));
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* CDI support for Neo4j specific repository implementation.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.cdi;
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import static org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension.*;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.config.DefaultRepositoryBaseClass;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Annotation to enable Neo4j repositories. Will scan the package of the annotated configuration class for Spring Data
|
||||
* repositories by default.
|
||||
*
|
||||
* @author Vince Bickers
|
||||
* @author Mark Angrish
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(Neo4jRepositoriesRegistrar.class)
|
||||
public @interface EnableNeo4jRepositories {
|
||||
|
||||
/**
|
||||
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:
|
||||
* {@code @EnableExperimentalNeo4jRepositories("org.my.pkg")} instead of
|
||||
* {@code @EnableExperimentalNeo4jRepositories(basePackages="org.my.pkg")}.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this
|
||||
* attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
|
||||
*/
|
||||
String[] basePackages() default {};
|
||||
|
||||
/**
|
||||
* Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The
|
||||
* package of each class specified will be scanned. Consider creating a special no-op marker class or interface in
|
||||
* each package that serves no purpose other than being referenced by this attribute.
|
||||
*/
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
/**
|
||||
* Specifies which types are eligible for component scanning. Further narrows the set of candidate components from
|
||||
* everything in {@link #basePackages()} to everything in the base packages that matches the given filter or filters.
|
||||
*/
|
||||
ComponentScan.Filter[] includeFilters() default {};
|
||||
|
||||
/**
|
||||
* Specifies which types are not eligible for component scanning.
|
||||
*/
|
||||
ComponentScan.Filter[] excludeFilters() default {};
|
||||
|
||||
/**
|
||||
* Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So
|
||||
* for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning
|
||||
* for {@code PersonRepositoryImpl}.
|
||||
*/
|
||||
String repositoryImplementationPostfix() default "Impl";
|
||||
|
||||
/**
|
||||
* Configures the location of where to find the Spring Data named queries properties file. Will default to
|
||||
* {@code META-INFO/neo4j-named-queries.properties}.
|
||||
*/
|
||||
String namedQueriesLocation() default "";
|
||||
|
||||
/**
|
||||
* Returns the key of the {@link QueryLookupStrategy} to be used for lookup queries for query methods. Defaults to
|
||||
* {@link org.springframework.data.repository.query.QueryLookupStrategy.Key#CREATE_IF_NOT_FOUND}.
|
||||
*/
|
||||
QueryLookupStrategy.Key queryLookupStrategy() default QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* Returns the {@link org.springframework.beans.factory.FactoryBean} class to be used for each repository instance.
|
||||
* Defaults to {@link Neo4jRepositoryFactoryBean}.
|
||||
*/
|
||||
Class<?> repositoryFactoryBeanClass() default Neo4jRepositoryFactoryBean.class;
|
||||
|
||||
/**
|
||||
* Configure the repository base class to be used to create repository proxies for this particular configuration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link org.neo4j.ogm.session.SessionFactory} bean definition to be used to create
|
||||
* repositories discovered through this annotation. Defaults to {@code sessionFactory}.
|
||||
*/
|
||||
String sessionFactoryRef() default DEFAULT_SESSION_FACTORY_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link org.neo4j.ogm.session.Session} bean definition created. Defaults to a generated
|
||||
* name.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
String sessionBeanName() default GENERATE_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories
|
||||
* discovered through this annotation. Defaults to {@code transactionManager}.
|
||||
*/
|
||||
String transactionManagerRef() default DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link org.springframework.data.neo4j.mapping.Neo4jMappingContext} bean definition
|
||||
* created. Defaults to a generated name.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
String mappingContextBeanName() default GENERATE_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be discovered by the
|
||||
* repositories infrastructure.
|
||||
*/
|
||||
boolean considerNestedRepositories() default false;
|
||||
|
||||
/**
|
||||
* Configures whether to enable default transactions for Spring Data Neo4j repositories. Defaults to {@literal true}.
|
||||
* If disabled, repositories must be used behind a facade that's configuring transactions (e.g. using Spring's
|
||||
* annotation driven transaction facilities) or repository methods have to be used to demarcate transactions.
|
||||
*
|
||||
* @return whether to enable default transactions, defaults to {@literal true}.
|
||||
*/
|
||||
boolean enableDefaultTransactions() default true;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.neo4j.ogm.session.event.Event;
|
||||
import org.neo4j.ogm.session.event.EventListener;
|
||||
import org.neo4j.ogm.session.event.EventListenerAdapter;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.data.auditing.AuditingHandler;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jAuditingBeanPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
public class Neo4jAuditingEventListener extends EventListenerAdapter implements EventListener {
|
||||
|
||||
private final ObjectFactory<? extends IsNewAwareAuditingHandler> auditingHandlerFactory;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Neo4jAuditingEventListener} using the given {@link MappingContext} and {@link AuditingHandler}
|
||||
* provided by the given {@link ObjectFactory}. This constructor does an additional registration to the
|
||||
* {@link SessionFactory}. Therefore the {@link SessionFactory} must already be instantiated.
|
||||
*
|
||||
* @deprecated this constructor could create a cyclic dependency to the session factory if this class gets registered
|
||||
* as a bean in the context.
|
||||
* @param auditingHandlerFactory must not be {@literal null}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Neo4jAuditingEventListener(ObjectFactory<? extends IsNewAwareAuditingHandler> auditingHandlerFactory,
|
||||
SessionFactory sessionFactory) {
|
||||
|
||||
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
|
||||
this.auditingHandlerFactory = auditingHandlerFactory;
|
||||
|
||||
sessionFactory.register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor used for creating an instance in the {@link Neo4jAuditingRegistrar} to get registered in the session
|
||||
* "manually". The registration is done within the {@link Neo4jAuditingBeanPostProcessor}.
|
||||
*
|
||||
* @param auditingHandlerFactory {@link AuditingHandler} to hook into the {@code preSave} phase for auditing.
|
||||
*/
|
||||
public Neo4jAuditingEventListener(ObjectFactory<? extends IsNewAwareAuditingHandler> auditingHandlerFactory) {
|
||||
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
|
||||
this.auditingHandlerFactory = auditingHandlerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreSave(Event event) {
|
||||
Object object = event.getObject();
|
||||
if (object != null) {
|
||||
auditingHandlerFactory.getObject().markAudited(object);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.auditing.config.AuditingConfiguration;
|
||||
import org.springframework.data.config.ParsingUtils;
|
||||
import org.springframework.data.mapping.context.PersistentEntities;
|
||||
import org.springframework.data.neo4j.annotation.EnableNeo4jAuditing;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jAuditingBeanPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Frantisek Hartman
|
||||
*/
|
||||
public class Neo4jAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableNeo4jAuditing.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAuditingHandlerBeanName() {
|
||||
return "neo4jAuditingHandler";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
|
||||
|
||||
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
|
||||
|
||||
BeanDefinitionBuilder handler = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
|
||||
|
||||
BeanDefinitionBuilder definition = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(Neo4jMappingContextFactoryBean.class);
|
||||
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
|
||||
BeanDefinitionBuilder entities = BeanDefinitionBuilder.rootBeanDefinition(PersistentEntities.class);
|
||||
entities.addConstructorArgValue(definition);
|
||||
|
||||
handler.addConstructorArgValue(definition.getBeanDefinition());
|
||||
return configureDefaultAuditHandlerAttributes(configuration, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
|
||||
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Neo4jAuditingBeanPostProcessor.class);
|
||||
|
||||
beanDefinitionBuilder
|
||||
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
|
||||
|
||||
registerInfrastructureBeanWithId(beanDefinitionBuilder.getBeanDefinition(),
|
||||
Neo4jAuditingBeanPostProcessor.class.getName(), registry);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} to setup {@link Neo4jMappingContext} instances from Spring configuration.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jMappingContextFactoryBean extends AbstractFactoryBean<Neo4jMappingContext>
|
||||
implements ApplicationContextAware {
|
||||
|
||||
private final String sessionFactoryBeanName;
|
||||
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* Uses the default session factory name
|
||||
* {@link Neo4jRepositoryConfigurationExtension#DEFAULT_SESSION_FACTORY_BEAN_NAME} for finding the session factory
|
||||
* passed to the {@link Neo4jMappingContext}.
|
||||
*
|
||||
* @deprecated since 5.1.0, use {@link #Neo4jMappingContextFactoryBean(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public Neo4jMappingContextFactoryBean() {
|
||||
this(Neo4jRepositoryConfigurationExtension.DEFAULT_SESSION_FACTORY_BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the mapping context with a named {@link SessionFactory}.
|
||||
*
|
||||
* @param sessionFactoryBeanName must not be {@literal null} or empty.
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public Neo4jMappingContextFactoryBean(String sessionFactoryBeanName) {
|
||||
|
||||
Assert.hasText(sessionFactoryBeanName, "SessionFactoryBeanName must not be null or empty!");
|
||||
|
||||
this.sessionFactoryBeanName = sessionFactoryBeanName;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.beanFactory = applicationContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return Neo4jMappingContext.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
@Override
|
||||
protected Neo4jMappingContext createInstance() {
|
||||
|
||||
SessionFactory sessionFactory = beanFactory.getBean(this.sessionFactoryBeanName, SessionFactory.class);
|
||||
Neo4jMappingContext context = new Neo4jMappingContext(sessionFactory.metaData());
|
||||
|
||||
context.initialize();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.neo4j.ogm.typeconversion.ConversionCallback;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.neo4j.conversion.MetaDataDrivenConversionService;
|
||||
import org.springframework.data.neo4j.conversion.Neo4jOgmEntityInstantiatorAdapter;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class Neo4jOgmEntityInstantiatorConfigurationBean {
|
||||
|
||||
public Neo4jOgmEntityInstantiatorConfigurationBean(SessionFactory sessionFactory, Neo4jMappingContext mappingContext,
|
||||
ObjectProvider<ConversionService> conversionServiceObjectProvider) {
|
||||
|
||||
MetaData metaData = sessionFactory.metaData();
|
||||
ConversionService conversionService = conversionServiceObjectProvider
|
||||
.getIfUnique(() -> new MetaDataDrivenConversionService(metaData));
|
||||
metaData.registerConversionCallback(new ConversionServiceBasedConversionCallback(conversionService));
|
||||
|
||||
sessionFactory.setEntityInstantiator(new Neo4jOgmEntityInstantiatorAdapter(mappingContext, conversionService));
|
||||
}
|
||||
|
||||
private static class ConversionServiceBasedConversionCallback implements ConversionCallback {
|
||||
|
||||
private final ConversionService delegate;
|
||||
|
||||
public ConversionServiceBasedConversionCallback(ConversionService conversionService) {
|
||||
this.delegate = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T convert(Class<T> targetType, Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return delegate.convert(value, targetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
|
||||
/**
|
||||
* @author Vince Bickers
|
||||
* @author Mark Angrish
|
||||
*/
|
||||
public class Neo4jRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
|
||||
|
||||
@Override
|
||||
protected Class<? extends Annotation> getAnnotation() {
|
||||
return EnableNeo4jRepositories.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryConfigurationExtension getExtension() {
|
||||
return new Neo4jRepositoryConfigurationExtension();
|
||||
}
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.neo4j.ogm.annotation.NodeEntity;
|
||||
import org.neo4j.ogm.annotation.RelationshipEntity;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
|
||||
import org.springframework.data.neo4j.repository.Neo4jRepository;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jPersistenceExceptionTranslator;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean;
|
||||
import org.springframework.data.neo4j.transaction.SharedSessionCreator;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Neo4j specific configuration extension parsing custom attributes from the XML namespace and
|
||||
* {@link EnableNeo4jRepositories} annotation. Creates and registers {@link BeanDefinitionBuilder} for
|
||||
* {@link SharedSessionCreator} and {@link Neo4jMappingContextFactoryBean}. Also, it registers a bean definition for a
|
||||
* {@link PersistenceExceptionTranslationPostProcessor} to enable exception translation of persistence specific
|
||||
* exceptions into Spring's {@link DataAccessException} hierarchy.
|
||||
*
|
||||
* @author Vince Bickers
|
||||
* @author Mark Angrish
|
||||
* @author Mark Paluch
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
|
||||
|
||||
/** See {@link AbstractBeanDefinition#INFER_METHOD}. */
|
||||
static final String GENERATE_BEAN_NAME = "(generated)";
|
||||
static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
|
||||
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
|
||||
|
||||
private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";
|
||||
private static final String NEO4J_PERSISTENCE_EXCEPTION_TRANSLATOR_NAME = "neo4jPersistenceExceptionTranslator";
|
||||
private static final String MODULE_PREFIX = "neo4j";
|
||||
private static final String MODULE_NAME = "Neo4j";
|
||||
|
||||
/**
|
||||
* We use a generated name for every pair of {@code SessionFactory} and {@code Session} unless the user configures a
|
||||
* session bean name with {@code @EnableNeo4jRepositories(sessionBeanName="someName")}.
|
||||
*/
|
||||
private String sessionBeanName;
|
||||
|
||||
/**
|
||||
* We use a generated name for every pair of {@code SessionFactory} and {@code Neo4jMappingContext} unless the user
|
||||
* configures a session bean name with {@code @EnableNeo4jRepositories(mappingContextBeanName="someName")}.
|
||||
*/
|
||||
private String neo4jMappingContextBeanName;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModuleName()
|
||||
*/
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return MODULE_NAME;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config14.RepositoryConfigurationExtension#getRepositoryFactoryBeanClassName()
|
||||
*/
|
||||
@Override
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
return Neo4jRepositoryFactoryBean.class.getName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config14.RepositoryConfigurationExtensionSupport#getModulePrefix()
|
||||
*/
|
||||
@Override
|
||||
protected String getModulePrefix() {
|
||||
return MODULE_PREFIX;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingAnnotations()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
|
||||
return Arrays.asList(NodeEntity.class, RelationshipEntity.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingTypes()
|
||||
*/
|
||||
@Override
|
||||
protected Collection<Class<?>> getIdentifyingTypes() {
|
||||
return Collections.<Class<?>> singleton(Neo4jRepository.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.RepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
|
||||
|
||||
String transactionManagerRefPropertyName = "transactionManagerRef";
|
||||
String transactionManagerPropertyName = "transactionManager";
|
||||
String mappingContextPropertyName = "mappingContext";
|
||||
String sessionPropertyName = "session";
|
||||
|
||||
Optional<String> transactionManagerRef = source.getAttribute(transactionManagerRefPropertyName);
|
||||
builder.addPropertyValue(transactionManagerPropertyName,
|
||||
transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
|
||||
|
||||
builder.addPropertyReference(sessionPropertyName, this.sessionBeanName);
|
||||
builder.addPropertyReference(mappingContextPropertyName, this.neo4jMappingContextBeanName);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) {
|
||||
|
||||
AnnotationAttributes attributes = config.getAttributes();
|
||||
|
||||
builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE,
|
||||
attributes.getBoolean(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.XmlRepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) {
|
||||
|
||||
Optional<String> enableDefaultTransactions = config.getAttribute(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE);
|
||||
|
||||
if (enableDefaultTransactions.filter(StringUtils::hasText).isPresent()) {
|
||||
enableDefaultTransactions
|
||||
.ifPresent(value -> builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE, value));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#registerBeansForRoot(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.data.repository.config.RepositoryConfigurationSource)
|
||||
*/
|
||||
@Override
|
||||
public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) {
|
||||
|
||||
super.registerBeansForRoot(registry, config);
|
||||
|
||||
Object source = config.getSource();
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String configuredSessionBeanName = config.getAttribute("sessionBeanName").orElse(GENERATE_BEAN_NAME);
|
||||
this.sessionBeanName = registerWithGeneratedNameOrUseConfigured(createSharedSessionCreatorBeanDefinition(config),
|
||||
registry, configuredSessionBeanName, source);
|
||||
|
||||
String configuredMappingContextBeanName = config.getAttribute("mappingContextBeanName").orElse(GENERATE_BEAN_NAME);
|
||||
this.neo4jMappingContextBeanName = registerWithGeneratedNameOrUseConfigured(
|
||||
createNeo4jMappingContextFactoryBeanDefinition(config), registry, configuredMappingContextBeanName, source);
|
||||
|
||||
registerIfNotAlreadyRegistered(() -> new RootBeanDefinition(Neo4jPersistenceExceptionTranslator.class), registry,
|
||||
NEO4J_PERSISTENCE_EXCEPTION_TRANSLATOR_NAME, source);
|
||||
|
||||
AbstractBeanDefinition rootBeanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Neo4jOgmEntityInstantiatorConfigurationBean.class)
|
||||
.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
|
||||
.addConstructorArgReference(getSessionFactoryBeanName(config))
|
||||
.addConstructorArgReference(this.neo4jMappingContextBeanName).getBeanDefinition();
|
||||
registerWithGeneratedNameOrUseConfigured(rootBeanDefinition, registry, GENERATE_BEAN_NAME, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a generated bean name if {@code configuredBeanName} is equal to {@link #GENERATE_BEAN_NAME}, otherwise uses
|
||||
* the configured bean name to register the new bean. Does not check if there's already a bean under the configured
|
||||
* name but throws a {@link org.springframework.beans.factory.BeanDefinitionStoreException}. Checks whether a bean is
|
||||
* already registered under {@code configuredBeanName} in the given {@link BeanDefinitionRegistry} and uses a
|
||||
* generated name for registering the bean instead. If not, the suggested bean name is used.
|
||||
*
|
||||
* @param bean must not be {@literal null}.
|
||||
* @param registry must not be {@literal null}.
|
||||
* @param configuredBeanName must not be {@literal null} or empty.
|
||||
* @param source must not be {@literal null}.
|
||||
* @throws org.springframework.beans.factory.BeanDefinitionStoreException if the BeanDefinition is invalid or if there
|
||||
* is already a BeanDefinition for the specified bean name * (and we are not allowed to override it)
|
||||
* @return the bean name used for registering the given {@link AbstractBeanDefinition}
|
||||
*/
|
||||
private static String registerWithGeneratedNameOrUseConfigured(AbstractBeanDefinition bean,
|
||||
BeanDefinitionRegistry registry, String configuredBeanName, Object source) {
|
||||
|
||||
String registeredBeanName = configuredBeanName;
|
||||
if (GENERATE_BEAN_NAME.equals(configuredBeanName)) {
|
||||
registeredBeanName = registerWithSourceAndGeneratedBeanName(bean, registry, source);
|
||||
} else {
|
||||
bean.setSource(source);
|
||||
registry.registerBeanDefinition(configuredBeanName, bean);
|
||||
}
|
||||
return registeredBeanName;
|
||||
}
|
||||
|
||||
private static AbstractBeanDefinition createSharedSessionCreatorBeanDefinition(RepositoryConfigurationSource config) {
|
||||
|
||||
String sessionFactoryBeanName = getSessionFactoryBeanName(config);
|
||||
|
||||
AbstractBeanDefinition sharedSessionCreatorBeanDefinition = BeanDefinitionBuilder //
|
||||
.rootBeanDefinition(SharedSessionCreator.class, "createSharedSession") //
|
||||
.addConstructorArgReference(sessionFactoryBeanName) //
|
||||
.getBeanDefinition();
|
||||
|
||||
sharedSessionCreatorBeanDefinition
|
||||
.addQualifier(new AutowireCandidateQualifier(Qualifier.class, sessionFactoryBeanName));
|
||||
|
||||
return sharedSessionCreatorBeanDefinition;
|
||||
}
|
||||
|
||||
private static AbstractBeanDefinition createNeo4jMappingContextFactoryBeanDefinition(
|
||||
RepositoryConfigurationSource config) {
|
||||
|
||||
return BeanDefinitionBuilder //
|
||||
.rootBeanDefinition(Neo4jMappingContextFactoryBean.class) //
|
||||
.addConstructorArgValue(getSessionFactoryBeanName(config)) //
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private static String getSessionFactoryBeanName(RepositoryConfigurationSource config) {
|
||||
return Optional.of("sessionFactoryRef").flatMap(config::getAttribute).orElse(DEFAULT_SESSION_FACTORY_BEAN_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionParser;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
|
||||
/**
|
||||
* Simple namespace handler for {@literal repositories} namespace.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
*/
|
||||
public class Neo4jRepositoryNameSpaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
|
||||
*/
|
||||
public void init() {
|
||||
|
||||
RepositoryConfigurationExtension extension = new Neo4jRepositoryConfigurationExtension();
|
||||
RepositoryBeanDefinitionParser repositoryBeanDefinitionParser = new RepositoryBeanDefinitionParser(extension);
|
||||
|
||||
registerBeanDefinitionParser("repositories", repositoryBeanDefinitionParser);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Infrastructure for Neo4j specific repositories.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository;
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.EmptyStackException;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.model.QueryStatistics;
|
||||
import org.neo4j.ogm.model.Result;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
/**
|
||||
* Base class for @link {@link RepositoryQuery}s.
|
||||
*
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public abstract class AbstractGraphRepositoryQuery implements RepositoryQuery {
|
||||
|
||||
protected final GraphQueryMethod queryMethod;
|
||||
protected final MetaData metaData;
|
||||
protected final Session session;
|
||||
|
||||
protected AbstractGraphRepositoryQuery(GraphQueryMethod queryMethod, MetaData metaData, Session session) {
|
||||
|
||||
this.queryMethod = queryMethod;
|
||||
this.metaData = metaData;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
protected abstract Query getQuery(Object[] parameters);
|
||||
|
||||
@Override
|
||||
public Object execute(Object[] parameters) {
|
||||
|
||||
Query query;
|
||||
try {
|
||||
query = getQuery(parameters);
|
||||
} catch (EmptyStackException e) {
|
||||
throw new IllegalArgumentException("Not enough arguments for query " + getQueryMethod().getName());
|
||||
}
|
||||
|
||||
return doExecute(query, parameters);
|
||||
}
|
||||
|
||||
protected abstract Object doExecute(Query params, Object[] parameters);
|
||||
|
||||
@Override
|
||||
public GraphQueryMethod getQueryMethod() {
|
||||
return queryMethod;
|
||||
}
|
||||
|
||||
protected GraphQueryExecution getExecution(GraphParameterAccessor accessor) {
|
||||
|
||||
if (queryMethod.isStreamQuery()) {
|
||||
return new GraphQueryExecution.CollectionExecution(session, accessor);
|
||||
}
|
||||
if (isCountQuery()) {
|
||||
return new GraphQueryExecution.CountByExecution(session);
|
||||
}
|
||||
if (isDeleteQuery()) {
|
||||
return new GraphQueryExecution.DeleteByExecution(session, queryMethod);
|
||||
}
|
||||
if (isExistsQuery()) {
|
||||
return new GraphQueryExecution.ExistsByExecution(session);
|
||||
}
|
||||
if (returnsOgmSpecificType()) {
|
||||
return new GraphQueryExecution.QueryResultExecution(session, accessor);
|
||||
}
|
||||
if (queryMethod.isCollectionQuery()) {
|
||||
return new GraphQueryExecution.CollectionExecution(session, accessor);
|
||||
}
|
||||
if (queryMethod.isPageQuery()) {
|
||||
return new GraphQueryExecution.PagedExecution(session, accessor);
|
||||
}
|
||||
if (queryMethod.isSliceQuery()) {
|
||||
return new GraphQueryExecution.SlicedExecution(session, accessor);
|
||||
}
|
||||
return new GraphQueryExecution.SingleEntityExecution(session, accessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the query returns an OGM specific object type that should get a special processing ?
|
||||
*
|
||||
* @return true if that's the case
|
||||
*/
|
||||
private boolean returnsOgmSpecificType() {
|
||||
Class returnType = queryMethod.getMethod().getReturnType();
|
||||
return QueryStatistics.class.isAssignableFrom(returnType) || Result.class.isAssignableFrom(returnType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the query should get a count projection applied.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract boolean isCountQuery();
|
||||
|
||||
/**
|
||||
* Returns whether the query should get an exists projection applied.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract boolean isExistsQuery();
|
||||
|
||||
/**
|
||||
* Return weather the query should delete matching documents.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract boolean isDeleteQuery();
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import static java.lang.reflect.Proxy.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.neo4j.ogm.context.SingleUseEntityMapper;
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.neo4j.annotation.QueryResult;
|
||||
|
||||
/**
|
||||
* Convert OGM special {@link QueryResult} annotated types into SD understandable type.
|
||||
*
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class CustomResultConverter implements Converter<Object, Object> {
|
||||
|
||||
private final MetaData metaData;
|
||||
private final Class returnedType;
|
||||
|
||||
private final QueryResultInstantiator entityInstantiator;
|
||||
|
||||
CustomResultConverter(MetaData metaData, Class<?> returnedType, QueryResultInstantiator entityInstantiator) {
|
||||
|
||||
this.metaData = metaData;
|
||||
this.returnedType = returnedType;
|
||||
this.entityInstantiator = entityInstantiator;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object convert(Object source) {
|
||||
|
||||
if (returnedType.getAnnotation(QueryResult.class) == null) {
|
||||
return source;
|
||||
}
|
||||
if (returnedType.isInterface()) {
|
||||
Class<?>[] interfaces = new Class<?>[] { returnedType };
|
||||
return newProxyInstance(returnedType.getClassLoader(), interfaces,
|
||||
new QueryResultProxy((Map<String, Object>) source));
|
||||
}
|
||||
|
||||
SingleUseEntityMapper mapper = new SingleUseEntityMapper(metaData, entityInstantiator);
|
||||
return mapper.map(returnedType, (Map<String, Object>) source);
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.Filters;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.repository.query.filter.FilterBuilder;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* A filter builder definition represents an ongoing state of {@link FilterBuilder filter builder} chained together with
|
||||
* "and" or "or". It is used with a resolved parameters to create an instance {@link Filters} for building a query
|
||||
* object.
|
||||
*
|
||||
* @author Luanne Misquitta
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class FilterBuildersDefinition {
|
||||
|
||||
private final Class<?> entityType;
|
||||
|
||||
private final Part basePart;
|
||||
|
||||
private final List<FilterBuilder> filterBuilders;
|
||||
private final Predicate<Part> isInternalIdProperty;
|
||||
|
||||
static UnstartedBuild forType(Neo4jMappingContext mappingContext, Class<?> entityType) {
|
||||
return new UnstartedBuild(mappingContext, entityType);
|
||||
}
|
||||
|
||||
private FilterBuildersDefinition(Neo4jMappingContext mappingContext, Class<?> entityType, Part basePart) {
|
||||
this.entityType = entityType;
|
||||
this.basePart = basePart;
|
||||
this.filterBuilders = new LinkedList<>();
|
||||
this.isInternalIdProperty = part -> {
|
||||
PersistentPropertyPath<Neo4jPersistentProperty> path = mappingContext
|
||||
.getPersistentPropertyPath(part.getProperty());
|
||||
Neo4jPersistentProperty possibleIdProperty = path.getRequiredLeafProperty();
|
||||
return possibleIdProperty.isInternalIdProperty();
|
||||
};
|
||||
this.filterBuilders.add(FilterBuilder.forPartAndEntity(basePart, entityType, BooleanOperator.NONE,
|
||||
isInternalIdProperty));
|
||||
}
|
||||
|
||||
TemplatedQuery buildTemplatedQuery() {
|
||||
return new TemplatedQuery(Collections.unmodifiableList(filterBuilders));
|
||||
}
|
||||
|
||||
/**
|
||||
* Comment on original DerivedQueryDefinition was: "because the OR is handled in a weird way. Luanne, explain better"
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Part getBasePart() {
|
||||
return basePart;
|
||||
}
|
||||
|
||||
FilterBuildersDefinition and(Part part) {
|
||||
this.filterBuilders.add(FilterBuilder.forPartAndEntity(part, entityType, BooleanOperator.AND, isInternalIdProperty));
|
||||
return this;
|
||||
}
|
||||
|
||||
FilterBuildersDefinition or(Part part) {
|
||||
this.filterBuilders.add(FilterBuilder.forPartAndEntity(part, entityType, BooleanOperator.OR, isInternalIdProperty));
|
||||
return this;
|
||||
}
|
||||
|
||||
static class UnstartedBuild {
|
||||
|
||||
private final Neo4jMappingContext mappingContext;
|
||||
|
||||
private final Class<?> entityType;
|
||||
|
||||
UnstartedBuild(Neo4jMappingContext mappingContext, Class<?> entityType) {
|
||||
this.mappingContext = mappingContext;
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
FilterBuildersDefinition startWith(Part firstPart) {
|
||||
return new FilterBuildersDefinition(mappingContext, entityType, firstPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import org.neo4j.ogm.cypher.query.SortOrder;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
|
||||
/**
|
||||
* Class used to lookup and handle special parameters such as depth, spatial parameters...
|
||||
*
|
||||
* @author Nicolas Mervaillie
|
||||
*/
|
||||
public interface GraphParameterAccessor extends ParameterAccessor {
|
||||
|
||||
/**
|
||||
* Gets the loading depth value of the {@link org.springframework.data.neo4j.annotation.Depth} annotated method
|
||||
* parameter.
|
||||
*
|
||||
* @return the depth value
|
||||
*/
|
||||
int getDepth();
|
||||
|
||||
/**
|
||||
* Get OGM specific sort order translated from method parameters.
|
||||
*
|
||||
* @return The sort order
|
||||
*/
|
||||
SortOrder getOgmSort();
|
||||
|
||||
// Should probably do the same for ogm specific pagination
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.neo4j.annotation.Depth;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
/**
|
||||
* Custom extension of {@link Parameters} discovering additional to handle @link{Depth} special parameter.
|
||||
*
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class GraphParameters extends Parameters<GraphParameters, GraphParameters.GraphParameter> {
|
||||
|
||||
private Integer depthIndex;
|
||||
|
||||
GraphParameters(Method method) {
|
||||
super(method);
|
||||
}
|
||||
|
||||
GraphParameters(List<GraphParameter> parameters, Integer depthIndex) {
|
||||
super(parameters);
|
||||
this.depthIndex = depthIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphParameter createParameter(MethodParameter parameter) {
|
||||
GraphParameter graphParameter = new GraphParameter(parameter);
|
||||
|
||||
// Detect manually annotated @Depth and reject multiple annotated ones
|
||||
if (this.depthIndex == null && graphParameter.isDepthParameter()) {
|
||||
this.depthIndex = graphParameter.getIndex();
|
||||
} else if (graphParameter.isDepthParameter()) {
|
||||
|
||||
String methodFragment = Optional.ofNullable(parameter)
|
||||
.map(MethodParameter::getMethod)
|
||||
.map(Method::toString)
|
||||
.map(s -> "method " + s)
|
||||
.orElse("unknown method");
|
||||
throw new IllegalStateException(String.format("Found multiple @Depth annotations on method %s! Only one allowed!",
|
||||
methodFragment));
|
||||
}
|
||||
|
||||
return graphParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphParameters createFrom(List<GraphParameter> parameters) {
|
||||
return new GraphParameters(parameters, this.depthIndex);
|
||||
}
|
||||
|
||||
int getDepthIndex() {
|
||||
return (depthIndex != null) ? depthIndex : -1;
|
||||
}
|
||||
|
||||
static class GraphParameter extends Parameter {
|
||||
|
||||
private final MethodParameter parameter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link GraphParameter}.
|
||||
*
|
||||
* @param parameter must not be {@literal null}.
|
||||
*/
|
||||
GraphParameter(MethodParameter parameter) {
|
||||
super(parameter);
|
||||
this.parameter = parameter;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.Parameter#isSpecialParameter()
|
||||
*/
|
||||
@Override
|
||||
public boolean isSpecialParameter() {
|
||||
return super.isSpecialParameter() || Distance.class.isAssignableFrom(getType())
|
||||
|| parameter.getParameterAnnotation(Depth.class) != null || Distance.class.isAssignableFrom(getType())
|
||||
|| Point.class.isAssignableFrom(getType());
|
||||
}
|
||||
|
||||
boolean isDepthParameter() {
|
||||
return parameter.getParameterAnnotation(Depth.class) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import org.neo4j.ogm.cypher.query.SortOrder;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.annotation.Depth;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
|
||||
/**
|
||||
* Custom {@link ParametersParameterAccessor} to allow access to the {@link Depth} parameter.
|
||||
*
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class GraphParametersParameterAccessor extends ParametersParameterAccessor implements GraphParameterAccessor {
|
||||
|
||||
private static final int DEFAULT_QUERY_DEPTH = 1;
|
||||
private final GraphQueryMethod method;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @param method must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
*/
|
||||
public GraphParametersParameterAccessor(GraphQueryMethod method, Object[] values) {
|
||||
super(method.getParameters(), values);
|
||||
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDepth() {
|
||||
|
||||
Depth methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method.getMethod(), Depth.class);
|
||||
if (methodAnnotation != null) {
|
||||
Object value = AnnotationUtils.getValue(methodAnnotation);
|
||||
return value == null ? DEFAULT_QUERY_DEPTH : (int) value;
|
||||
}
|
||||
|
||||
GraphParameters graphParameters = method.getParameters();
|
||||
|
||||
int depthIndex = graphParameters.getDepthIndex();
|
||||
|
||||
if (depthIndex != -1) {
|
||||
return getValue(depthIndex);
|
||||
} else {
|
||||
return DEFAULT_QUERY_DEPTH;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortOrder getOgmSort() {
|
||||
SortOrder sortOrder = new SortOrder();
|
||||
|
||||
if (getSort() != null) {
|
||||
for (Sort.Order order : getSort()) {
|
||||
if (order.isAscending()) {
|
||||
sortOrder.add(order.getProperty());
|
||||
} else {
|
||||
sortOrder.add(SortOrder.Direction.DESC, order.getProperty());
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortOrder;
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
import org.neo4j.ogm.model.Result;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.data.neo4j.annotation.QueryResult;
|
||||
import org.springframework.data.repository.support.PageableExecutionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Classes intended to pilot query execution according to the type of the query. The type of the query is determined by
|
||||
* looking at the result class of the method.
|
||||
*
|
||||
* @see AbstractGraphRepositoryQuery#getExecution(org.springframework.data.neo4j.repository.query.GraphParameterAccessor)
|
||||
* @author Nicolas Mervaillie
|
||||
*/
|
||||
public interface GraphQueryExecution {
|
||||
|
||||
Object execute(Query query, Class<?> type);
|
||||
|
||||
final class SingleEntityExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
private final GraphParameterAccessor accessor;
|
||||
|
||||
SingleEntityExecution(Session session, GraphParameterAccessor accessor) {
|
||||
this.session = session;
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
Iterable<?> result;
|
||||
if (query.isFilterQuery()) {
|
||||
result = session.loadAll(type, query.getFilters(), accessor.getDepth());
|
||||
} else {
|
||||
// not using queryForObject here because it raises too generic RuntimeException
|
||||
// if more than one result found
|
||||
if (type.getAnnotation(QueryResult.class) != null) {
|
||||
result = session.query(query.getCypherQuery(), query.getParameters()).queryResults();
|
||||
} else {
|
||||
result = session.query(type, query.getCypherQuery(), query.getParameters());
|
||||
}
|
||||
}
|
||||
Iterator<?> iterator = result.iterator();
|
||||
if (!iterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
Object ret = iterator.next();
|
||||
if (iterator.hasNext()) {
|
||||
throw new IncorrectResultSizeDataAccessException("Incorrect result size: expected at most 1", 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
final class CollectionExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
private final GraphParameterAccessor accessor;
|
||||
|
||||
CollectionExecution(Session session, GraphParameterAccessor accessor) {
|
||||
this.session = session;
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
if (query.isFilterQuery()) {
|
||||
return session.loadAll(type, query.getFilters(), accessor.getOgmSort(), accessor.getDepth());
|
||||
} else {
|
||||
if (type.getAnnotation(QueryResult.class) != null || Map.class.isAssignableFrom(type)) {
|
||||
return session.query(query.getCypherQuery(accessor.getSort()), query.getParameters()).queryResults();
|
||||
} else {
|
||||
return session.query(type, query.getCypherQuery(accessor.getSort()), query.getParameters());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class QueryResultExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
private final GraphParameterAccessor accessor;
|
||||
|
||||
QueryResultExecution(Session session, GraphParameterAccessor accessor) {
|
||||
this.session = session;
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
return session.query(query.getCypherQuery(accessor.getSort()), query.getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
final class PagedExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
private final Pageable pageable;
|
||||
private final GraphParameterAccessor accessor;
|
||||
|
||||
PagedExecution(Session session, GraphParameterAccessor accessor) {
|
||||
this.session = session;
|
||||
this.pageable = accessor.getPageable();
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
|
||||
List<?> result;
|
||||
long count;
|
||||
if (query.isFilterQuery()) {
|
||||
result = (List<?>) session.loadAll(type, query.getFilters(), accessor.getOgmSort(),
|
||||
query.getPagination(pageable, false), accessor.getDepth());
|
||||
count = session.count(type, query.getFilters());
|
||||
} else {
|
||||
if (type.getAnnotation(QueryResult.class) != null) {
|
||||
result = (List<?>) session.query(query.getCypherQuery(pageable, false), query.getParameters()).queryResults();
|
||||
} else {
|
||||
result = (List<?>) session.query(type, query.getCypherQuery(pageable, false), query.getParameters());
|
||||
}
|
||||
count = (result.size() > 0) ? countTotalNumberOfElements(query) : 0;
|
||||
}
|
||||
|
||||
return PageableExecutionUtils.getPage(result, pageable, (LongSupplier) () -> count);
|
||||
}
|
||||
|
||||
private Integer countTotalNumberOfElements(Query query) {
|
||||
Assert.hasText(query.getCountQuery(), "Must specify a count query to get pagination info.");
|
||||
return session.queryForObject(Integer.class, query.getCountQuery(), query.getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
final class SlicedExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
private final GraphParameterAccessor accessor;
|
||||
private final Pageable pageable;
|
||||
|
||||
SlicedExecution(Session session, GraphParameterAccessor accessor) {
|
||||
this.session = session;
|
||||
this.accessor = accessor;
|
||||
this.pageable = accessor.getPageable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
|
||||
int pageSize = pageable.getPageSize();
|
||||
|
||||
List<?> result;
|
||||
if (query.isFilterQuery()) {
|
||||
// For a slice, need one extra result to determine if there is a next page
|
||||
result = (List<?>) session.loadAll(type, query.getFilters(), accessor.getOgmSort(),
|
||||
query.getPagination(pageable, true), accessor.getDepth());
|
||||
} else {
|
||||
String cypherQuery = query.getCypherQuery(pageable, true);
|
||||
if (type.getAnnotation(QueryResult.class) != null) {
|
||||
result = (List<?>) session.query(cypherQuery, query.getParameters()).queryResults();
|
||||
} else {
|
||||
result = (List<?>) session.query(type, cypherQuery, query.getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasNext = result.size() > pageSize;
|
||||
return new SliceImpl(hasNext ? result.subList(0, pageSize) : result, pageable, hasNext);
|
||||
}
|
||||
}
|
||||
|
||||
final class CountByExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
|
||||
CountByExecution(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
return session.count(type, query.getFilters());
|
||||
}
|
||||
}
|
||||
|
||||
final class ExistsByExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
|
||||
ExistsByExecution(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
if (query.isFilterQuery()) {
|
||||
return session.count(type, query.getFilters()) > 0;
|
||||
}
|
||||
Result result = session.query(query.getCypherQuery(), query.getParameters());
|
||||
return result.iterator().hasNext();
|
||||
}
|
||||
}
|
||||
|
||||
final class DeleteByExecution implements GraphQueryExecution {
|
||||
|
||||
private final Session session;
|
||||
private final GraphQueryMethod graphQueryMethod;
|
||||
|
||||
DeleteByExecution(Session session, GraphQueryMethod graphQueryMethod) {
|
||||
this.session = session;
|
||||
this.graphQueryMethod = graphQueryMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type) {
|
||||
Class<?> returnType = graphQueryMethod.getReturnedObjectType();
|
||||
|
||||
if (returnType.equals(Long.class)) {
|
||||
return session.delete(type, query.getFilters(), graphQueryMethod.isCollectionQuery());
|
||||
}
|
||||
throw new RuntimeException("Long or Iterable<Long> is required as the return type of a Delete query");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.mapping.MetaDataProvider;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Mark Angrish
|
||||
* @author Luanne Misquitta
|
||||
* @author Oliver Gierke
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
* @author Ihor Dziuba
|
||||
*/
|
||||
public class GraphQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final MetaData metaData;
|
||||
private final Session session;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> mappingContext;
|
||||
|
||||
/**
|
||||
* @param session
|
||||
* @param evaluationContextProvider
|
||||
* @deprecated since 5.1.0, use
|
||||
* {@link GraphQueryLookupStrategy#GraphQueryLookupStrategy(Session, QueryMethodEvaluationContextProvider, MappingContext)}
|
||||
* instead and provide the mapping context.
|
||||
*/
|
||||
@Deprecated
|
||||
public GraphQueryLookupStrategy(Session session, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this(session, evaluationContextProvider, null);
|
||||
}
|
||||
|
||||
public GraphQueryLookupStrategy(Session session, QueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
@Nullable MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> mappingContext) {
|
||||
|
||||
this.metaData = getMetaData(session);
|
||||
this.session = session;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
|
||||
*/
|
||||
@Override
|
||||
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
|
||||
NamedQueries namedQueries) {
|
||||
|
||||
GraphQueryMethod queryMethod = new GraphQueryMethod(method, metadata, factory);
|
||||
queryMethod.setMappingContext(this.mappingContext);
|
||||
String namedQueryName = queryMethod.getNamedQueryName();
|
||||
String namedCountQueryName = queryMethod.getNamedCountQueryName();
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String cypherQuery = namedQueries.getQuery(namedQueryName);
|
||||
if (namedQueries.hasQuery(namedCountQueryName)){
|
||||
String countQuery = namedQueries.getQuery(namedCountQueryName);
|
||||
return new NamedGraphRepositoryQuery(queryMethod, metaData, session, cypherQuery, countQuery, evaluationContextProvider);
|
||||
}
|
||||
return new NamedGraphRepositoryQuery(queryMethod, metaData, session, cypherQuery, evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new GraphRepositoryQuery(queryMethod, metaData, session, evaluationContextProvider);
|
||||
} else {
|
||||
return new PartTreeNeo4jQuery(queryMethod, metaData, session);
|
||||
}
|
||||
}
|
||||
|
||||
private static MetaData getMetaData(Session session) {
|
||||
|
||||
// This is the case in most standard setups: The SharedSessionCreator adds this MetaDataProvider interface
|
||||
if (session instanceof MetaDataProvider) {
|
||||
return ((MetaDataProvider) session).getMetaData();
|
||||
}
|
||||
|
||||
// That is usually the case in a programmatic setup of repositories or in the case of CDI environment.
|
||||
// Note that in this case the meta-data is retrieved from the session, not from the session-factory.
|
||||
Method getMetaData = ReflectionUtils.findMethod(session.getClass(), "metaData");
|
||||
if (getMetaData == null) {
|
||||
throw new IllegalStateException("Could not retrieve Neo4j-OGM MetaData from session.");
|
||||
} else {
|
||||
return (MetaData) ReflectionUtils.invokeMethod(getMetaData, session);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.annotation.Depth;
|
||||
import org.springframework.data.neo4j.annotation.Query;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Mark Angrish
|
||||
* @author Luanne Misquitta
|
||||
* @author Oliver Gierke
|
||||
* @author Michael J. Simons
|
||||
* @author Ihor Dziuba
|
||||
*/
|
||||
public class GraphQueryMethod extends QueryMethod {
|
||||
|
||||
private final Method method;
|
||||
private final Query queryAnnotation;
|
||||
private final Integer queryDepthParamIndex;
|
||||
private final boolean isExistsQuery;
|
||||
private @Nullable MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> mappingContext;
|
||||
|
||||
public GraphQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
|
||||
super(method, metadata, factory);
|
||||
|
||||
this.method = method;
|
||||
this.queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
|
||||
if (this.queryAnnotation != null) {
|
||||
this.isExistsQuery = queryAnnotation.exists();
|
||||
} else {
|
||||
this.isExistsQuery = false;
|
||||
}
|
||||
this.queryDepthParamIndex = getQueryDepthParamIndex(method);
|
||||
Integer queryDepth = getStaticQueryDepth(method);
|
||||
if (queryDepth != null && queryDepthParamIndex != null) {
|
||||
throw new IllegalArgumentException(method.getName() + " cannot have both a method @Depth and a parameter @Depth");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parameters<?, ?> createParameters(Method method) {
|
||||
return new GraphParameters(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphParameters getParameters() {
|
||||
return (GraphParameters) super.getParameters();
|
||||
}
|
||||
|
||||
public MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> getMappingContext() {
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
public void setMappingContext(MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> mappingContext) {
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return queryAnnotation.value();
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the named query this method belongs to.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getNamedQueryName() {
|
||||
return String.format("%s.%s", getDomainClass().getSimpleName(), method.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the count query related to named query this method belongs to
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getNamedCountQueryName() {
|
||||
return String.format("%s.countQuery", getNamedQueryName());
|
||||
}
|
||||
|
||||
public Integer getQueryDepthParamIndex() {
|
||||
return queryDepthParamIndex;
|
||||
}
|
||||
|
||||
private Integer getQueryDepthParamIndex(Method method) {
|
||||
Annotation[][] annotations = method.getParameterAnnotations();
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
if (annotations[i].length > 0) {
|
||||
for (Annotation annotation : annotations[i]) {
|
||||
if (annotation.annotationType() == Depth.class) {
|
||||
if (method.getParameterTypes()[i] == Integer.class || method.getParameterTypes()[i] == int.class) {
|
||||
return i;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Depth parameter in " + method.getName() + " must be an integer");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Integer getStaticQueryDepth(Method method) {
|
||||
if (method.isAnnotationPresent(Depth.class)) {
|
||||
return method.getAnnotation(Depth.class).value();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCountQueryString() {
|
||||
return queryAnnotation != null ? queryAnnotation.countQuery() : null;
|
||||
}
|
||||
|
||||
public boolean hasAnnotatedQuery() {
|
||||
return getAnnotatedQuery() != null;
|
||||
}
|
||||
|
||||
boolean isAnnotatedExistsQuery() {
|
||||
return isExistsQuery;
|
||||
}
|
||||
|
||||
private String getAnnotatedQuery() {
|
||||
|
||||
String query = (String) AnnotationUtils.getValue(getQueryAnnotation());
|
||||
return StringUtils.hasText(query) ? query : null;
|
||||
}
|
||||
|
||||
private Query getQueryAnnotation() {
|
||||
return AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.model.Result;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.neo4j.repository.query.spel.ParameterizedQuery;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
|
||||
/**
|
||||
* Specialisation of {@link RepositoryQuery} that handles mapping to object annotated with <code>@Query</code>.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @author Luanne Misquitta
|
||||
* @author Jasper Blues
|
||||
* @author Mark Paluch
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class GraphRepositoryQuery extends AbstractGraphRepositoryQuery {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GraphRepositoryQuery.class);
|
||||
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final QueryResultInstantiator entityInstantiator;
|
||||
private final boolean isExistsQuery;
|
||||
|
||||
private ParameterizedQuery parameterizedQuery;
|
||||
|
||||
GraphRepositoryQuery(GraphQueryMethod graphQueryMethod, MetaData metaData, Session session,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider
|
||||
) {
|
||||
|
||||
super(graphQueryMethod, metaData, session);
|
||||
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.entityInstantiator = new QueryResultInstantiator(metaData, queryMethod.getMappingContext());
|
||||
this.isExistsQuery = graphQueryMethod.isAnnotatedExistsQuery();
|
||||
}
|
||||
|
||||
protected Object doExecute(Query query, Object[] parameters) {
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Executing query for method {}", queryMethod.getName());
|
||||
}
|
||||
|
||||
GraphParameterAccessor accessor = new GraphParametersParameterAccessor(queryMethod, parameters);
|
||||
ResultProcessor processor = queryMethod.getResultProcessor().withDynamicProjection(accessor);
|
||||
|
||||
Class<?> methodReturnType = queryMethod.getMethod().getReturnType();
|
||||
Class<?> processorReturnType = processor.getReturnedType().getReturnedType();
|
||||
|
||||
Object result = getExecution(accessor).execute(query, processorReturnType);
|
||||
|
||||
return Result.class.equals(methodReturnType) ? result
|
||||
: processor.processResult(result, new CustomResultConverter(metaData,
|
||||
processorReturnType, entityInstantiator));
|
||||
}
|
||||
|
||||
protected Query getQuery(Object[] parameters) {
|
||||
ParameterizedQuery parameterizedQuery = getParameterizedQuery();
|
||||
Map<String, Object> parametersFromQuery = parameterizedQuery.resolveParameter(parameters, this::resolveParams);
|
||||
return new Query(parameterizedQuery.getQueryString(), queryMethod.getCountQueryString(), parametersFromQuery);
|
||||
}
|
||||
|
||||
private ParameterizedQuery getParameterizedQuery() {
|
||||
if (parameterizedQuery == null) {
|
||||
|
||||
Parameters<?, ?> methodParameters = queryMethod.getParameters();
|
||||
parameterizedQuery = ParameterizedQuery.getParameterizedQuery(getAnnotationQueryString(), methodParameters,
|
||||
evaluationContextProvider);
|
||||
}
|
||||
return parameterizedQuery;
|
||||
}
|
||||
|
||||
Map<String, Object> resolveParams(Parameters<?, ?> methodParameters, Object[] parameters) {
|
||||
|
||||
Map<String, Object> resolvedParameters = new HashMap<>();
|
||||
|
||||
for (Parameter parameter : methodParameters) {
|
||||
int parameterIndex = parameter.getIndex();
|
||||
Object parameterValue = getParameterValue(parameters[parameterIndex]);
|
||||
|
||||
// We support using parameters based on their index and their name at the same time,
|
||||
// so parameters are always bound by index.
|
||||
resolvedParameters.put(Integer.toString(parameterIndex), parameterValue);
|
||||
|
||||
// Make sure we don't add "special" parameters as named parameters
|
||||
if (parameter.isNamedParameter()) {
|
||||
// even though the above check ensures the presence usually, it's probably better to
|
||||
// treat #isNamedParameter as a blackbox and not just calling #get() on the optional.
|
||||
parameter.getName().ifPresent(parameterName -> resolvedParameters.put(parameterName, parameterValue));
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedParameters;
|
||||
}
|
||||
|
||||
private String getAnnotationQueryString() {
|
||||
return getQueryMethod().getQuery();
|
||||
}
|
||||
|
||||
private Object getParameterValue(Object parameter) {
|
||||
|
||||
// The parameter might be an entity, try to resolve its id
|
||||
Object parameterValue = session.resolveGraphIdFor(parameter);
|
||||
if (parameterValue == null) { // Either not an entity or not persisted
|
||||
parameterValue = parameter;
|
||||
}
|
||||
return parameterValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCountQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return isExistsQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Specialisation of {@link GraphRepositoryQuery} that creates queries from named queries defined in
|
||||
* {@code META-INFO/neo4j-named-queries.properties}.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
* @author Ihor Dziuba
|
||||
*/
|
||||
public class NamedGraphRepositoryQuery extends GraphRepositoryQuery {
|
||||
|
||||
private final String cypherQuery;
|
||||
private @Nullable final String countQuery;
|
||||
|
||||
NamedGraphRepositoryQuery(GraphQueryMethod graphQueryMethod, MetaData metaData, Session session, String cypherQuery,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
this(graphQueryMethod, metaData, session, cypherQuery, null, evaluationContextProvider);
|
||||
}
|
||||
|
||||
NamedGraphRepositoryQuery(GraphQueryMethod graphQueryMethod, MetaData metaData, Session session, String cypherQuery,
|
||||
String countQuery, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(graphQueryMethod, metaData, session, evaluationContextProvider);
|
||||
this.cypherQuery = cypherQuery;
|
||||
this.countQuery = countQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQuery(Object[] parameters) {
|
||||
|
||||
return new Query(cypherQuery, countQuery, resolveParams(queryMethod.getParameters(), parameters));
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
/**
|
||||
* Specialisation of {@link RepositoryQuery} that handles mapping of filter finders.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @author Luanne Misquitta
|
||||
* @author Jasper Blues
|
||||
* @author Vince Bickers
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Mark Paluch
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class PartTreeNeo4jQuery extends AbstractGraphRepositoryQuery {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PartTreeNeo4jQuery.class);
|
||||
private final GraphQueryMethod graphQueryMethod;
|
||||
private final PartTree tree;
|
||||
|
||||
private final TemplatedQuery queryTemplate;
|
||||
|
||||
public PartTreeNeo4jQuery(GraphQueryMethod graphQueryMethod, MetaData metaData, Session session) {
|
||||
super(graphQueryMethod, metaData, session);
|
||||
|
||||
Class<?> domainType = graphQueryMethod.getEntityInformation().getJavaType();
|
||||
this.graphQueryMethod = graphQueryMethod;
|
||||
this.tree = new PartTree(graphQueryMethod.getName(), domainType);
|
||||
|
||||
this.queryTemplate = new TemplatedQueryCreator(this.tree,
|
||||
(Neo4jMappingContext) this.graphQueryMethod.getMappingContext(), domainType).createQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doExecute(Query params, Object[] parameters) {
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Executing query for method {}", graphQueryMethod.getName());
|
||||
}
|
||||
|
||||
GraphParameterAccessor accessor = new GraphParametersParameterAccessor(graphQueryMethod, parameters);
|
||||
Class<?> returnType = graphQueryMethod.getMethod().getReturnType();
|
||||
|
||||
if (returnType.equals(Void.class)) {
|
||||
throw new RuntimeException("Derived Queries must have a return type");
|
||||
}
|
||||
|
||||
ResultProcessor processor = graphQueryMethod.getResultProcessor().withDynamicProjection(accessor);
|
||||
Object results = getExecution(accessor).execute(params, processor.getReturnedType().getDomainType());
|
||||
|
||||
return processor.processResult(results);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query getQuery(Object[] parameters) {
|
||||
|
||||
Map<Integer, Object> resolvedParameters = resolveParameters(parameters);
|
||||
return this.queryTemplate.createExecutableQuery(resolvedParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCountQuery() {
|
||||
return tree.isCountProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return tree.isExistsProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return tree.isDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets values from parameters supplied by the finder on {@link org.neo4j.ogm.cypher.Filter} built by the
|
||||
* {@link GraphQueryMethod}
|
||||
*
|
||||
* @param parameters parameter values supplied by the finder method
|
||||
* @return List of Parameter with values set
|
||||
*/
|
||||
private Map<Integer, Object> resolveParameters(Object[] parameters) {
|
||||
Map<Integer, Object> resolvedParameters = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (graphQueryMethod.getQueryDepthParamIndex() == null
|
||||
|| (graphQueryMethod.getQueryDepthParamIndex() != null && graphQueryMethod.getQueryDepthParamIndex() != i)) {
|
||||
resolvedParameters.put(i, parameters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedParameters;
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.neo4j.ogm.cypher.Filters;
|
||||
import org.neo4j.ogm.cypher.query.Pagination;
|
||||
import org.neo4j.ogm.cypher.query.SortOrder;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.util.PagingAndSortingUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Represents an OGM query. Can hold either cypher queries or filter definitions. Also in charge of adding pagination /
|
||||
* sort to the string based queries as OGM does not support pagination and sort on those.
|
||||
*
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
* @author Ihor Dziuba
|
||||
*/
|
||||
public class Query {
|
||||
|
||||
private static final String SKIP = "sdnSkip";
|
||||
private static final String LIMIT = "sdnLimit";
|
||||
private static final String SKIP_LIMIT = " SKIP $" + SKIP + " LIMIT $" + LIMIT;
|
||||
private static final String ORDER_BY_CLAUSE = " ORDER BY %s";
|
||||
|
||||
private Filters filters;
|
||||
private String cypherQuery;
|
||||
private Map<String, Object> parameters;
|
||||
private @Nullable String countQuery;
|
||||
|
||||
public Query(Filters filters) {
|
||||
Assert.notNull(filters, "Filters must not be null.");
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public Query(String cypherQuery, @Nullable String countQuery, Map<String, Object> parameters) {
|
||||
Assert.notNull(cypherQuery, "Query must not be null.");
|
||||
Assert.notNull(parameters, "Parameters must not be null.");
|
||||
this.cypherQuery = sanitize(cypherQuery);
|
||||
this.countQuery = countQuery == null ? null : sanitize(countQuery);
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public boolean isFilterQuery() {
|
||||
return filters != null;
|
||||
}
|
||||
|
||||
public Filters getFilters() {
|
||||
return filters;
|
||||
}
|
||||
|
||||
public String getCypherQuery() {
|
||||
return cypherQuery;
|
||||
}
|
||||
|
||||
public Map<String, ?> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public String getCountQuery() {
|
||||
return countQuery;
|
||||
}
|
||||
|
||||
public String getCypherQuery(Pageable pageable, boolean forSlicing) {
|
||||
String result = cypherQuery;
|
||||
Sort sort = null;
|
||||
if (pageable.isPaged() && pageable.getSort() != Sort.unsorted()) {
|
||||
sort = pageable.getSort();
|
||||
}
|
||||
if (sort != Sort.unsorted()) {
|
||||
// Custom queries in the OGM do not support pageable
|
||||
result = addSorting(result, sort);
|
||||
}
|
||||
result = addPaging(result, pageable, forSlicing);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getCypherQuery(Sort sort) {
|
||||
// Custom queries in the OGM do not support pageable
|
||||
String result = cypherQuery;
|
||||
if (sort != Sort.unsorted()) {
|
||||
result = addSorting(cypherQuery, sort);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String addPaging(String cypherQuery, Pageable pageable, boolean forSlicing) {
|
||||
// Custom queries in the OGM do not support pageable
|
||||
cypherQuery = formatBaseQuery(cypherQuery);
|
||||
cypherQuery = cypherQuery + SKIP_LIMIT;
|
||||
parameters.put(SKIP, pageable.getPageNumber() * pageable.getPageSize());
|
||||
if (forSlicing) {
|
||||
parameters.put(LIMIT, pageable.getPageSize() + 1);
|
||||
} else {
|
||||
parameters.put(LIMIT, pageable.getPageSize());
|
||||
}
|
||||
return cypherQuery;
|
||||
}
|
||||
|
||||
private String addSorting(String baseQuery, Sort sort) {
|
||||
baseQuery = formatBaseQuery(baseQuery);
|
||||
if (sort == null) {
|
||||
return baseQuery;
|
||||
}
|
||||
final String sortOrder = getSortOrder(sort);
|
||||
if (sortOrder.isEmpty()) {
|
||||
return baseQuery;
|
||||
}
|
||||
return baseQuery + String.format(ORDER_BY_CLAUSE, sortOrder);
|
||||
}
|
||||
|
||||
private String getSortOrder(Sort sort) {
|
||||
return sort.stream()
|
||||
.map(Query::getPropertySortOrder)
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
private static String getPropertySortOrder(Sort.Order order) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(order.isIgnoreCase()){
|
||||
sb.append("toLower(").append(order.getProperty()).append(")");
|
||||
} else {
|
||||
sb.append(order.getProperty());
|
||||
}
|
||||
sb.append(" ").append(order.getDirection());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String formatBaseQuery(String cypherQuery) {
|
||||
cypherQuery = cypherQuery.trim();
|
||||
if (cypherQuery.endsWith(";")) {
|
||||
cypherQuery = cypherQuery.substring(0, cypherQuery.length() - 1);
|
||||
}
|
||||
return cypherQuery;
|
||||
}
|
||||
|
||||
public Pagination getPagination(Pageable pageable, boolean forSlicing) {
|
||||
Pagination pagination = new Pagination(pageable.getPageNumber(), pageable.getPageSize() + ((forSlicing) ? 1 : 0));
|
||||
pagination.setOffset(pageable.getPageNumber() * pageable.getPageSize());
|
||||
return pagination;
|
||||
}
|
||||
|
||||
public SortOrder getSort(Pageable pageable) {
|
||||
return PagingAndSortingUtils.convert(pageable.getSort());
|
||||
}
|
||||
|
||||
private String sanitize(String cypherQuery) {
|
||||
cypherQuery = cypherQuery.trim();
|
||||
if (cypherQuery.endsWith(";")) {
|
||||
cypherQuery = cypherQuery.substring(0, cypherQuery.length() - 1);
|
||||
}
|
||||
return cypherQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("Query{");
|
||||
if (filters != null) {
|
||||
sb.append("filters=").append(filters);
|
||||
} else {
|
||||
sb.append("cypherQuery='").append(cypherQuery).append('\'');
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.neo4j.ogm.metadata.reflect.ReflectionEntityInstantiator;
|
||||
import org.neo4j.ogm.session.EntityInstantiator;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.conversion.Neo4jOgmEntityInstantiatorAdapter;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* This class uses the {@link Neo4jOgmEntityInstantiatorAdapter} that came with OGM 3.1.0+ if possible, otherwise falls back
|
||||
* to {@link org.neo4j.ogm.metadata.reflect.ReflectionEntityInstantiator}, appearing in the same release. Thus it
|
||||
* handles the conversion of non entity {@code QueryResult} objects the same way as entities, supporting non-default
|
||||
* constructors and such. The {@link Neo4jOgmEntityInstantiatorAdapter} cannot be used if Springs mapping context is not
|
||||
* available.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class QueryResultInstantiator implements EntityInstantiator {
|
||||
|
||||
private final EntityInstantiator delegate;
|
||||
|
||||
QueryResultInstantiator(MetaData metadata,
|
||||
@Nullable MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> mappingContext) {
|
||||
|
||||
this.delegate = Optional.ofNullable(mappingContext)
|
||||
.<EntityInstantiator> map(ctx -> new Neo4jOgmEntityInstantiatorAdapter(ctx, null))
|
||||
.orElseGet(() -> new ReflectionEntityInstantiator(metadata));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T createInstance(Class<T> clazz, Map<String, Object> propertyValues) {
|
||||
return this.delegate.createInstance(clazz, propertyValues);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.neo4j.ogm.annotation.Property;
|
||||
import org.neo4j.ogm.session.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Method {@link InvocationHandler} used for proxy objects that implement arbitrary interfaces annotated with
|
||||
* <code>@QueryResult</code>.
|
||||
*
|
||||
* @author Adam George
|
||||
*/
|
||||
class QueryResultProxy implements InvocationHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(QueryResultProxy.class);
|
||||
|
||||
private static final Pattern beanGetterPattern = Pattern.compile("^(is|get)(\\w+)");
|
||||
|
||||
private final Map<String, ?> data;
|
||||
|
||||
QueryResultProxy(Map<String, ?> queryResults) {
|
||||
this.data = queryResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (isNotTraditionalGetter(method)) {
|
||||
log.warn("QueryResult interface method " + method.getName()
|
||||
+ " doesn't appear to be a getter and therefore may not return the correct result.");
|
||||
}
|
||||
|
||||
if (method.isAnnotationPresent(Property.class)) {
|
||||
Property annotation = method.getAnnotation(Property.class);
|
||||
return Utils.coerceTypes(method.getReturnType(), data.get(annotation.name()));
|
||||
}
|
||||
|
||||
Matcher matcher = beanGetterPattern.matcher(method.getName());
|
||||
if (matcher.matches()) {
|
||||
String propertyKey = matcher.group(2);
|
||||
propertyKey = propertyKey.substring(0, 1).toLowerCase().concat(propertyKey.substring(1));
|
||||
return Utils.coerceTypes(method.getReturnType(), data.get(propertyKey));
|
||||
}
|
||||
|
||||
return Utils.coerceTypes(method.getReturnType(), data.get(method.getName()));
|
||||
}
|
||||
|
||||
private boolean isNotTraditionalGetter(Method method) {
|
||||
return method.getParameterTypes().length != 0 || Void.class.equals(method.getReturnType())
|
||||
|| (!method.getName().startsWith("get") && !method.getName().startsWith("is"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.neo4j.ogm.cypher.Filters;
|
||||
import org.springframework.data.neo4j.repository.query.filter.FilterBuilder;
|
||||
|
||||
/**
|
||||
* A template query based on filters. {@link #createExecutableQuery(Map)} is used to create an executable query from
|
||||
* resolved parameters.
|
||||
*
|
||||
* @author Luanne Misquitta
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class TemplatedQuery {
|
||||
|
||||
private final List<FilterBuilder> filterBuilders;
|
||||
|
||||
static TemplatedQuery unfiltered() {
|
||||
return new TemplatedQuery(Collections.emptyList());
|
||||
}
|
||||
|
||||
TemplatedQuery(List<FilterBuilder> filterBuilders) {
|
||||
this.filterBuilders = filterBuilders;
|
||||
}
|
||||
|
||||
Query createExecutableQuery(Map<Integer, Object> resolvedParameters) {
|
||||
|
||||
// building a stack of parameter values, so that the builders can pull them
|
||||
// according to their needs (zero, one or more parameters)
|
||||
// avoids to manage a current parameter index state here.
|
||||
Stack<Object> parametersStack = new Stack<>();
|
||||
if (!resolvedParameters.isEmpty()) {
|
||||
Integer maxParameterIndex = Collections.max(resolvedParameters.keySet());
|
||||
for (int i = 0; i <= maxParameterIndex; i++) {
|
||||
parametersStack.add(0, resolvedParameters.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
List<Filter> filters = new ArrayList<>();
|
||||
for (FilterBuilder filterBuilder : filterBuilders) {
|
||||
filters.addAll(filterBuilder.build(parametersStack));
|
||||
}
|
||||
|
||||
return new Query(new Filters(filters));
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An {@link AbstractQueryCreator} that builds a query template based on filters. The intermediate object is a filter
|
||||
* definition whose state is modified during query creation.
|
||||
*
|
||||
* @author Luanne Misquitta
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class TemplatedQueryCreator extends AbstractQueryCreator<TemplatedQuery, FilterBuildersDefinition> {
|
||||
|
||||
private final Neo4jMappingContext mappingContext;
|
||||
private final Class<?> entityType;
|
||||
|
||||
public TemplatedQueryCreator(PartTree tree, Neo4jMappingContext mappingContext, Class<?> entityType) {
|
||||
super(tree);
|
||||
|
||||
this.mappingContext = mappingContext;
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterBuildersDefinition create(Part part, Iterator<Object> iterator) {
|
||||
return FilterBuildersDefinition.forType(mappingContext, entityType) //
|
||||
.startWith(part);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterBuildersDefinition and(Part part, FilterBuildersDefinition base, Iterator<Object> iterator) {
|
||||
return base.and(part);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FilterBuildersDefinition or(FilterBuildersDefinition base, FilterBuildersDefinition criteria) {
|
||||
return base.or(criteria.getBasePart());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TemplatedQuery complete(@Nullable FilterBuildersDefinition filterDefinition, Sort sort) {
|
||||
|
||||
return Optional.ofNullable(filterDefinition)
|
||||
.map(FilterBuildersDefinition::buildTemplatedQuery)
|
||||
.orElseGet(TemplatedQuery::unfiltered);
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import static org.neo4j.ogm.cypher.ComparisonOperator.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class BetweenComparisonBuilder extends FilterBuilder {
|
||||
|
||||
BetweenComparisonBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
|
||||
final Object lowerBoundOrRange = params.pop();
|
||||
|
||||
Object lowerBoundValue;
|
||||
Object upperBoundValue;
|
||||
|
||||
boolean inclusiveLowerBound = true;
|
||||
boolean inclusiveUpperBound = true;
|
||||
|
||||
if (lowerBoundOrRange instanceof Range) {
|
||||
Range range = (Range) lowerBoundOrRange;
|
||||
Range.Bound lowerBound = range.getLowerBound();
|
||||
lowerBoundValue = lowerBound.getValue();
|
||||
inclusiveLowerBound = lowerBound.isInclusive();
|
||||
|
||||
Range.Bound upperBound = range.getUpperBound();
|
||||
upperBoundValue = upperBound.getValue();
|
||||
inclusiveUpperBound = upperBound.isInclusive();
|
||||
} else {
|
||||
lowerBoundValue = lowerBoundOrRange;
|
||||
upperBoundValue = params.pop();
|
||||
}
|
||||
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
Filter lowerBoundFilter = createLowerBoundFilter(lowerBoundValue, inclusiveLowerBound, nestedAttributes);
|
||||
Filter upperBoundFilter = createUpperBoundFilter(upperBoundValue, inclusiveUpperBound, nestedAttributes);
|
||||
|
||||
return Arrays.asList(lowerBoundFilter, upperBoundFilter);
|
||||
}
|
||||
|
||||
private Filter createLowerBoundFilter(Object value, boolean inclusive, NestedAttributes nestedAttributes) {
|
||||
return createBoundFilter(Bound.LOWER, value, inclusive, booleanOperator, nestedAttributes);
|
||||
}
|
||||
|
||||
private Filter createUpperBoundFilter(Object value, boolean inclusive, NestedAttributes nestedAttributes) {
|
||||
return createBoundFilter(Bound.UPPER, value, inclusive, BooleanOperator.AND, nestedAttributes);
|
||||
}
|
||||
|
||||
private Filter createBoundFilter(Bound bound, Object value, boolean inclusive, BooleanOperator operator,
|
||||
NestedAttributes nestedAttributes) {
|
||||
Filter filter = new Filter(nestedAttributes.isEmpty() ?
|
||||
propertyName() :
|
||||
nestedAttributes.getLeafPropertySegment(), deriveComparisonOperator(bound, inclusive), value);
|
||||
filter.setOwnerEntityType(entityType);
|
||||
filter.setNegated(isNegated());
|
||||
filter.setBooleanOperator(operator);
|
||||
filter.setNestedPath(nestedAttributes.getSegments());
|
||||
return filter;
|
||||
}
|
||||
|
||||
private ComparisonOperator deriveComparisonOperator(Bound bound, boolean inclusive) {
|
||||
switch (bound) {
|
||||
case LOWER:
|
||||
return inclusive ? GREATER_THAN_EQUAL : GREATER_THAN;
|
||||
case UPPER:
|
||||
return inclusive ? LESS_THAN_EQUAL : LESS_THAN;
|
||||
}
|
||||
throw new IllegalArgumentException("unsupported bound");
|
||||
}
|
||||
|
||||
private enum Bound {
|
||||
UPPER,
|
||||
LOWER
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import static org.springframework.data.repository.query.parser.Part.Type.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class BooleanComparisonBuilder extends FilterBuilder {
|
||||
|
||||
BooleanComparisonBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
Filter filter = new Filter(
|
||||
nestedAttributes.isEmpty() ? propertyName() : nestedAttributes.getLeafPropertySegment(),
|
||||
ComparisonOperator.IS_TRUE);
|
||||
filter.setOwnerEntityType(entityType);
|
||||
filter.setBooleanOperator(booleanOperator);
|
||||
filter.setNegated(isNegated() || part.getType() == FALSE);
|
||||
filter.setNestedPath(nestedAttributes.getSegments());
|
||||
|
||||
return Collections.singletonList(filter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.neo4j.ogm.cypher.function.ContainsAnyComparison;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* Filter for entities having a collection like property (not) containing a given element.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class ContainsComparisonBuilder extends FilterBuilder {
|
||||
|
||||
ContainsComparisonBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
final Object containingValue = params.pop();
|
||||
Filter containingFilter = new Filter(
|
||||
nestedAttributes.isEmpty() ? propertyName() : nestedAttributes.getLeafPropertySegment(),
|
||||
new ContainsAnyComparison(containingValue));
|
||||
containingFilter.setOwnerEntityType(entityType);
|
||||
containingFilter.setBooleanOperator(booleanOperator);
|
||||
containingFilter.setNegated(isNegated());
|
||||
containingFilter.setNestedPath(nestedAttributes.getSegments());
|
||||
|
||||
return Collections.singletonList(containingFilter);
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.neo4j.ogm.cypher.function.DistanceComparison;
|
||||
import org.neo4j.ogm.cypher.function.DistanceFromNativePoint;
|
||||
import org.neo4j.ogm.cypher.function.DistanceFromPoint;
|
||||
import org.neo4j.ogm.cypher.function.FilterFunction;
|
||||
import org.neo4j.ogm.cypher.function.NativeDistanceComparison;
|
||||
import org.neo4j.ogm.types.spatial.AbstractPoint;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class DistanceComparisonBuilder extends FilterBuilder {
|
||||
|
||||
private final BiFunction<String, FilterFunction, Filter> filterSupplier;
|
||||
|
||||
DistanceComparisonBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
|
||||
this.filterSupplier = createFilterSupplier();
|
||||
}
|
||||
|
||||
private static BiFunction<String, FilterFunction, Filter> createFilterSupplier() {
|
||||
try {
|
||||
// Neo4j-OGM 3.2
|
||||
final Constructor<Filter> ctor = Filter.class
|
||||
.getDeclaredConstructor(String.class, FilterFunction.class, ComparisonOperator.class);
|
||||
return (propertyName, filterFunction) ->
|
||||
BeanUtils.instantiateClass(ctor, propertyName, filterFunction, ComparisonOperator.LESS_THAN);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return (propertyName, filterFunction) -> new Filter(propertyName, filterFunction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
|
||||
Object firstArg = params.pop();
|
||||
Object secondArg = params.pop();
|
||||
|
||||
Filter distanceComparisonFilter;
|
||||
|
||||
if (needsFilterForSpringPoint(firstArg, secondArg)) {
|
||||
distanceComparisonFilter = springPointFilterOf(firstArg, secondArg);
|
||||
} else {
|
||||
distanceComparisonFilter = nativePointFilterOf(firstArg, secondArg);
|
||||
}
|
||||
|
||||
return Collections.singletonList(distanceComparisonFilter);
|
||||
|
||||
}
|
||||
|
||||
private Filter springPointFilterOf(Object firstArg, Object secondArg) {
|
||||
|
||||
Distance distance;
|
||||
Point point;
|
||||
|
||||
if (firstArg instanceof Distance && secondArg instanceof Point) {
|
||||
distance = (Distance) firstArg;
|
||||
point = (Point) secondArg;
|
||||
} else if (secondArg instanceof Distance && firstArg instanceof Point) {
|
||||
distance = (Distance) secondArg;
|
||||
point = (Point) firstArg;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"findNear requires an argument of type Distance and an argument of type Point");
|
||||
}
|
||||
|
||||
return createFilterForSpringPoint(point, calculateDistanceInMeter(distance));
|
||||
}
|
||||
|
||||
private Filter nativePointFilterOf(Object firstArg, Object secondArg) {
|
||||
|
||||
Distance distance;
|
||||
AbstractPoint spatialPoint;
|
||||
|
||||
if (firstArg instanceof AbstractPoint && secondArg instanceof Distance) {
|
||||
distance = (Distance) secondArg;
|
||||
spatialPoint = (AbstractPoint) firstArg;
|
||||
} else if (firstArg instanceof Distance && secondArg instanceof AbstractPoint) {
|
||||
distance = (Distance) firstArg;
|
||||
spatialPoint = (AbstractPoint) secondArg;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"findNear requires an argument of type Distance and an argument of type Point");
|
||||
}
|
||||
|
||||
return createFilterForSpatialPoint(spatialPoint, calculateDistanceInMeter(distance));
|
||||
}
|
||||
|
||||
private boolean needsFilterForSpringPoint(Object firstArg, Object secondArg) {
|
||||
return (firstArg instanceof Point || secondArg instanceof Point);
|
||||
}
|
||||
|
||||
private Filter createFilterForSpatialPoint(AbstractPoint spatialPoint, double meters) {
|
||||
|
||||
NativeDistanceComparison distanceComparison = NativeDistanceComparison
|
||||
.distanceComparisonFor(new DistanceFromNativePoint(spatialPoint, meters));
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
String propertyName = super.part.getProperty().getLeafProperty().getSegment();
|
||||
|
||||
Filter filter = filterSupplier.apply(propertyName, distanceComparison);
|
||||
filter.setOwnerEntityType(entityType);
|
||||
filter.setBooleanOperator(booleanOperator);
|
||||
filter.setNegated(isNegated());
|
||||
filter.setNestedPath(nestedAttributes.getSegments());
|
||||
return filter;
|
||||
}
|
||||
|
||||
private Filter createFilterForSpringPoint(Point point, double meters) {
|
||||
|
||||
DistanceFromPoint distanceFromPoint = new DistanceFromPoint(point.getX(), point.getY(), meters);
|
||||
|
||||
DistanceComparison distanceComparison = new DistanceComparison(distanceFromPoint) {
|
||||
|
||||
@Override
|
||||
public String expression(String nodeIdentifier, String filteredProperty,
|
||||
UnaryOperator<String> createUniqueParameterName) {
|
||||
String latitudeProperty = nodeIdentifier + ".latitude";
|
||||
String longitudeProperty = nodeIdentifier + ".longitude";
|
||||
|
||||
return String.format(
|
||||
"distance(coalesce(point({latitude: %s, longitude: %s}), %s), point({latitude:$lat, longitude:$lon})) "
|
||||
+ "%s $distance ",
|
||||
latitudeProperty, longitudeProperty, nodeIdentifier + "." + propertyName(),
|
||||
ComparisonOperator.LESS_THAN.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
Filter filter = filterSupplier.apply(nestedAttributes.getLeafPropertySegment(), distanceComparison);
|
||||
filter.setOwnerEntityType(entityType);
|
||||
filter.setBooleanOperator(booleanOperator);
|
||||
filter.setNegated(isNegated());
|
||||
filter.setNestedPath(nestedAttributes.getSegments());
|
||||
return filter;
|
||||
}
|
||||
|
||||
static double calculateDistanceInMeter(Distance distance) {
|
||||
|
||||
if (distance.getMetric() == Metrics.KILOMETERS) {
|
||||
return distance.getValue() / 0.001d;
|
||||
} else if (distance.getMetric() == Metrics.MILES) {
|
||||
return distance.getValue() / 0.00062137d;
|
||||
} else {
|
||||
return distance.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class ExistsFilterBuilder extends FilterBuilder {
|
||||
|
||||
ExistsFilterBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
Filter filter = new Filter(
|
||||
nestedAttributes.isEmpty() ? propertyName() : nestedAttributes.getLeafPropertySegment(),
|
||||
ComparisonOperator.EXISTS);
|
||||
filter.setOwnerEntityType(entityType);
|
||||
filter.setBooleanOperator(booleanOperator);
|
||||
filter.setNegated(isNegated());
|
||||
filter.setNestedPath(nestedAttributes.getSegments());
|
||||
|
||||
return Collections.singletonList(filter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Stack;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* The name of this class is wrong: It builds a list of filters, not a single filter. Starting with Neo4j-OGM 4.0,
|
||||
* there will be an actual filter builder in OGM and this class needs to be renamed.
|
||||
*
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public abstract class FilterBuilder {
|
||||
|
||||
protected Part part;
|
||||
protected BooleanOperator booleanOperator;
|
||||
protected Class<?> entityType;
|
||||
protected Predicate<Part> isInternalIdProperty = part -> false;
|
||||
|
||||
public static FilterBuilder forPartAndEntity(Part part, Class<?> entityType, BooleanOperator booleanOperator,
|
||||
Predicate<Part> isInternalIdProperty) {
|
||||
FilterBuilder filterBuilder;
|
||||
switch (part.getType()) {
|
||||
case NEAR:
|
||||
filterBuilder = new DistanceComparisonBuilder(part, booleanOperator, entityType);
|
||||
break;
|
||||
case BETWEEN:
|
||||
filterBuilder = new BetweenComparisonBuilder(part, booleanOperator, entityType);
|
||||
break;
|
||||
case NOT_CONTAINING:
|
||||
case CONTAINING:
|
||||
filterBuilder = resolveMatchingContainsFilterBuilder(part, entityType, booleanOperator);
|
||||
break;
|
||||
case IS_NULL:
|
||||
case IS_NOT_NULL:
|
||||
filterBuilder = new IsNullFilterBuilder(part, booleanOperator, entityType);
|
||||
break;
|
||||
case EXISTS:
|
||||
filterBuilder = new ExistsFilterBuilder(part, booleanOperator, entityType);
|
||||
break;
|
||||
case TRUE:
|
||||
case FALSE:
|
||||
filterBuilder = new BooleanComparisonBuilder(part, booleanOperator, entityType);
|
||||
break;
|
||||
default:
|
||||
filterBuilder = new PropertyComparisonBuilder(part, booleanOperator, entityType);
|
||||
break;
|
||||
}
|
||||
|
||||
filterBuilder.isInternalIdProperty = isInternalIdProperty;
|
||||
return filterBuilder;
|
||||
}
|
||||
|
||||
FilterBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
this.part = part;
|
||||
this.booleanOperator = booleanOperator;
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
public abstract List<Filter> build(Stack<Object> params);
|
||||
|
||||
boolean isNegated() {
|
||||
return part.getType().name().startsWith("NOT");
|
||||
}
|
||||
|
||||
protected String propertyName() {
|
||||
return part.getProperty().getSegment();
|
||||
}
|
||||
|
||||
protected final NestedAttributes getNestedAttributes(Part part) {
|
||||
|
||||
PropertyPath property = part.getProperty();
|
||||
if (!property.hasNext()) {
|
||||
return EMPTY_NESTED_ATTRIBUTES;
|
||||
} else {
|
||||
List<Filter.NestedPathSegment> segments = new ArrayList<>();
|
||||
segments.add(new Filter.NestedPathSegment(property.getSegment(), property.getType()));
|
||||
segments.addAll(deepNestedProperty(property));
|
||||
return new NestedAttributes(property.getOwningType().getType(), segments,
|
||||
property.getLeafProperty().getSegment());
|
||||
}
|
||||
}
|
||||
|
||||
public static final NestedAttributes EMPTY_NESTED_ATTRIBUTES = new NestedAttributes(Void.class,
|
||||
Collections.emptyList(), null);
|
||||
|
||||
protected static final class NestedAttributes {
|
||||
|
||||
private final Class<?> owningType;
|
||||
private final List<Filter.NestedPathSegment> segments;
|
||||
private final String leafPropertySegment;
|
||||
|
||||
NestedAttributes(Class<?> owningType, List<Filter.NestedPathSegment> segments,
|
||||
@Nullable String leafPropertySegment) {
|
||||
this.owningType = owningType;
|
||||
this.segments = new ArrayList<>(segments);
|
||||
this.leafPropertySegment = leafPropertySegment;
|
||||
}
|
||||
|
||||
public Filter.NestedPathSegment[] getSegments() {
|
||||
return segments.toArray(new Filter.NestedPathSegment[segments.size()]);
|
||||
}
|
||||
|
||||
public String getLeafPropertySegment() {
|
||||
return leafPropertySegment;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this == EMPTY_NESTED_ATTRIBUTES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
NestedAttributes that = (NestedAttributes) o;
|
||||
return owningType.equals(that.owningType) &&
|
||||
segments.equals(that.segments) &&
|
||||
Objects.equals(leafPropertySegment, that.leafPropertySegment);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return Objects.hash(owningType, segments, leafPropertySegment);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Filter.NestedPathSegment> deepNestedProperty(PropertyPath path) {
|
||||
List<Filter.NestedPathSegment> segments = new ArrayList<>();
|
||||
if (path.hasNext()) {
|
||||
PropertyPath next = path.next();
|
||||
if (next != null && !next.equals(next.getLeafProperty())) {
|
||||
segments.add(new Filter.NestedPathSegment(next.getSegment(), next.getType()));
|
||||
segments.addAll(deepNestedProperty(next));
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
private static FilterBuilder resolveMatchingContainsFilterBuilder(Part part, Class<?> entityType,
|
||||
BooleanOperator booleanOperator) {
|
||||
boolean usePropertyComparison = !part.getProperty().getTypeInformation().isCollectionLike();
|
||||
if (usePropertyComparison) {
|
||||
return new PropertyComparisonBuilder(part, booleanOperator, entityType);
|
||||
}
|
||||
return new ContainsComparisonBuilder(part, booleanOperator, entityType);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import static org.springframework.data.repository.query.parser.Part.Type.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class IsNullFilterBuilder extends FilterBuilder {
|
||||
|
||||
IsNullFilterBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
Filter filter = new Filter(
|
||||
nestedAttributes.isEmpty() ? propertyName() : nestedAttributes.getLeafPropertySegment(),
|
||||
ComparisonOperator.IS_NULL);
|
||||
filter.setOwnerEntityType(entityType);
|
||||
filter.setBooleanOperator(booleanOperator);
|
||||
filter.setNegated(isNegated() || part.getType() == IS_NOT_NULL);
|
||||
filter.setNestedPath(nestedAttributes.getSegments());
|
||||
|
||||
return Collections.singletonList(filter);
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import static org.neo4j.ogm.cypher.ComparisonOperator.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.neo4j.ogm.cypher.PropertyValueTransformer;
|
||||
import org.neo4j.ogm.cypher.function.FilterFunction;
|
||||
|
||||
/**
|
||||
* This is a specialised filter function taking care of filtering native id properties.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
* @soundtrack Freddie Mercury - Never Boring
|
||||
*/
|
||||
final class NativeIdFilterFunction implements FilterFunction<Object> {
|
||||
|
||||
// This function belongs somewhat more into OGM than SDN. The reason having it here is simple: The filter is build
|
||||
// explicitly and not via reflection and we don't want to have yet another shim managing separate possible versions
|
||||
// OGM like we already have with the embedded support, entity instantiator and some other things. ^ms
|
||||
|
||||
protected static final String PARAMETER_NAME = "idProperty";
|
||||
|
||||
private final ComparisonOperator operator;
|
||||
private final Object value;
|
||||
private Filter filter;
|
||||
|
||||
NativeIdFilterFunction(ComparisonOperator operator, Object value) {
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expression(String nodeIdentifier, String filteredProperty,
|
||||
UnaryOperator<String> createUniqueParameterName) {
|
||||
|
||||
switch (operator) {
|
||||
case EQUALS:
|
||||
case GREATER_THAN:
|
||||
case GREATER_THAN_EQUAL:
|
||||
case LESS_THAN:
|
||||
case LESS_THAN_EQUAL:
|
||||
case IN:
|
||||
return String.format("id(%s) %s $`%s` ", nodeIdentifier, operator.getValue(),
|
||||
createUniqueParameterName.apply(PARAMETER_NAME));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported comparision operator for an ID attribute.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> parameters(UnaryOperator<String> createUniqueParameterName,
|
||||
PropertyValueTransformer valueTransformer) {
|
||||
|
||||
Map<String, Object> map = Collections.emptyMap();
|
||||
if (EnumSet.of(EQUALS, GREATER_THAN, GREATER_THAN_EQUAL, LESS_THAN, LESS_THAN_EQUAL, IN).contains(operator)) {
|
||||
map = Collections.singletonMap(createUniqueParameterName.apply(PARAMETER_NAME),
|
||||
valueTransformer.transformPropertyValue(this.value));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Map<String, Object> parameters() {
|
||||
throw new UnsupportedOperationException(
|
||||
"This method has been kept here for backward compatiblity and should never be called.");
|
||||
}
|
||||
|
||||
public String expression(String s) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This method has been kept here for backward compatiblity and should never be called.");
|
||||
}
|
||||
|
||||
public Filter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void setFilter(Filter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
|
||||
import static org.springframework.data.repository.query.parser.Part.Type.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.neo4j.ogm.cypher.BooleanOperator;
|
||||
import org.neo4j.ogm.cypher.ComparisonOperator;
|
||||
import org.neo4j.ogm.cypher.Filter;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Jasper Blues
|
||||
* @author Nicolas Mervaillie
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class PropertyComparisonBuilder extends FilterBuilder {
|
||||
|
||||
PropertyComparisonBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
|
||||
super(part, booleanOperator, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> build(Stack<Object> params) {
|
||||
|
||||
NestedAttributes nestedAttributes = getNestedAttributes(part);
|
||||
|
||||
Object value = params.pop();
|
||||
|
||||
Filter filter;
|
||||
String propertyName = nestedAttributes.isEmpty() ? propertyName() : nestedAttributes.getLeafPropertySegment();
|
||||
if (isInternalIdProperty.test(part)) {
|
||||
filter = new Filter(new NativeIdFilterFunction(convertToComparisonOperator(part.getType()), value));
|
||||
filter.setPropertyName(propertyName);
|
||||
} else {
|
||||
filter = new Filter(propertyName, convertToComparisonOperator(part.getType()), value);
|
||||
}
|
||||
filter.setOwnerEntityType(entityType);
|
||||
filter.setBooleanOperator(booleanOperator);
|
||||
filter.setNegated(isNegated());
|
||||
filter.setNestedPath(nestedAttributes.getSegments());
|
||||
applyCaseInsensitivityIfShouldIgnoreCase(part, filter);
|
||||
|
||||
return Collections.singletonList(filter);
|
||||
}
|
||||
|
||||
private ComparisonOperator convertToComparisonOperator(Part.Type type) {
|
||||
|
||||
switch (type) {
|
||||
case AFTER:
|
||||
case GREATER_THAN:
|
||||
return ComparisonOperator.GREATER_THAN;
|
||||
case GREATER_THAN_EQUAL:
|
||||
return ComparisonOperator.GREATER_THAN_EQUAL;
|
||||
case BEFORE:
|
||||
case LESS_THAN:
|
||||
return ComparisonOperator.LESS_THAN;
|
||||
case LESS_THAN_EQUAL:
|
||||
return ComparisonOperator.LESS_THAN_EQUAL;
|
||||
case REGEX:
|
||||
return ComparisonOperator.MATCHES;
|
||||
case LIKE:
|
||||
case NOT_LIKE:
|
||||
return ComparisonOperator.LIKE;
|
||||
case STARTING_WITH:
|
||||
return ComparisonOperator.STARTING_WITH;
|
||||
case ENDING_WITH:
|
||||
return ComparisonOperator.ENDING_WITH;
|
||||
case CONTAINING:
|
||||
case NOT_CONTAINING:
|
||||
return ComparisonOperator.CONTAINING;
|
||||
case IN:
|
||||
case NOT_IN:
|
||||
return ComparisonOperator.IN;
|
||||
case SIMPLE_PROPERTY:
|
||||
return ComparisonOperator.EQUALS;
|
||||
default:
|
||||
throw new IllegalArgumentException("No ComparisonOperator for Part.Type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filter to ignore the case in case the underlying {@link Part} requires ignoring case and the property
|
||||
* actually supports it.
|
||||
*
|
||||
* @param part
|
||||
* @param filter
|
||||
*/
|
||||
private void applyCaseInsensitivityIfShouldIgnoreCase(Part part, Filter filter) {
|
||||
|
||||
switch (part.shouldIgnoreCase()) {
|
||||
case ALWAYS:
|
||||
Assert.state(canIgnoreCase(part), "Unable to ignore case of " + part.getProperty().getLeafType().getName()
|
||||
+ " types, the property '" + part.getProperty().getSegment() + "' must reference a String");
|
||||
filter.ignoreCase();
|
||||
break;
|
||||
case WHEN_POSSIBLE:
|
||||
if (canIgnoreCase(part)) {
|
||||
filter.ignoreCase();
|
||||
}
|
||||
break;
|
||||
case NEVER:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canIgnoreCase(Part part) {
|
||||
return isSupportedIgnoreKeyword(part) && String.class.equals(part.getProperty().getLeafType());
|
||||
}
|
||||
|
||||
private boolean isSupportedIgnoreKeyword(Part part) {
|
||||
Part.Type type = part.getType();
|
||||
return type == SIMPLE_PROPERTY || type == CONTAINING;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Support for creating Cypher queries through Neo4j-OGMs filter mechanism. Not to be meant as part of an public api.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.filter;
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Query implementation to execute queries against Neo4j.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.spel;
|
||||
|
||||
public class Neo4jQueryPlaceholderSupplier implements PlaceholderSupplier {
|
||||
|
||||
private static final String PLACEHOLDER = "spel_expr";
|
||||
private int index = 0;
|
||||
|
||||
@Override
|
||||
public String nextPlaceholder() {
|
||||
return parameterName(index++);
|
||||
}
|
||||
|
||||
public String parameterName(int index) {
|
||||
return PLACEHOLDER + index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decoratePlaceholder(String placeholder) {
|
||||
return "$" + placeholder;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.spel;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.SpelEvaluator;
|
||||
import org.springframework.data.repository.query.SpelQueryContext;
|
||||
import org.springframework.data.repository.query.SpelQueryContext.EvaluatingSpelQueryContext;
|
||||
|
||||
public class ParameterizedQuery {
|
||||
|
||||
private final Parameters<?, ?> methodParameters;
|
||||
private final SpelEvaluator spelEvaluator;
|
||||
|
||||
private ParameterizedQuery(Parameters<?, ?> methodParameters, SpelEvaluator spelEvaluator) {
|
||||
this.methodParameters = methodParameters;
|
||||
this.spelEvaluator = spelEvaluator;
|
||||
}
|
||||
|
||||
public static ParameterizedQuery getParameterizedQuery(String queryString, Parameters<?, ?> methodParameters,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Neo4jQueryPlaceholderSupplier supplier = new Neo4jQueryPlaceholderSupplier();
|
||||
|
||||
EvaluatingSpelQueryContext context = SpelQueryContext.of( //
|
||||
(index, prefix) -> supplier.parameterName(index), //
|
||||
(prefix, name) -> supplier.decoratePlaceholder(name)) //
|
||||
.withEvaluationContextProvider(evaluationContextProvider);
|
||||
|
||||
SpelEvaluator evaluator = context.parse(queryString, methodParameters);
|
||||
return new ParameterizedQuery(methodParameters, evaluator);
|
||||
}
|
||||
|
||||
public Map<String, Object> resolveParameter(Object[] parameters,
|
||||
BiFunction<Parameters<?, ?>, Object[], Map<String, Object>> nativePlaceholderFunction) {
|
||||
|
||||
Map<String, Object> parameterValues = spelEvaluator.evaluate(parameters);
|
||||
Map<String, Object> nativeParameterValues = nativePlaceholderFunction.apply(methodParameters, parameters);
|
||||
parameterValues.putAll(nativeParameterValues);
|
||||
return parameterValues;
|
||||
}
|
||||
|
||||
public String getQueryString() {
|
||||
return spelEvaluator.getQueryString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.spel;
|
||||
|
||||
interface PlaceholderSupplier {
|
||||
String nextPlaceholder();
|
||||
|
||||
String decoratePlaceholder(String placeholder);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Spring Expression Language (SpEL) support.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query.spel;
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.neo4j.ogm.metadata.ClassInfo;
|
||||
import org.neo4j.ogm.metadata.FieldInfo;
|
||||
import org.neo4j.ogm.metadata.MetaData;
|
||||
import org.springframework.data.repository.core.support.AbstractEntityInformation;
|
||||
|
||||
/**
|
||||
* @author Mark Angrish
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class GraphEntityInformation<T, ID extends Serializable> extends AbstractEntityInformation<T, ID> {
|
||||
|
||||
private final MetaData metaData;
|
||||
|
||||
public GraphEntityInformation(MetaData metaData, Class<T> type) {
|
||||
super(type);
|
||||
this.metaData = metaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ID getId(T entity) {
|
||||
final ClassInfo classInfo = metaData.classInfo(getJavaType().getName());
|
||||
final FieldInfo primaryIndex = classInfo.primaryIndexField();
|
||||
|
||||
return (ID) getId(entity, classInfo, primaryIndex);
|
||||
}
|
||||
|
||||
private Object getId(T entity, ClassInfo classInfo, FieldInfo primaryIndex) {
|
||||
if (primaryIndex != null) {
|
||||
return classInfo.propertyField(primaryIndex.getName()).readProperty(entity);
|
||||
} else {
|
||||
return classInfo.propertyField(classInfo.identityField().getName()).readProperty(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<ID> getIdType() {
|
||||
final FieldInfo primaryIndex = metaData.classInfo(getJavaType().getName()).primaryIndexField();
|
||||
|
||||
if (primaryIndex != null) {
|
||||
return (Class<ID>) primaryIndex.convertedType();
|
||||
}
|
||||
return (Class<ID>) Long.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
|
||||
import org.springframework.data.neo4j.repository.config.Neo4jAuditingEventListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class Neo4jAuditingBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectFactory<IsNewAwareAuditingHandler> isNewAwareHandler;
|
||||
|
||||
public Neo4jAuditingBeanPostProcessor(ObjectFactory<IsNewAwareAuditingHandler> isNewAwareHandler) {
|
||||
|
||||
Assert.notNull(isNewAwareHandler, "IsNewAwareHandler must not be null!");
|
||||
|
||||
this.isNewAwareHandler = isNewAwareHandler;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (!SessionFactory.class.isInstance(bean)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
SessionFactory sessionFactory = (SessionFactory) bean;
|
||||
sessionFactory.register(new Neo4jAuditingEventListener(isNewAwareHandler));
|
||||
|
||||
return sessionFactory;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.neo4j.transaction.SessionFactoryUtils;
|
||||
|
||||
/**
|
||||
* A PersistenceExceptionTranslator to get picked up by the Spring exception translation infrastructure.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
public class Neo4jPersistenceExceptionTranslator implements PersistenceExceptionTranslator {
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return SessionFactoryUtils.convertOgmAccessException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.neo4j.ogm.session.Neo4jSession;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.repository.query.GraphQueryLookupStrategy;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Neo4j OGM specific generic repository factory.
|
||||
*
|
||||
* @author Vince Bickers
|
||||
* @author Luanne Misquitta
|
||||
* @author Mark Angrish
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Neo4jRepositoryFactory.class);
|
||||
|
||||
private final Session session;
|
||||
|
||||
private @Nullable final MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> mappingContext;
|
||||
|
||||
/**
|
||||
* @param session
|
||||
* @deprecated since 5.1.0, use {@link Neo4jRepositoryFactory#Neo4jRepositoryFactory(Session, MappingContext)} instead
|
||||
* and provide the mapping context.
|
||||
*/
|
||||
@Deprecated
|
||||
public Neo4jRepositoryFactory(Session session) {
|
||||
this(session, null);
|
||||
}
|
||||
|
||||
public Neo4jRepositoryFactory(Session session, MappingContext<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> mappingContext) {
|
||||
Assert.notNull(session, "Session must not be null!");
|
||||
|
||||
this.session = session;
|
||||
if (mappingContext != null) {
|
||||
this.mappingContext = mappingContext;
|
||||
} else if(session instanceof Neo4jSession) {
|
||||
logger.debug("Creating a new mapping context");
|
||||
this.mappingContext = new Neo4jMappingContext(((Neo4jSession) session).metaData());
|
||||
((Neo4jMappingContext) this.mappingContext).initialize();
|
||||
} else {
|
||||
logger.warn("No mapping context present, some operations won't support persistence constructors");
|
||||
this.mappingContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#setBeanClassLoader(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
super.setBeanClassLoader(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> type) {
|
||||
Assert.notNull(type, "Domain class must not be null!");
|
||||
|
||||
return new GraphEntityInformation(((Neo4jSession) session).metaData(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getTargetRepository(RepositoryInformation information) {
|
||||
return getTargetRepositoryViaReflection(information, information.getDomainType(), session);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> getRepositoryBaseClass(RepositoryMetadata repositoryMetadata) {
|
||||
return SimpleNeo4jRepository.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
return Optional.of(new GraphQueryLookupStrategy(session, evaluationContextProvider, this.mappingContext));
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Special adapter for Springs {@link org.springframework.beans.factory.FactoryBean} interface to allow easy setup of
|
||||
* repository factories via Spring configuration.
|
||||
*
|
||||
* @param <T> the type of the repository
|
||||
* @author Vince Bickers
|
||||
* @author Luanne Misquitta
|
||||
* @author Mark Angrish
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
|
||||
extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> {
|
||||
|
||||
private Session session;
|
||||
private Neo4jMappingContext mappingContext;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Neo4jRepositoryFactoryBean} for the given repository interface.
|
||||
*
|
||||
* @param repositoryInterface must not be {@literal null}.
|
||||
*/
|
||||
public Neo4jRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
|
||||
super(repositoryInterface);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setSession(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#setMappingContext(org.springframework.data.mapping.context.MappingContext)
|
||||
*/
|
||||
@Override
|
||||
public void setMappingContext(MappingContext<?, ?> mappingContext) {
|
||||
super.setMappingContext(mappingContext);
|
||||
|
||||
if (mappingContext instanceof Neo4jMappingContext) {
|
||||
this.mappingContext = (Neo4jMappingContext) mappingContext;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(session, "Session must not be null!");
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport#doCreateRepositoryFactory()
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport doCreateRepositoryFactory() {
|
||||
return createRepositoryFactory(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link RepositoryFactorySupport}.
|
||||
*
|
||||
* @deprecated since 5.1.0, will be removed in 5.2.x.
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
protected RepositoryFactorySupport createRepositoryFactory(Session session) {
|
||||
return new Neo4jRepositoryFactory(session, mappingContext);
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.neo4j.ogm.cypher.query.Pagination;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.repository.Neo4jRepository;
|
||||
import org.springframework.data.neo4j.util.PagingAndSortingUtils;
|
||||
import org.springframework.data.repository.support.PageableExecutionUtils;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer
|
||||
* you a more sophisticated interface than the plain {@link Session} .
|
||||
*
|
||||
* @param <T> the type of the entity to handle
|
||||
* @author Vince Bickers
|
||||
* @author Luanne Misquitta
|
||||
* @author Mark Angrish
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Gerrit Meier
|
||||
*/
|
||||
@Repository
|
||||
@Transactional(readOnly = true)
|
||||
public class SimpleNeo4jRepository<T, ID extends Serializable> implements Neo4jRepository<T, ID> {
|
||||
|
||||
private static final int DEFAULT_QUERY_DEPTH = 1;
|
||||
private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";
|
||||
|
||||
private final Class<T> clazz;
|
||||
private final Session session;
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleNeo4jRepository} to manage objects of the given domain type.
|
||||
*
|
||||
* @param domainClass must not be {@literal null}.
|
||||
* @param session must not be {@literal null}.
|
||||
*/
|
||||
public SimpleNeo4jRepository(Class<T> domainClass, Session session) {
|
||||
Assert.notNull(domainClass, "Domain class must not be null!");
|
||||
Assert.notNull(session, "Session must not be null!");
|
||||
|
||||
this.clazz = domainClass;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public <S extends T> S save(S entity) {
|
||||
session.save(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public <S extends T> Iterable<S> saveAll(Iterable<S> entities) {
|
||||
session.save(entities);
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> findById(ID id) {
|
||||
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
|
||||
return Optional.ofNullable(session.load(clazz, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsById(ID id) {
|
||||
return findById(id).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return session.countEntitiesOfType(clazz);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void deleteById(ID id) {
|
||||
findById(id).ifPresent(session::delete);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void delete(T t) {
|
||||
session.delete(t);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void deleteAll(Iterable<? extends T> ts) {
|
||||
for (T t : ts) {
|
||||
session.delete(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
session.deleteAll(clazz);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public <S extends T> S save(S s, int depth) {
|
||||
session.save(s, depth);
|
||||
return s;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public <S extends T> Iterable<S> save(Iterable<S> ses, int depth) {
|
||||
session.save(ses, depth);
|
||||
return ses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> findById(ID id, int depth) {
|
||||
return Optional.ofNullable(session.load(clazz, id, depth));
|
||||
}
|
||||
|
||||
// findAll and variants
|
||||
@Override
|
||||
public Iterable<T> findAll() {
|
||||
return findAll(DEFAULT_QUERY_DEPTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAll(int depth) {
|
||||
return session.loadAll(clazz, depth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAllById(Iterable<ID> longs) {
|
||||
return findAllById(longs, DEFAULT_QUERY_DEPTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAllById(Iterable<ID> ids, int depth) {
|
||||
return session.loadAll(clazz, (Collection<ID>) ids, depth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAll(Sort sort) {
|
||||
return findAll(sort, DEFAULT_QUERY_DEPTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAll(Sort sort, int depth) {
|
||||
return session.loadAll(clazz, PagingAndSortingUtils.convert(sort), depth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAllById(Iterable<ID> ids, Sort sort) {
|
||||
return findAllById(ids, sort, DEFAULT_QUERY_DEPTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAllById(Iterable<ID> ids, Sort sort, int depth) {
|
||||
return session.loadAll(clazz, (Collection<ID>) ids, PagingAndSortingUtils.convert(sort), depth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Pageable pageable) {
|
||||
return findAll(pageable, DEFAULT_QUERY_DEPTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Pageable pageable, int depth) {
|
||||
Pagination pagination = new Pagination(pageable.getPageNumber(), pageable.getPageSize());
|
||||
Collection<T> data = session.loadAll(clazz, PagingAndSortingUtils.convert(pageable.getSort()), pagination, depth);
|
||||
|
||||
return PageableExecutionUtils.getPage(new ArrayList<>(data), pageable, () -> session.countEntitiesOfType(clazz));
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Neo4j repository implementations.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
@@ -1,426 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.transaction;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.neo4j.ogm.transaction.Transaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.neo4j.bookmark.BookmarkInfo;
|
||||
import org.springframework.data.neo4j.bookmark.BookmarkManager;
|
||||
import org.springframework.data.neo4j.bookmark.BookmarkSupport;
|
||||
import org.springframework.transaction.CannotCreateTransactionException;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.InvalidIsolationLevelException;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
import org.springframework.transaction.support.ResourceTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* {@link PlatformTransactionManager} implementation for a single Neo4j OGM {@link SessionFactory}. Binds a Neo4j OGM
|
||||
* Session from the specified factory to the thread, potentially allowing for one thread-bound Session per factory.
|
||||
* {@link SharedSessionCreator} is aware of thread-bound session and participates in such transactions automatically. It
|
||||
* is required for Neo4j OGM access code supporting this transaction management mechanism.
|
||||
* <p>
|
||||
* This transaction manager is appropriate for applications that use a single Neo4j OGM SessionFactory for transactional
|
||||
* data access. JTA (usually through {@link org.springframework.transaction.jta.JtaTransactionManager}) has not been
|
||||
* tested or considered at the moment.
|
||||
* <p>
|
||||
* This transaction manager does not support nested transactions or requires new propagation.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @see #setSessionFactory
|
||||
*/
|
||||
public class Neo4jTransactionManager extends AbstractPlatformTransactionManager
|
||||
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Neo4jTransactionManager.class);
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
private BookmarkManager bookmarkManager;
|
||||
|
||||
/**
|
||||
* Create a new Neo4jTransactionManager instance.
|
||||
* <p>
|
||||
* An SessionFactory has to be set to be able to use it.
|
||||
*
|
||||
* @see #setSessionFactory(SessionFactory)
|
||||
*/
|
||||
public Neo4jTransactionManager() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Neo4jTransactionManager instance.
|
||||
*
|
||||
* @param sessionFactory SessionFactory to manage transactions for
|
||||
*/
|
||||
public Neo4jTransactionManager(SessionFactory sessionFactory) {
|
||||
|
||||
setTransactionSynchronization(SYNCHRONIZATION_ON_ACTUAL_TRANSACTION);
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SessionFactory that this instance should manage transactions for.
|
||||
* <p>
|
||||
* By default, a default SessionFactory will be retrieved by finding a single unique bean of type SessionFactory in
|
||||
* the containing BeanFactory.
|
||||
*/
|
||||
public void setSessionFactory(SessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SessionFactory that this instance should manage transactions for.
|
||||
*/
|
||||
public SessionFactory getSessionFactory() {
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a default SessionFactory bean.
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (getSessionFactory() == null) {
|
||||
setSessionFactory(beanFactory.getBean(SessionFactory.class));
|
||||
}
|
||||
try {
|
||||
bookmarkManager = beanFactory.getBean(BookmarkManager.class);
|
||||
logger.debug("Found BookmarkManager {}", bookmarkManager);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
logger.debug("No BookmarkManager bean found, bookmark management not enabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (getSessionFactory() == null) {
|
||||
throw new IllegalArgumentException("'sessionFactory' is required");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResourceFactory() {
|
||||
return getSessionFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doGetTransaction() {
|
||||
Neo4jTransactionObject txObject = new Neo4jTransactionObject();
|
||||
|
||||
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
|
||||
if (sessionHolder != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Neo4j OGM transaction");
|
||||
}
|
||||
txObject.setSessionHolder(sessionHolder, false);
|
||||
}
|
||||
return txObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistingTransaction(Object transaction) {
|
||||
return ((Neo4jTransactionObject) transaction).hasTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
|
||||
Neo4jTransactionObject txObject = (Neo4jTransactionObject) transaction;
|
||||
|
||||
try {
|
||||
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
|
||||
Session session = sessionFactory.openSession();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opened new Session [" + session + "] for Neo4j OGM transaction");
|
||||
}
|
||||
txObject.setSessionHolder(new SessionHolder(session), true);
|
||||
}
|
||||
|
||||
Session session = txObject.getSessionHolder().getSession();
|
||||
|
||||
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
|
||||
// We should set a specific isolation level but are not allowed to...
|
||||
throw new InvalidIsolationLevelException(
|
||||
"Neo4jTransactionManager is not allowed to support custom isolation levels.");
|
||||
}
|
||||
|
||||
if (definition.getPropagationBehavior() != TransactionDefinition.PROPAGATION_REQUIRED) {
|
||||
throw new IllegalTransactionStateException("Neo4jTransactionManager only supports 'required' propagation.");
|
||||
}
|
||||
|
||||
Transaction.Type type = getTransactionType(definition, txObject);
|
||||
Transaction transactionData = session.beginTransaction(type, getBookmarks());
|
||||
|
||||
txObject.setTransactionData(transactionData);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Beginning Transaction [" + transactionData + "] on Session [" + session + "]");
|
||||
}
|
||||
|
||||
// Bind the session holder to the thread.
|
||||
if (txObject.isNewSessionHolder()) {
|
||||
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
|
||||
}
|
||||
|
||||
if (definition.isReadOnly()) {
|
||||
TransactionSynchronizationManager.setCurrentTransactionReadOnly(true);
|
||||
}
|
||||
|
||||
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
|
||||
} catch (TransactionException ex) {
|
||||
closeSessionAfterFailedBegin(txObject);
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
closeSessionAfterFailedBegin(txObject);
|
||||
throw new CannotCreateTransactionException("Could not open Neo4j Session for transaction", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Transaction.Type getTransactionType(TransactionDefinition definition, Neo4jTransactionObject txObject) {
|
||||
Transaction.Type type;
|
||||
if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
|
||||
type = Transaction.Type.READ_ONLY;
|
||||
} else if (txObject.transactionData != null) {
|
||||
type = txObject.transactionData.type();
|
||||
} else {
|
||||
type = Transaction.Type.READ_WRITE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private Iterable<String> getBookmarks() {
|
||||
BookmarkInfo bookmarkInfo = BookmarkSupport.currentBookmarkInfo();
|
||||
if (bookmarkInfo != null && bookmarkInfo.shouldUseBookmark()) {
|
||||
if (bookmarkManager != null) {
|
||||
Collection<String> bookmarks = bookmarkManager.getBookmarks();
|
||||
bookmarkInfo.setBookmarks(bookmarks);
|
||||
return bookmarks;
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("Configured @UseBookmark but no BookmarkManager bean found.");
|
||||
}
|
||||
} else {
|
||||
return emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current transaction's Session. Called after a transaction begin attempt failed.
|
||||
*
|
||||
* @param txObject the current transaction
|
||||
*/
|
||||
private void closeSessionAfterFailedBegin(Neo4jTransactionObject txObject) {
|
||||
if (txObject.isNewSessionHolder()) {
|
||||
Session session = txObject.getSessionHolder().getSession();
|
||||
try {
|
||||
if (session.getTransaction() != null) {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
logger.debug("Could not rollback Session after failed transaction begin", ex);
|
||||
} finally {
|
||||
txObject.setSessionHolder(null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doSuspend(Object transaction) {
|
||||
Neo4jTransactionObject txObject = (Neo4jTransactionObject) transaction;
|
||||
txObject.setSessionHolder(null, false);
|
||||
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
return new SuspendedResourcesHolder(sessionHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doResume(Object transaction, Object suspendedResources) {
|
||||
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
|
||||
if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
|
||||
// From non-transactional code running in active transaction synchronization
|
||||
// -> can be safely removed, will be closed on transaction completion.
|
||||
TransactionSynchronizationManager.unbindResource(getSessionFactory());
|
||||
}
|
||||
TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCommit(DefaultTransactionStatus status) {
|
||||
|
||||
Neo4jTransactionObject txObject = (Neo4jTransactionObject) status.getTransaction();
|
||||
Session session = txObject.getSessionHolder().getSession();
|
||||
|
||||
try (Transaction tx = session.getTransaction()) {
|
||||
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Committing Neo4j OGM transaction [" + tx + "] on Session [" + session + "]");
|
||||
}
|
||||
|
||||
if (tx != null) {
|
||||
tx.commit();
|
||||
|
||||
if (bookmarkManager != null) {
|
||||
String lastBookmark = session.getLastBookmark();
|
||||
|
||||
BookmarkInfo bookmarkInfo = BookmarkSupport.currentBookmarkInfo();
|
||||
Collection<String> bookmarks;
|
||||
if (bookmarkInfo != null && bookmarkInfo.getBookmarks() != null) {
|
||||
bookmarks = bookmarkInfo.getBookmarks();
|
||||
} else {
|
||||
bookmarks = emptySet();
|
||||
}
|
||||
logger.debug("Found bookmarks {}, replacing with {}", bookmarks, lastBookmark);
|
||||
bookmarkManager.storeBookmark(lastBookmark, bookmarks);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
DataAccessException dae = SessionFactoryUtils.convertOgmAccessException(ex);
|
||||
throw (dae != null ? dae : ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRollback(DefaultTransactionStatus status) {
|
||||
Neo4jTransactionObject txObject = (Neo4jTransactionObject) status.getTransaction();
|
||||
Session session = txObject.getSessionHolder().getSession();
|
||||
|
||||
try (Transaction tx = session.getTransaction()) {
|
||||
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Rolling back Neo4j transaction [" + tx + "] on Session [" + session + "]");
|
||||
}
|
||||
|
||||
if (tx != null) {
|
||||
tx.rollback();
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
DataAccessException dae = SessionFactoryUtils.convertOgmAccessException(ex);
|
||||
throw (dae != null ? dae : ex);
|
||||
} finally {
|
||||
if (!txObject.isNewSessionHolder()) {
|
||||
// Clear all pending inserts/updates/deletes in the Session.
|
||||
session.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
|
||||
Neo4jTransactionObject txObject = (Neo4jTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug(
|
||||
"Setting Neo4j OGM transaction on Session [" + txObject.getSessionHolder().getSession() + "] rollback-only");
|
||||
}
|
||||
status.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCleanupAfterCompletion(Object transaction) {
|
||||
Neo4jTransactionObject txObject = (Neo4jTransactionObject) transaction;
|
||||
|
||||
// Remove the session holder from the thread, if still there.
|
||||
if (txObject.isNewSessionHolder()) {
|
||||
TransactionSynchronizationManager.unbindResourceIfPossible(getSessionFactory());
|
||||
}
|
||||
|
||||
Transaction rawTransaction = txObject.getTransactionData();
|
||||
|
||||
if (rawTransaction != null && rawTransaction.status().equals(Transaction.Status.OPEN)) {
|
||||
rawTransaction.close();
|
||||
}
|
||||
|
||||
// Remove the session holder from the thread.
|
||||
if (txObject.isNewSessionHolder()) {
|
||||
Session session = txObject.getSessionHolder().getSession();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Closing Neo4j Session [" + session + "] after transaction");
|
||||
}
|
||||
} else {
|
||||
logger.debug("Not closing pre-bound Neo4j Session after transaction");
|
||||
}
|
||||
|
||||
txObject.getSessionHolder().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Neo4j OGM transaction object, representing a SessionHolder. Used as transaction object by Neo4jTransactionManager.
|
||||
*/
|
||||
private static class Neo4jTransactionObject {
|
||||
|
||||
private SessionHolder sessionHolder;
|
||||
|
||||
private boolean newSessionHolder;
|
||||
|
||||
private Transaction transactionData;
|
||||
|
||||
void setSessionHolder(SessionHolder sessionHolder, boolean newSessionHolder) {
|
||||
this.sessionHolder = sessionHolder;
|
||||
this.newSessionHolder = newSessionHolder;
|
||||
}
|
||||
|
||||
SessionHolder getSessionHolder() {
|
||||
return this.sessionHolder;
|
||||
}
|
||||
|
||||
boolean isNewSessionHolder() {
|
||||
return this.newSessionHolder;
|
||||
}
|
||||
|
||||
boolean hasTransaction() {
|
||||
return (this.sessionHolder != null && this.sessionHolder.isTransactionActive());
|
||||
}
|
||||
|
||||
void setTransactionData(Transaction rawTransaction) {
|
||||
this.transactionData = rawTransaction;
|
||||
this.sessionHolder.setTransactionActive(true);
|
||||
}
|
||||
|
||||
Transaction getTransactionData() {
|
||||
return this.transactionData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for suspended resources. Used internally by {@code doSuspend} and {@code doResume}.
|
||||
*/
|
||||
private static class SuspendedResourcesHolder {
|
||||
|
||||
private final SessionHolder sessionHolder;
|
||||
|
||||
private SuspendedResourcesHolder(SessionHolder sessionHolder) {
|
||||
this.sessionHolder = sessionHolder;
|
||||
}
|
||||
|
||||
private SessionHolder getSessionHolder() {
|
||||
return this.sessionHolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.transaction;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.neo4j.ogm.autoindex.MissingIndexException;
|
||||
import org.neo4j.ogm.exception.ConnectionException;
|
||||
import org.neo4j.ogm.exception.CypherException;
|
||||
import org.neo4j.ogm.exception.ResultProcessingException;
|
||||
import org.neo4j.ogm.exception.TransactionException;
|
||||
import org.neo4j.ogm.exception.core.InvalidDepthException;
|
||||
import org.neo4j.ogm.exception.core.MappingException;
|
||||
import org.neo4j.ogm.exception.core.MetadataException;
|
||||
import org.neo4j.ogm.exception.core.MissingOperatorException;
|
||||
import org.neo4j.ogm.exception.core.TransactionManagerException;
|
||||
import org.neo4j.ogm.exception.core.UnknownStatementTypeException;
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.TypeMismatchDataAccessException;
|
||||
import org.springframework.data.neo4j.exception.Neo4jErrorStatusCodes;
|
||||
import org.springframework.data.neo4j.exception.UncategorizedNeo4jException;
|
||||
import org.springframework.transaction.support.ResourceHolderSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for Neo4j OGM Session handling, allowing for reuse of Session instances within
|
||||
* transactions. Also provides support for exception translation.
|
||||
* <p>
|
||||
* Mainly intended for internal use within the framework.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class SessionFactoryUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SessionFactoryUtils.class);
|
||||
|
||||
/**
|
||||
* @deprecated since 5.2, this has been a Noop since 4.2 and has no meaning since then. It will be removed in the next major version.
|
||||
* @param session
|
||||
*/
|
||||
@Deprecated
|
||||
public static void closeSession(Session session) {}
|
||||
|
||||
public static Session getSession(SessionFactory sessionFactory) throws IllegalStateException {
|
||||
|
||||
Assert.notNull(sessionFactory, "No SessionFactory specified");
|
||||
|
||||
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
|
||||
if (sessionHolder != null) {
|
||||
if (!sessionHolder.isSynchronizedWithTransaction()
|
||||
&& TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
sessionHolder.setSynchronizedWithTransaction(true);
|
||||
TransactionSynchronizationManager
|
||||
.registerSynchronization(new SessionSynchronization(sessionHolder, sessionFactory, false));
|
||||
}
|
||||
return sessionHolder.getSession();
|
||||
}
|
||||
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Session session = sessionFactory.openSession();
|
||||
|
||||
logger.debug("Registering transaction synchronization for Neo4j Session");
|
||||
// Use same Session for further Neo4j actions within the transaction.
|
||||
// Thread object will get removed by synchronization at transaction completion.
|
||||
|
||||
sessionHolder = new SessionHolder(session);
|
||||
sessionHolder.setSynchronizedWithTransaction(true);
|
||||
TransactionSynchronizationManager
|
||||
.registerSynchronization(new SessionSynchronization(sessionHolder, sessionFactory, true));
|
||||
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given runtime exception to an appropriate exception from the {@code org.springframework.dao} hierarchy.
|
||||
* Return null if no translation is appropriate: any other exception may have resulted from user code, and should not
|
||||
* be translated.
|
||||
*
|
||||
* @param ex runtime exception that occurred
|
||||
* @return the corresponding DataAccessException instance, or {@code null} if the exception should not be translated
|
||||
*/
|
||||
public static DataAccessException convertOgmAccessException(RuntimeException ex) {
|
||||
|
||||
// TODO: The OGM should not be throwing common runtime exceptions as these mask user errors.
|
||||
// TODO: Instead we should be defining our own Exceptions that can then be translated into Spring Exceptions.
|
||||
if (ex instanceof IllegalStateException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof InvalidDepthException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof ResultProcessingException) {
|
||||
return new DataRetrievalFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof MappingException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof MetadataException) {
|
||||
return new TypeMismatchDataAccessException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof UnknownStatementTypeException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof ConnectionException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof MissingOperatorException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof TransactionException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof TransactionManagerException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof MissingIndexException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
// All exceptions coming back from the database.
|
||||
if (ex instanceof CypherException) {
|
||||
String code = ((CypherException) ex).getCode();
|
||||
final Class<? extends DataAccessException> dae = Neo4jErrorStatusCodes.translate(code);
|
||||
|
||||
if (dae != null) {
|
||||
try {
|
||||
final Constructor<? extends DataAccessException> constructor = dae.getDeclaredConstructor(String.class,
|
||||
Throwable.class);
|
||||
return constructor.newInstance(ex.getMessage(), ex);
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException
|
||||
| InvocationTargetException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new UncategorizedNeo4jException(ex.getMessage(), ex);
|
||||
}
|
||||
// If we get here, we have an exception that resulted from user code,
|
||||
// rather than the persistence provider, so we return null to indicate
|
||||
// that translation should not occur.
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class SessionSynchronization extends ResourceHolderSynchronization<SessionHolder, SessionFactory>
|
||||
implements Ordered {
|
||||
|
||||
private final boolean newSession;
|
||||
|
||||
SessionSynchronization(SessionHolder sessionHolder, SessionFactory sessionFactory, boolean newSession) {
|
||||
super(sessionHolder, sessionFactory);
|
||||
this.newSession = newSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 900;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushResource(SessionHolder resourceHolder) {}
|
||||
|
||||
@Override
|
||||
protected boolean shouldUnbindAtCompletion() {
|
||||
return this.newSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldReleaseAfterCompletion(SessionHolder resourceHolder) {
|
||||
// return !resourceHolder.getSession().isClosed();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.transaction;
|
||||
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.springframework.transaction.support.ResourceHolderSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Holder wrapping a Neo4j OGM Session. Neo4jTransactionManager binds instances of this class to the thread, for a given
|
||||
* SessionFactory.
|
||||
* <p>
|
||||
* Note: This is an SPI class, not intended to be used by applications.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @see Neo4jTransactionManager
|
||||
* @see SessionFactoryUtils
|
||||
*/
|
||||
public class SessionHolder extends ResourceHolderSupport {
|
||||
|
||||
private final Session session;
|
||||
|
||||
private boolean transactionActive;
|
||||
|
||||
public SessionHolder(Session session) {
|
||||
Assert.notNull(session, "Session must not be null");
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public Session getSession() {
|
||||
return this.session;
|
||||
}
|
||||
|
||||
protected void setTransactionActive(boolean transactionActive) {
|
||||
this.transactionActive = transactionActive;
|
||||
}
|
||||
|
||||
protected boolean isTransactionActive() {
|
||||
return this.transactionActive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
this.transactionActive = false;
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.transaction;
|
||||
|
||||
import static org.neo4j.ogm.transaction.Transaction.Status.*;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.neo4j.ogm.session.Session;
|
||||
import org.neo4j.ogm.session.SessionFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.neo4j.mapping.MetaDataProvider;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Delegate for creating a shareable Neo4j OGM {@link Session} reference for a given {@link SessionFactory}.
|
||||
* <p>
|
||||
* A shared Session will behave just like a Session fetched from an application SessionFactory. It will delegate all
|
||||
* calls to the current transactional Session, if any; otherwise it will fall back to a newly created Session per
|
||||
* operation.
|
||||
*
|
||||
* @author Mark Angrish
|
||||
* @author Michael J. Simons
|
||||
* @see Neo4jTransactionManager
|
||||
*/
|
||||
public class SharedSessionCreator {
|
||||
|
||||
private static final Set<String> TRANSACTION_REQUIRING_METHODS;
|
||||
|
||||
static {
|
||||
Set<String> tmp = new HashSet<>();
|
||||
tmp.add("deleteAll");
|
||||
tmp.add("save");
|
||||
tmp.add("delete");
|
||||
tmp.add("purgeDatabase");
|
||||
|
||||
TRANSACTION_REQUIRING_METHODS = Collections.unmodifiableSet(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transactional Session proxy for the given SessionFactory.
|
||||
*
|
||||
* @param sessionFactory SessionFactory to obtain Sessions from as needed {@code createSession} call (may be
|
||||
* {@code null}) Session. Allows the addition or specification of proprietary interfaces.
|
||||
* @return a shareable transactional Session proxy
|
||||
*/
|
||||
public static Session createSharedSession(SessionFactory sessionFactory) {
|
||||
return (Session) Proxy.newProxyInstance(SharedSessionCreator.class.getClassLoader(),
|
||||
new Class<?>[] { Session.class, MetaDataProvider.class }, new SharedSessionInvocationHandler(sessionFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invocation handler that delegates all calls to the current transactional Session, if any; else, it will fall back
|
||||
* to a newly created Session per operation.
|
||||
*/
|
||||
private static class SharedSessionInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final SessionFactory sessionFactory;
|
||||
|
||||
private final Method queryMethod;
|
||||
|
||||
public SharedSessionInvocationHandler(SessionFactory sessionFactory) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
this.queryMethod = ReflectionUtils
|
||||
.findMethod(Session.class, "query", String.class, Map.class, boolean.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
String methodName = method.getName();
|
||||
switch (methodName) {
|
||||
case "equals":
|
||||
// Only consider equal when proxies are identical.
|
||||
return (proxy == args[0]);
|
||||
case "hashCode":
|
||||
// Use hashCode of Session proxy.
|
||||
return hashCode();
|
||||
case "toString":
|
||||
// Deliver toString without touching a target Session.
|
||||
return "Shared Session proxy for target factory [" + sessionFactory + "]";
|
||||
case "getMetaData":
|
||||
return this.sessionFactory.metaData();
|
||||
case "beginTransaction":
|
||||
throw new IllegalStateException(
|
||||
"Not allowed to create transaction on shared Session - "
|
||||
+ "use Spring transactions instead");
|
||||
default:
|
||||
Function<Session, Object> methodCall;
|
||||
if (isGenericQueryMethod(method)) {
|
||||
Object[] newArgs = new Object[args.length + 1];
|
||||
System.arraycopy(args, 0, newArgs, 0, args.length);
|
||||
newArgs[newArgs.length - 1] = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
|
||||
methodCall = targetSession -> ReflectionUtils.invokeMethod(queryMethod, targetSession, newArgs);
|
||||
} else {
|
||||
methodCall = targetSession -> ReflectionUtils.invokeMethod(method, targetSession, args);
|
||||
}
|
||||
return invokeInTransaction(methodName, methodCall);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isGenericQueryMethod(Method method) {
|
||||
|
||||
Parameter[] parameters = method.getParameters();
|
||||
return "query".equals(method.getName()) && method.getParameterCount() == 2 &&
|
||||
parameters[0].getType() == String.class && parameters[1].getType() == Map.class;
|
||||
}
|
||||
|
||||
private Object invokeInTransaction(String methodName, Function<Session, Object> methodCall) {
|
||||
|
||||
// Determine current Session: either the transactional one
|
||||
// managed by the factory or a temporary one for the given invocation.
|
||||
Session targetSession = SessionFactoryUtils.getSession(this.sessionFactory);
|
||||
|
||||
if (TRANSACTION_REQUIRING_METHODS.contains(methodName)) {
|
||||
if (targetSession == null
|
||||
|| (!TransactionSynchronizationManager.isActualTransactionActive()
|
||||
&& targetSession.getTransaction() != null
|
||||
&& EnumSet.of(CLOSED, COMMITTED, ROLLEDBACK)
|
||||
.contains(targetSession.getTransaction().status()))) {
|
||||
throw new IllegalStateException("No Session with actual transaction available "
|
||||
+ "for current thread - cannot reliably process '" + methodName + "' call");
|
||||
}
|
||||
}
|
||||
|
||||
// Regular Session operations.
|
||||
if (targetSession == null) {
|
||||
logger.debug("Creating new Session for shared Session invocation");
|
||||
targetSession = this.sessionFactory.openSession();
|
||||
}
|
||||
|
||||
// Invoke method on current Session.
|
||||
try {
|
||||
return methodCall.apply(targetSession);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Spring Data Neo4js implementation of a {@link org.springframework.transaction.PlatformTransactionManager}.
|
||||
*/
|
||||
package org.springframework.data.neo4j.transaction;
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.util;
|
||||
|
||||
import org.neo4j.ogm.cypher.query.SortOrder;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
public class PagingAndSortingUtils {
|
||||
|
||||
public static SortOrder convert(Sort sort) {
|
||||
|
||||
SortOrder sortOrder = new SortOrder();
|
||||
|
||||
if (sort != Sort.unsorted()) {
|
||||
for (Sort.Order order : sort) {
|
||||
if (order.isAscending()) {
|
||||
sortOrder.add(order.getProperty());
|
||||
} else {
|
||||
sortOrder.add(SortOrder.Direction.DESC, order.getProperty());
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortOrder;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Util classes for iteration and pagination.
|
||||
*/
|
||||
package org.springframework.data.neo4j.util;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user