当前位置:   article > 正文

SpringBoot整合Neo4j的简单demo_@relationshipproperties

@relationshipproperties

现在工作中开始使用Neo4j,但对照网络上的教程,导入starter后,没有@NodeEntity这个注释,所以参考官方文档,开发了一个简单demo
官方文档

简单DEMO

  1. 导入starter
	<!-- neo4j -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-neo4j</artifactId>
	</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 创建节点实体和Repository
@Node("Movie")
public class MovieEntity {
    @Id
    @GeneratedValue
    private Long id;
    private final String title;
    @Property("tagline")
    private final String description;
    @Relationship(type = "ACTED_IN", direction = Direction.INCOMING)
    private List<ActorEntity> actors;

    public MovieEntity(String title, String description) {
        this.id = null;
        this.title = title;
        this.description = description;
    }

    public MovieEntity withId(Long id) {
        if (this.id.equals(id)) {
            return this;
        } else {
            MovieEntity newObject = new MovieEntity(this.title, this.description);
            newObject.id = id;
            return newObject;
        }
    }

    public MovieEntity addActor(ActorEntity actor) {
        if (this.actors == null) {
            this.actors = new ArrayList<>();
        }
        this.actors.add(actor);
        return this;
    }
    // getter
    // ...
}
  • 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
