赞
踩
现在工作中开始使用Neo4j,但对照网络上的教程,导入starter后,没有@NodeEntity这个注释,所以参考官方文档,开发了一个简单demo
官方文档
<!-- neo4j -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
@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);
}
@Test
public void testAdd() {
MovieEntity entity = new MovieEntity("让子弹飞", "民国故事");
entity.addActor(new ActorEntity("姜文", "中国"))
.addActor(new ActorEntity("周润发", "中国"));
movieRepository.save(entity);
}
参考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的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);
主要对照文档的11.12. Is @Query the only way to use custom queries?
@Repository
public interface QueryDSLPersonRepository extends Neo4jRepository<PersonEntity, Long>, CypherdslConditionExecutor<PersonEntity> {
}
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);
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。