如何使用@StartNode 关系查询 Neo4jRepository?
How to query Neo4jRepository using a @StartNode relationship?
我有以下节点:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@NodeEntity
public class Person {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
private LocalDate birthday;
@Email
private String email;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@NodeEntity
public class Skill {
@Id
@GeneratedValue
private Long id;
private String name;
private String description;
}
还有这个RelationshipEntity
:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RelationshipEntity("RATED")
public class SkillRating {
@Id
@GeneratedValue
private Long id;
@Min(0)
@Max(100)
private Integer score;
private LocalDate measurementDate;
@StartNode
private Person person;
@EndNode
private Skill skill;
}
我不想加载我不打算使用的关系,即我不想添加:
@Relationship(type = "RATED")
private Set<SkillRating> skillRatings;
我的 Person
class 定义,以防止我每次加载 Person
时加载这些评级。我想在必要时使用 Repository
方法加载它们。这是我尝试使用我对 JPA 存储库的了解:
@Repository
public interface SkillRatingRepository extends Neo4jRepository<SkillRating, Long> {
List<SkillRating> findAllByPerson(Person person);
}
但是这个方法没有按预期工作,因为它没有找到任何人的评级。我做错了什么?
-- 编辑--
MATCH (p)-[r:RATED]->(skill) WHERE id(p)={personId} RETURN r
我相信这是一个用 Neo4j Cypher 查询语言编写的查询,可以解决我的问题。如何使用我当前的 class 设置以 Repository
方法 "translate" 它?
谢谢提问。
如您所见,我们不支持基于对象的派生查找器方法。 @RelationshipEntity
还有一个额外的限制,派生的查找器方法只针对属性而不是结束或开始节点。
话虽如此,我已经接受了您的项目(域 classes)并为您创建了一个解决方案。因此,域 classes Person
、Skill
和 SkillRating
可以按原样使用。
请这样声明您的SkillRatingRepository
:
import java.util.List;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
public interface SkillRatingRepository extends Neo4jRepository<SkillRating, Long> {
@Query("MATCH (p)-[r:RATED]->(skill) WHERE id(p) = :#{#person.id} RETURN p, r, skill")
List<SkillRating> findAllByPerson(Person person);
}
@Query
表示自定义查询。在该自定义查询中,您可以使用 Spring 表达式语言 (SpEL),如此处所述 https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions.
因此,您将取消引用已通过的人并访问该 ID。与您已经编写的查询非常相似。请注意,您还必须 return 开始和结束节点才能使映射工作。
如果您 运行 一个标准的 Spring 引导项目,那么参数名称会在编译期间保留,不需要其他注释。如果您不保留它们,请在参数中添加@Param("person")
。
我注意到您在您的域中使用 LocalDate
。 Neo4j 3.4+ 和 Java(又名 Bolt)驱动程序原生支持这些。
在当前版本的 Spring Data Neo4j 和 Neo4j-OGM 与 Spring Boot 2.1.8 一起分发时,可以激活它们,如下面的测试所示(向下滚动到 Config
class 注释为 @TestConfiguration
):
import static org.assertj.core.api.Assertions.*;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.ogm.config.Configuration;
import org.neo4j.ogm.driver.ParameterConversionMode;
import org.neo4j.ogm.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
@SpringBootTest
@RunWith(SpringRunner.class)
@TestConfiguration
public class SkillRatingRepositoryTest {
@Autowired
private SkillRatingRepository skillRatingRepository;
@Autowired
private Session session;
@Autowired
private PlatformTransactionManager transactionManager;
@Test
public void retrievalOfSkillsShouldWork() {
Skill s = Skill.builder().name("Java")
.description("The Number one programming language everyone loves and hates").build();
new TransactionTemplate(transactionManager).execute(t -> {
session.purgeDatabase();
return null;
});
Person ms = Person.builder()
.firstName("Michael")
.lastName("Simons")
.build();
Person gm = Person.builder()
.firstName("Gerrit")
.lastName("M")
.build();
SkillRating r1 = SkillRating
.builder().person(ms)
.skill(s).measurementDate(LocalDate.now()).score(23).build();
SkillRating r2 = SkillRating
.builder().person(gm)
.skill(s).measurementDate(LocalDate.now()).score(42).build();
skillRatingRepository.saveAll(Arrays.asList(r1, r2));
List<SkillRating> skillRatings =
skillRatingRepository.findAllByPerson(gm);
assertThat(skillRatings).hasSize(1);
}
@TestConfiguration
static class Config {
@Bean
public org.neo4j.ogm.config.Configuration configuration() {
Configuration.Builder builder = new org.neo4j.ogm.config.Configuration.Builder();
builder.uri("bolt://localhost:7687");
builder.credentials("neo4j", "secret");
builder.withCustomProperty(ParameterConversionMode.CONFIG_PARAMETER_CONVERSION_MODE,
ParameterConversionMode.CONVERT_NON_NATIVE_ONLY);
return builder.build();
}
}
}
请注意,我既没有使用嵌入式实例进行测试,也没有使用 @DataNeo4jTest
。想要查看我本地 运行ning 实例中创建的数据。
我还建议不要在测试中使用嵌入式数据库,而是在生产中使用测试容器 "the real thing" 运行:https://medium.com/neo4j/testing-your-neo4j-based-java-application-34bef487cc3c
作为最终参考,这是我使用的 POM
。如果这个东西有用并解决了您的问题,请采纳答案。
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>neo4j</groupId>
<artifactId>so_re</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>so_re</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
我有以下节点:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@NodeEntity
public class Person {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
private LocalDate birthday;
@Email
private String email;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@NodeEntity
public class Skill {
@Id
@GeneratedValue
private Long id;
private String name;
private String description;
}
还有这个RelationshipEntity
:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RelationshipEntity("RATED")
public class SkillRating {
@Id
@GeneratedValue
private Long id;
@Min(0)
@Max(100)
private Integer score;
private LocalDate measurementDate;
@StartNode
private Person person;
@EndNode
private Skill skill;
}
我不想加载我不打算使用的关系,即我不想添加:
@Relationship(type = "RATED")
private Set<SkillRating> skillRatings;
我的 Person
class 定义,以防止我每次加载 Person
时加载这些评级。我想在必要时使用 Repository
方法加载它们。这是我尝试使用我对 JPA 存储库的了解:
@Repository
public interface SkillRatingRepository extends Neo4jRepository<SkillRating, Long> {
List<SkillRating> findAllByPerson(Person person);
}
但是这个方法没有按预期工作,因为它没有找到任何人的评级。我做错了什么?
-- 编辑--
MATCH (p)-[r:RATED]->(skill) WHERE id(p)={personId} RETURN r
我相信这是一个用 Neo4j Cypher 查询语言编写的查询,可以解决我的问题。如何使用我当前的 class 设置以 Repository
方法 "translate" 它?
谢谢提问。
如您所见,我们不支持基于对象的派生查找器方法。 @RelationshipEntity
还有一个额外的限制,派生的查找器方法只针对属性而不是结束或开始节点。
话虽如此,我已经接受了您的项目(域 classes)并为您创建了一个解决方案。因此,域 classes Person
、Skill
和 SkillRating
可以按原样使用。
请这样声明您的SkillRatingRepository
:
import java.util.List;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
public interface SkillRatingRepository extends Neo4jRepository<SkillRating, Long> {
@Query("MATCH (p)-[r:RATED]->(skill) WHERE id(p) = :#{#person.id} RETURN p, r, skill")
List<SkillRating> findAllByPerson(Person person);
}
@Query
表示自定义查询。在该自定义查询中,您可以使用 Spring 表达式语言 (SpEL),如此处所述 https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions.
因此,您将取消引用已通过的人并访问该 ID。与您已经编写的查询非常相似。请注意,您还必须 return 开始和结束节点才能使映射工作。
如果您 运行 一个标准的 Spring 引导项目,那么参数名称会在编译期间保留,不需要其他注释。如果您不保留它们,请在参数中添加@Param("person")
。
我注意到您在您的域中使用 LocalDate
。 Neo4j 3.4+ 和 Java(又名 Bolt)驱动程序原生支持这些。
在当前版本的 Spring Data Neo4j 和 Neo4j-OGM 与 Spring Boot 2.1.8 一起分发时,可以激活它们,如下面的测试所示(向下滚动到 Config
class 注释为 @TestConfiguration
):
import static org.assertj.core.api.Assertions.*;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.ogm.config.Configuration;
import org.neo4j.ogm.driver.ParameterConversionMode;
import org.neo4j.ogm.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
@SpringBootTest
@RunWith(SpringRunner.class)
@TestConfiguration
public class SkillRatingRepositoryTest {
@Autowired
private SkillRatingRepository skillRatingRepository;
@Autowired
private Session session;
@Autowired
private PlatformTransactionManager transactionManager;
@Test
public void retrievalOfSkillsShouldWork() {
Skill s = Skill.builder().name("Java")
.description("The Number one programming language everyone loves and hates").build();
new TransactionTemplate(transactionManager).execute(t -> {
session.purgeDatabase();
return null;
});
Person ms = Person.builder()
.firstName("Michael")
.lastName("Simons")
.build();
Person gm = Person.builder()
.firstName("Gerrit")
.lastName("M")
.build();
SkillRating r1 = SkillRating
.builder().person(ms)
.skill(s).measurementDate(LocalDate.now()).score(23).build();
SkillRating r2 = SkillRating
.builder().person(gm)
.skill(s).measurementDate(LocalDate.now()).score(42).build();
skillRatingRepository.saveAll(Arrays.asList(r1, r2));
List<SkillRating> skillRatings =
skillRatingRepository.findAllByPerson(gm);
assertThat(skillRatings).hasSize(1);
}
@TestConfiguration
static class Config {
@Bean
public org.neo4j.ogm.config.Configuration configuration() {
Configuration.Builder builder = new org.neo4j.ogm.config.Configuration.Builder();
builder.uri("bolt://localhost:7687");
builder.credentials("neo4j", "secret");
builder.withCustomProperty(ParameterConversionMode.CONFIG_PARAMETER_CONVERSION_MODE,
ParameterConversionMode.CONVERT_NON_NATIVE_ONLY);
return builder.build();
}
}
}
请注意,我既没有使用嵌入式实例进行测试,也没有使用 @DataNeo4jTest
。想要查看我本地 运行ning 实例中创建的数据。
我还建议不要在测试中使用嵌入式数据库,而是在生产中使用测试容器 "the real thing" 运行:https://medium.com/neo4j/testing-your-neo4j-based-java-application-34bef487cc3c
作为最终参考,这是我使用的 POM
。如果这个东西有用并解决了您的问题,请采纳答案。
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>neo4j</groupId>
<artifactId>so_re</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>so_re</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>