0%

neo4j-与springboot整合

注解

基于spring-data-neo4j-6.2.9,neo4j-java-driver.version-4.4.9-不同版本存在差异

Spring Data Neo4j注解

  • @Node:将指定标签的对象和neo4j对应标签节点之间建立映射,具有一个属性labels,允许配置一个或多个标签

  • @Id:将类的属性标记为对象的唯一标识符。

  • @GeneratedValue:在字段级别应用,@Id以指定应如何生成唯一标识符。

    1
    2
    3
    4
    5
    //默认InternalIdGenerator,什么都不做,交由数据库自己生成id
    GeneratedValue.InternalIdGenerator.class
    //生成UUID
    GeneratedValue.UUIDGenerator.class
    //自定义,实现IdGenerator<String>接口,覆写generateId方法
  • @Property:应用于字段级别,作为 Neo4j 节点和关系的属性保留,需要使用@Property. name用于指定数据库中属性的名称。

  • @CompositeProperty:在字段级别应用于应作为复合读回的 Map 类型的属性。请参阅复合属性

  • @Relationship:应用于字段级别,用以指定关系的详细信息。

    • type:关系名称
    • Direction:关系方向
      • Relationship.Direction.INCOMING:关系的目标对象开始节点当前类对象结束节点关系的目标对象 –> 当前类对象
      • Relationship.Direction.OUTGOING:关系的目标对象是结束节点,当前类对象开始节点关系的目标对象 <– 当前类对象
  • @DynamicLabels:应用于字段级别以指定动态标签的来源。

  • @RelationshipProperties:应用于类级别,标识该类是关系类,搭配@Relationship@TargetNode使用。

  • @TargetNode: 应用在@RelationshipProperties注解类上的某个字段,从另一端的角度来标记该关系的目标。

Spring Data commons注解

  • @org.springframework.data.annotation.Id和SDN一样@Id,其实@Id是用Spring Data Common的Id-annotation来注解的。
  • @CreatedBy:应用于字段级别,表示节点的创建者。
  • @CreatedDate:应用于字段级别,表示节点的创建日期。
  • @LastModifiedBy:应用于字段级别以指示对节点的最后更改的作者。
  • @LastModifiedDate:应用于字段级别,表示节点的最后修改日期。
  • @PersistenceCreator:应用于一个构造函数以在读取实体时将其标记为首选构造函数。
  • @Persistent:应用于类级别以指示此类是映射到数据库的候选者。
  • @Version:应用于字段级别,用于乐观锁定并检查保存操作的修改。初始值为零,每次更新时都会自动增加。
  • @ReadOnlyProperty:应用于字段级别以将属性标记为只读。该属性将在数据库读取期间被水化,但不受写入的影响。当用于关系时,请注意,如果不相关,则该集合中的任何相关实体都不会被持久化。

配置

1
2
3
4
5
6
spring:
neo4j:
authentication:
username: neo4j
password: 123
uri: bolt://localhost:7687

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?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.6.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yrl</groupId>
<artifactId>test-neo4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test-neo4j</name>
<description>test-neo4j</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>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

修改neo4j版本

<neo4j-java-driver.version>4.4.2</neo4j-java-driver.version>

实体

节点类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.yrl.neo4j.entity;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.neo4j.core.schema.*;

import java.util.List;


