SpringBoot整合Neo4j的简单demo

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

简单DEMO

  1. 导入starter
	<!-- neo4j -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-neo4j</artifactId>
	</dependency>
  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
    // ...
}
@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. 添加测试
    @Test
    public void testAdd() {
        MovieEntity entity = new MovieEntity("让子弹飞", "民国故事");
        entity.addActor(new ActorEntity("姜文", "中国"))
                .addActor(new ActorEntity("周润发", "中国"));
        movieRepository.save(entity);
    }

结果

添加自定义的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);
    }

在这里插入图片描述
可以看到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);

除了@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. 简单的查询
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);
}

几点注意:
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> {
}

查询的测试代码

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

控制台输出:

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. 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);
    }
}
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值