@Repository
public interface MovieRepository extends Neo4jRepository<MovieEntity, Long> {
    @Query("MATCH (m:Movie) where m.title=$title RETURN m")
    List<MovieEntity> findByTitle(@Param("title") String title);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 添加测试
    @Test
    public void testAdd() {
        MovieEntity entity = new MovieEntity("让子弹飞", "民国故事");
        entity.addActor(new ActorEntity("姜文", "中国"))
                .addActor(new ActorEntity("周润发", "中国"));
        movieRepository.save(entity);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

结果

添加自定义的RelationShip

参考relationShipProperties注释
上面的简单demo中,@Relationship声明的list中,ActorEntity类使用的是@Node注释,所以在保存时Actor被作为节点保存,而现在将类使用@RelationshipProperties声明即可
但是要注意,使用@RelationshipProperties声明的类必须有一个@TargetNode作为目标节点

// movieEntity代码
public class MovieEntity {
    @Id
    @GeneratedValue
    private Long id;
    private final String title;
    @Property("tagline")
    private final String description;
    @Relationship(value = "ACTED_IN", direction = Relationship.Direction.INCOMING)
    private List<RoleRelationShip> roles;
}

// RoleRelationShip类代码
@RelationshipProperties
public class RoleRelationShip {
    @Id
    @GeneratedValue
    private Long id;
    private final String name;
    @TargetNode
    private final PersonEntity person;
}

// PersonEntity类与movie类相似

// 测试代码
    @Test
    public void testAdd() {
        MovieEntity movie = new MovieEntity("肖申克的救赎", "自我救赎,重获自由!");
        movie.addRole(new RoleRelationShip("安迪·杜佛兰", new PersonEntity("Tim Robbins", "美国")))
                .addRole(new RoleRelationShip("瑞德", new PersonEntity("Morgan Freeman", "美国")));

        movieRepository.save(movie);
    }
  • 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

在这里插入图片描述
可以看到relation也添加了需要的属性

自定义的Query

@Query的value可以使用SpEL,只需要使用:#{ 和 } 包装即可
使用MovieEntity更新对象

@Query("MATCH (m:Movie) where id(m)=:#{#query.id} set m.tagline=:#{#query.description} RETURN m")
MovieEntity updateById(@Param("query") MovieEntity entity);
  • 1
  • 2

除了@Query外的其他定制查询

主要对照文档的11.12. Is @Query the only way to use custom queries?

  1. 创建一个接口
    接口实现Neo4jRepository和CypherdslConditionExecutor接口
@Repository
public interface QueryDSLPersonRepository extends Neo4jRepository<PersonEntity, Long>, CypherdslConditionExecutor<PersonEntity> {
}
  • 1
  • 2
  • 3
  1. 简单的查询
Node person = Cypher.node("Person").named("n");// [1]
Property name = person.property("name");
Property nation = person.property("nation");
Collection<PersonEntity> collection = queryDSLPersonRepository.findAll(
        name.eq(Cypher.anonParameter("Morgan Freeman")).or(nation.eq(Cypher.parameter("someName", "America"))),// [2]
        nation.descending()
);
for (PersonEntity personEntity : collection) {
        System.out.println(personEntity);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

几点注意:
3. [1]处声明一个节点,named()方法的介绍是 Creates a copy of this node with a new symbolic name.,但我不太理解,为什么在(n:Person)内没有被修改名称,所以我只能使用"n"
4. [2]处就是查询的条件,Cypher.parameter(“someName”, “America”)和Cypher.anonParameter(“Morgan Freeman”),目前来看效果是一样的,只不过Cypher.parameter("someName", "America")就是参数使用$someName, 而不是默认的占位符

第二种定制查询

简单的基于关系的查询

@Repository
public interface PersonStatementRepository extends Neo4jRepository<PersonEntity, Long>, CypherdslStatementExecutor<PersonEntity> {
}
  • 1
  • 2
  • 3

查询的测试代码

Node p = Cypher.node("Person").named("p");
Node m = Cypher.node("Movie").named("m");
Relationship r = p.relationshipTo(m, "ACTED_IN");

// 查询参演了电影标题是"黑水"的演员
ResultStatement build = Cypher.match(r).where(m.property("title").isEqualTo(Cypher.anonParameter("黑水")))
        .returning(Functions.collect(p)).build();
Collection<PersonEntity> all = personStatementRepository.findAll(build);
for (PersonEntity personEntity : all) {
    System.out.println(personEntity);
}
System.out.println("break;");
// 查询名称以"Freeman"结尾的演员,并且以"福克斯"这个名称参演得电影
build = Cypher.match(p, r, m).where(
        p.property("name").endsWith(Cypher.parameter("lastName", "Freeman"))
        .and(r.property("name").isEqualTo(Cypher.anonParameter("福克斯"))))
        .returning(
//                Functions.collect(p),
//                Functions.collect(r),
                Functions.collect(m)
        ).build();
Collection<MovieEntity> all1 = movieStatementRepository.findAll(build);
for (MovieEntity movie : all1) {
    System.out.println(movie);
}
  • 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

控制台输出:

PersonEntity{id=5, name='Anne Hathaway', from='美国'}
PersonEntity{id=1, name='Tim Robbins', from='美国'}
break;
MovieEntity{id=6, title='蝙蝠侠:黑暗骑士', description='广受好评的超级英雄电影', roles=[]}
MovieEntity{id=9, title='蝙蝠侠:黑暗骑士崛起', description='又一部广受好评的超级英雄电影', roles=[]}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

目前的几个问题
1. Cypher.match(p, r, m)的入参到底是什么?第一个statement只传入了relationship,但可以查出Person
2. 如何获得一个关联的列表?第二个查询怎么样把其关联的roles查询出来
3. returning()的用法,目前只发现一种用法,这个也行可以解决2的问题
这一种查询的复杂用法
参考这种方法,可以自定义到一些较为复杂的查询,例如下面的:查询和名称以"Freeman"结尾的演员共同演出过的演员

@Test
public void testHyperCustomerQuery() {
    Node p = Cypher.node("Person").named("p");
    Node m = Cypher.node("Movie").named("m");
    Node p2 = Cypher.node("Person").named("p2");
    Relationship r1 = p.relationshipTo(m, "ACTED_IN");
    Relationship r2 = p2.relationshipTo(m, "ACTED_IN");
    ResultStatement build = Cypher.match(r1, r2)
            .where(p.property("name").endsWith(Cypher.parameter("lastName", "Freeman")))
            .returning(Functions.collect(p2)).build();
    Collection<PersonEntity> list = personStatementRepository.findAll(build);
    for (PersonEntity personEntity : list) {
        System.out.println(personEntity);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/641468
推荐阅读
相关标签
  

闽ICP备14008679号