@Builder
@Getter
@Setter
@ToString
@Node("Movie")
public class MovieEntity {

/**
* 标识主键属性
*/
@Id
/**
* 主键生成策略,默认使用InternalIdGenerator,不生成主键值,交由数据库自己维护
*/
@GeneratedValue
private Long id;

/**
* 节点属性,同名无需加 @Property
*/
private String title;

@Property("tagline")
private String description;

/**
* 通过关系类维护节点之间的关系,可以为关系增加属性
* Relationship.Direction.INCOMING:关系的目标对象 是 开始节点,当前类对象 是 结束节点,关系的目标对象 -ACTED_IN-> 当前类对象
*/
@Relationship(type = "ACTED_IN", direction = Relationship.Direction.INCOMING)
private List<RolesRelationship> actorsAndRoles;

/**
* 节点与节点之间直接通过@Relationship注解建立关系
* Relationship.Direction.INCOMING:属性对象 是 开始节点,当前类对象 是 结束节点,属性对象 -DIRECTED-> 当前类对象
*/
@Relationship(type = "DIRECTED", direction = Relationship.Direction.INCOMING)
private List<PersonEntity> directors;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.yrl.neo4j.entity;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;


@Builder
@Setter
@Getter
@ToString
@Node("Person")
public class PersonEntity {

/**
* 标识主键属性
*/
@Id
/**
* 主键生成策略,默认使用InternalIdGenerator,不生成主键值,交由数据库自己维护
*/
@GeneratedValue
private Long id;

/**
* 节点属性,同名无需加 @Property
*/
private String name;

/**
* 节点属性,同名无需加 @Property
*/
private Integer born;
}

关系类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.yrl.neo4j.entity;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.neo4j.core.schema.RelationshipId;
import org.springframework.data.neo4j.core.schema.RelationshipProperties;
import org.springframework.data.neo4j.core.schema.TargetNode;
import java.util.List;

@Builder
@Setter
@Getter
@ToString
@RelationshipProperties
public class RolesRelationship {

@RelationshipId
private Long id;

/**
* 关系的属性
*/
private List<String> roles;

/**
* 当前关系的目标节点
*/
@TargetNode
private PersonEntity person;

}

持久层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.yrl.neo4j.repository;

import com.yrl.neo4j.entity.MovieEntity;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface MovieEntityRepository extends Neo4jRepository<MovieEntity,Long> {
/**
* 方法名查询:根据title查询movie
*/
List<MovieEntity> findMovieEntityByTitle(String title);
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.yrl.neo4j;

import com.yrl.neo4j.entity.MovieEntity;
import com.yrl.neo4j.entity.PersonEntity;
import com.yrl.neo4j.entity.RolesRelationship;
import com.yrl.neo4j.repository.MovieEntityRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.neo4j.core.Neo4jTemplate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootTest
class TestNeo4jApplicationTests {
@Autowired
private MovieEntityRepository movieEntityRepository;
@Autowired
private Neo4jTemplate neo4jTemplate;

@Test
public void save() {
//演员1
List<RolesRelationship> actorsAndRoles = new ArrayList<>();
List<String> roleNameList = new ArrayList<>();
roleNameList.add("角色名称1-1");
roleNameList.add("角色名称1-2");
actorsAndRoles.add(
RolesRelationship.builder()
.roles(roleNameList)
.person(PersonEntity.builder().name("演员名称1").build())
.build());
//演员2
List<String> roleNameList2 = new ArrayList<>();
roleNameList2.add("角色名称2-1");
roleNameList2.add("角色名称2-2");
actorsAndRoles.add(
RolesRelationship.builder()
.roles(roleNameList2)
.person(PersonEntity.builder().name("演员名称2").build())
.build());

//导演
List<PersonEntity> directors = new ArrayList<>();
directors.add(PersonEntity.builder().name("导演").build());

//电影
MovieEntity build = MovieEntity.builder()
.title("电影")
.description("描述")
.actorsAndRoles(actorsAndRoles)
.directors(directors)
.build();

MovieEntity save = movieEntityRepository.save(build);
// MovieEntity save = neo4jTemplate.save(build);
System.out.println(save);
//MovieEntity(id=23, title=电影, description=描述, actorsAndRoles=[RolesRelationship(id=29, roles=[角色名称1-1, 角色名称1-2], person=PersonEntity(id=40, name=演员名称1, born=null)), RolesRelationship(id=30, roles=[角色名称2-1, 角色名称2-2], person=PersonEntity(id=24, name=演员名称2, born=null))], directors=[PersonEntity(id=52, name=导演, born=null)])
}


@Test
public void findAll() {
MovieEntity movie = MovieEntity.builder().description("描述").build();
// List<MovieEntity> movieEntityList = movieEntityRepository.findAll(Example.of(movie));
// List<MovieEntity> movieEntityList = movieEntityRepository.findAll();
List<MovieEntity> movieEntityList = neo4jTemplate.findAll(MovieEntity.class);
System.out.println(movieEntityList);
//[MovieEntity(id=23, title=电影, description=描述, actorsAndRoles=[RolesRelationship(id=30, roles=[角色名称2-1, 角色名称2-2], person=PersonEntity(id=24, name=演员名称2, born=null)), RolesRelationship(id=29, roles=[角色名称1-1, 角色名称1-2], person=PersonEntity(id=40, name=演员名称1, born=null))], directors=[PersonEntity(id=52, name=导演, born=null)])]
}

@Test
public void findByTitle() {
// List<MovieEntity> movieEntityList = movieEntityRepository.findMovieEntityByTitle("电影");

Map<String, Object> parameters = new HashMap<>();
parameters.put("param", "电影");
String cypherQuery = "match(m:Movie)-[r]-(p) where m.title = $param return m,r,p";
List<MovieEntity> movieEntityList = neo4jTemplate.findAll(cypherQuery, parameters, MovieEntity.class);
System.out.println(movieEntityList);
//[MovieEntity(id=23, title=电影, description=描述, actorsAndRoles=[RolesRelationship(id=30, roles=[角色名称2-1, 角色名称2-2], person=PersonEntity(id=24, name=演员名称2, born=null)), RolesRelationship(id=29, roles=[角色名称1-1, 角色名称1-2], person=PersonEntity(id=40, name=演员名称1, born=null))], directors=[PersonEntity(id=52, name=导演, born=null)])]
}

}