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:
Michael Simons
2020-07-02 10:19:24 +02:00
committed by Gerrit Meier
parent 915c5278c8
commit 070d664c8a
697 changed files with 6852 additions and 33948 deletions

20
Jenkinsfile vendored
View File

@@ -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} " +

View File

@@ -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 Neo4jRX - 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? Wed 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 doesnt 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].

View File

@@ -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>

View File

@@ -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 .+;$
^.*$

View File

@@ -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>

View File

@@ -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$

View File

@@ -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>

View File

@@ -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
----

View File

@@ -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
View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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 {
}

View File

@@ -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 "";
}

View File

@@ -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 "";
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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()));
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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>&#064;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;
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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>&#064;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"));
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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 {
}
}
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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