赞
踩
目录
Neo4j Java参考
确保在neo4j.conf中禁用-XX:+ TrustFinalNonStaticFields JVM标志。
用户自定义的存储过程通过Java部署到db,从Cypher进行访问。
存储过程用Java编写并编译成jar文件。 可以通过将jar文件放入每个独立服务器或群集服务器上的$ NEO4J_HOME / plugins目录中将它们部署到数据库中。 必须在每台服务器上重新启动数据库以获取新过程。
存储过程是扩展Neo4j的首选方法。 程序的用例示例如下:
提供对Cypher中不可用的功能的访问。
提供对第三方系统的访问。
执行图形全局操作,例如计算连接的组件或查找密集节点。
表达难以用Cypher声明性地表达的程序操作。
在org.neo4j.examples的包中的名为findDenseNodes的存储过程,定义如下
CALL org.neo4j.examples.findDenseNodes(1000)
Neo4j本身内置了很多存储过程,可以通过CALL dbms.procedures()来进行展示。
需要打jar包来调用,下面介绍整体编写、测试和部署neo4j的存储过程。
配置maven文件如下:
- <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
- http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>org.neo4j.example</groupId>
-
- <artifactId>procedure-template</artifactId>
-
- <version>1.0.0-SNAPSHOT</version>
-
- <packaging>jar</packaging>
-
- <name>Neo4j Procedure Template</name>
-
- <description>A template project for building a Neo4j Procedure</description>
-
- <properties>
-
- <neo4j.version>3.5.3</neo4j.version>
-
- </properties>
第一个依赖项部分包括存储过程在运行时使用的存储过程API。scope设置为provided,因为一旦将过程部署到Neo4j实例,此依赖关系由Neo4j提供。 如果将非Neo4j依赖项添加到项目中,则它们的作用域通常应该是compiled的。
- <dependency>
-
- <groupId>org.neo4j</groupId>
-
- <artifactId>neo4j</artifactId>
-
- <version>${neo4j.version}</version>
-
- <scope>provided</scope>
-
- </dependency>
接下来,添加测试过程所需的依赖项:
Neo4j Harness,一个允许启动轻量级Neo4j实例的实用程序。 它用于启动Neo4j并部署特定的过程,这极大地简化了测试。
Neo4j Java驱动程序,用于发送调用该过程的cypher语句。
JUnit,一个常见的Java测试框架。
- <dependency>
-
- <groupId>org.neo4j.test</groupId>
-
- <artifactId>neo4j-harness</artifactId>
-
- <version>${neo4j.version}</version>
-
- <scope>test</scope>
-
- </dependency>
-
-
-
- <dependency>
-
- <groupId>org.neo4j.driver</groupId>
-
- <artifactId>neo4j-java-driver</artifactId>
-
- <version>1.7.3</version>
-
- <scope>test</scope>
-
- </dependency>
-
-
-
- <dependency>
-
- <groupId>junit</groupId>
-
- <artifactId>junit</artifactId>
-
- <version>4.12</version>
-
- <scope>test</scope>
-
- </dependency>
最后自己加上maven shade对应的依赖文件。
所有的存储过程需要被标注为存储过程@procedure
@procedure可以采取三个可选参数
name用于为过程指定与生成的默认名称不同的名称,即class.path.nameOfMethod。如果mode已指定,则还name必须指定。
mode用于声明过程将执行的交互类型。默认mode是READ。可以使用以下模式:
如果需要存储过程的上下文所使用的资源相同,则注解为@Context。
设置远程调试参数:
dbms.jvm.additional=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
加到neo4j对应的conf文件后重新启动。
APOC是Neo4j 3.3版本推出时正式推荐的一个Java存储过程包,里面包含丰富的函数和过程,作为对Cypher所不能提供的复杂图算法和数据操作功能的补充,APOC还具有使用灵活、高性能等优势。
从neo4j3.0开始,引入了用户定义的存储过程的这一概念。简单地说,存储过程:
APOC was also the first bundled A Package Of Component for Neo4j in 2009.
APOC also stands for "Awesome Procedures On Cypher"
Call apoc.cypher.*
可以在Cypher里面调用存储过程,然后在过程里面使用Cypher查询。
使用apoc来执行cypher查询的好处:
1. 可以动态构造查询语句
2. 控制查询的执行时间
3. 条件化查询分支when,case
4. 更灵活的查询执行任务控制:批次大小,并行执行,重试等等
apoc的neighbor方式
- match (e:Eth_Port_Test)
- call apoc.neighbors.tohop(e,'RELY_ON_TEST>',3) yield node
- return node
触发器的好处在于自动触发而不需要手动执行
CALL apoc.trigger.add(name, statement, selector) yield name, statement, installed
可以使用’createNode’、‘deleteNode’等类似的名称下声明对应的语句,选择器是before、after、rollback返回上一个和新的触发器信息
- //删除以前添加的触发器 返回触发器信息
- CALL apoc.trigger.remove(name) yield name, statement, installed
-
- //删除所有先前添加的触发器,返回触发器信息
- CALL apoc.trigger.removeAll() yield name, statement, installed
-
- //更新并列出所有已安装的触发器
- CALL apoc.trigger.list() yield name, statement, installed
-
- //暂停触发器
- Call apoc.trigger.pause(name)
-
- //恢复暂停触发器
- Call apoc.trigger.resume(name)
辅助函数
apoc.trigger.nodesByLabel({assignedLabels/assign
edNodeProperties},'Label')
|
|
apoc.trigger.propertiesByKey({assigned
NodeProperties},'key')
//实际适用语句
apoc.trigger.propertiesByKey({assignedNodeProp
erties},"surname")
|
函数以property-key过滤propertyEntries,以在具有{assignedNode / RelationshipProperties}和{removedNode / RelationshipProperties}的触发器语句中使用。返回[{old,[new],key,node,relationship}]
|
apoc官方文档的触发器的demo里面存在一个小问题,会引发循环触发,如何避免?
- //创建数据集
- CREATE (d:Person {name:'Daniel'})
- CREATE (l:Person {name:'Mary'})
- CREATE (t:Person {name:'Tom'})
- CREATE (j:Person {name:'John'})
- CREATE (m:Person {name:'Michael'})
- CREATE (a:Person {name:'Anne'})
- CREATE (l)-[:DAUGHTER_OF]->(d)
- CREATE (t)-[:SON_OF]->(d)
- CREATE (t)-[:BROTHER]->(j)
- CREATE (a)-[:WIFE_OF]->(d)
- CREATE (d)-[:SON_OF]->(m)
- CREATE (j)-[:SON_OF]->(d)
-
- //使用propertiesBykey在surname属性上添加触发器
- CALL apoc.trigger.add('setAllConnectedNodes','UNWIND apoc.trigger.propertiesByKey({assignedNodeProperties},"surname") as prop
- WITH prop.node as n
- MATCH(n)-[]-(a)
- SET a.surname = n.surname', {phase:'after'});
//在节点上添加surname属性时,它会添加到所有的连接的节点(但是添加之后又会循环触发,循环触发的体现见APOC Trigger 循环触发,这样的情况如何处理)
要注意的是,这样的情况会引发数据库后台进程阻塞,需要后台重启neo4j实例,来阻止对应的阻塞进程。前端刷新并不生效。仅仅在前端刷新,会阻塞对Person标签类对象的一切操作,包括delete与set。
- //修改相应的触发器语句,此时成功执行,不再阻塞,但是在前端显示不会说Set 6 //properties。仅仅显示//Set 1 property,因为其他5个节点的属性是后台触发完成的。
-
- CALL apoc.trigger.add('setAllConnectedNodes','UNWIND apoc.trigger.propertiesByKey({assignedNodeProperties},"surname") as prop
- WITH prop.node as n
- MATCH(n:Person)-[]-(a:Person)
- where not exists(a.surname) or n.surname<>a.surname
- SET a.surname = n.surname', {phase:'after'});
数据集
- CREATE (k:Actor {name:'Keanu Reeves'})
- CREATE (l:Actor {name:'Laurence Fishburne'})
- CREATE (c:Actor {name:'Carrie-Anne Moss'})
- CREATE (m:Movie {title:'Matrix'})
- CREATE (k)-[:ACT_IN]->(m)
- CREATE (l)-[:ACT_IN]->(m)
- CREATE (c)-[:ACT_IN]->(m)
使用apoc.trigger.nodesByLabel来创建触发器,当一个节点的Actor标签被删除了之后,用Person标签更新所有的Actor标签
- CALL apoc.trigger.add('updateLabels',"UNWIND apoc.trigger.nodesByLabel({removedLabels},'Actor') AS node
- MATCH (n:Actor)
- REMOVE n:Actor SET n:Person SET node:Person", {phase:'before'})
-
-
- MATCH(k:Actor {name:'Keanu Reeves'})
- REMOVE k:Actor
- //当新节点的标签是actor,name属性是某些特定值时,为这些新节点创建关系。
- CALL apoc.trigger.add('create-rel-new-node',"UNWIND {createdNodes} AS n
- MATCH (m:Movie {title:'Matrix'})
- WHERE n:Actor AND n.name IN ['Keanu Reeves','Laurence Fishburne','Carrie-Anne Moss']
- CREATE (n)-[:ACT_IN]->(m)", {phase:'before'})
-
-
-
- CREATE (k:Actor {name:'Keanu Reeves'})
- CREATE (l:Actor {name:'Laurence Fishburne'})
- CREATE (c:Actor {name:'Carrie-Anne Moss'})
- CREATE (a:Actor {name:'Tom Hanks'})
- CREATE (m:Movie {title:'Matrix'})
-
-
-
- //暂停触发器
- Call apoc.trigger.pause(‘’)
-
- //恢复之前暂停的触发器
- Call apoc.trigger.resume(‘’)
和之前一样,创建before类型的触发器
我们希望所有的reference属性都是string类型
- CALL apoc.trigger.add("forceStringType",
- "UNWIND apoc.trigger.propertiesByKey({assignedNodeProperties}, 'reference') AS prop
- CALL apoc.util.validate(apoc.meta.type(prop) <> 'STRING', 'expected string property type, got %s', [apoc.meta.type(prop)]) RETURN null", {phase:'before'})
-
- //验证:
- CREATE (a:Node) SET a.reference = 1
-
- //触发器的其他例子
- CALL apoc.trigger.add('timestamp','UNWIND {createdNodes} AS n SET n.ts = timestamp()');
-
- CALL apoc.trigger.add('lowercase','UNWIND {createdNodes} AS n SET n.id = toLower(n.name)');
-
- CALL apoc.trigger.add('txInfo', 'UNWIND {createdNodes} AS n SET n.txId = {transactionId}, n.txTime = {commitTime}', {phase:'after'});
-
- CALL apoc.trigger.add('count-removed-rels','MATCH (c:Counter) SET c.count = c.count + size([r IN {deletedRelationships} WHERE type(r) = "X"])')
-
- CALL apoc.trigger.add('lowercase-by-label','UNWIND apoc.trigger.nodesByLabel({assignedLabels},'Person') AS n SET n.id = toLower(n.name)')
触发器的参数列表
声明
|
描述
|
transactionId
|
返回事务的id
|
commitTime
|
以毫秒为单位返回事务日期
|
createdNodes
|
创建节点时,触发器触发(节点列表)
|
createdRelationships
|
当创建一个关系时,我们的触发器会触发(关系列表)
|
deletedNodes
|
当一个节点被驱逐时,我们的触发器会触发(节点列表)
|
deletedRelationships
|
当关系降低时,我们的触发器会触发(关系列表)
|
removedLabels
|
当一个标签被删除时,我们的触发器会触发(标签映射到节点列表)
|
removedNodeProperties
|
当删除节点的属性时,我们的触发器触发(键映射到键,旧,节点的映射列表)
|
removedRelationshipProperties
|
当删除关系的属性时,我们的触发器触发(键映射到键,旧,关系的映射列表)
|
assignedLabels
|
当一个labes被分配时我们的触发器触发(标签到节点列表的映射)
|
assignedNodeProperties
|
当分配节点属性时,我们的触发器触发(键映射到key,old,new,node的映射列表)
|
assignedRelationshipProperties
|
当关系属性被分配时,我们的触发器触发(键的映射列表,键,旧,新,关系)
|
快速理清各种标签的节点之间的关系
图中并不实际存在虚拟节点和关系,它们仅返回给UI以表示图的投影。存在负id。
具体的实例:
1. 将关系聚合为一个
2. 将中间节点折叠成虚拟关系,出于安全考虑隐藏属性或者中间节点/关系。
3. 只返回节点/rels的几个属性到可视化,例如你有巨大的文本属性。
4. 对图算法找到的聚类进行可视化。
5. 将信息聚合到更高的抽象层次。
6. 跳过较长路径的中的中间节点,
7. 图表分组。
8. 将来自其他来源的数据csv、xml、json的数据可视化为图形,甚至不存储它。
9. 投射部分数据。
要记住的一件事是:由于您无法从图中查找已创建的虚拟节点,因此必须将它们保存在您自己的查找结构中。适合它的东西是apoc.map.groupBy从实体列表创建一个映射,由给定属性的字符串值键入。
到目前为止,虚拟实体可以在所有表面上工作,Neo4j-Browser,Bloom,neovis以及所有驱动程序,即使它最初并非如此,它们也非常酷。
它们主要用于可视化,但Cypher本身无法访问它们(它们的ID,标签,类型,属性)。这就是为什么我们添加了许多函数来访问它们的属性,标签和rel-types。
在某些将来,它们可能会被图形视图所包含,能够在Cypher 10中返回图形和可组合的Cypher查询。
Neo4j的原生api的抽象层次较低,使用起来不是很方便,原生api的代码要复杂不少,而使用Spring data的则简洁很多。
Neo4j repository本身与继承它的repository,它们的实例都已经被Spring自动创建,即意味着我只需要声明接口的引用,而不需要把它和具体的对象相关联,即我们不需要进行实例化的操作。Spring在检测到引用之后已经自动创建对应的实例,我们也不需要为声明的接口方法提供实现。
提供存储库的推荐方法是为每个聚合根定义存储库接口,而不是为每个域类定义存储库接口。底层Spring存储库基础结构将自动检测这些存储库以及其他实现类,并创建可在服务或其他Spring bean中使用的可注入存储库实现。
Spring Data Neo4j提供的存储库构建在Spring Data Commons中的可组合存储库基础结构上。这些允许基于接口的存储库组合,包括为某些接口提供的默认实现和其他方法的其他自定义实现。
Spring Data Neo4j带有一个org.springframework.data.repository.PagingAndSortingRepository专门Neo4jRepository<T. ID>用于所有对象图映射存储库的特殊化。此子接口还添加了特定的finder方法,这些方法采用深度参数来控制获取和保存相关实体的范围。通常,它提供所有期望的存储库方法。如果需要其他操作,则应将其他存储库接口添加到单个接口声明中。
您通常在存储库上触发的大多数数据访问操作都将导致对Neo4j数据库执行查询。定义这样的查询只需要在存储库接口上声明一个方法。
- public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
- List<Person> findByLastname(String lastname);
- Page<Person> findByFirstname(String firstname, Pageable pageable);
- Person findByShippingAddresses(Address address);
- Stream<Person> findAllBy();
- }
1、该方法显示具有给定姓氏的所有人的查询。将派生查询解析可以与And和连接的约束的方法名称Or。因此,方法名称将导致查询表达式为{"lastname" : lastname}。
2、将分页应用于查询。只需为方法签名配备一个Pageable参数,让方法返回一个Page实例,我们将自动相应地分页查询。
3、显示您可以基于非基本类型的属性进行查询。
4、使用Java 8 Stream,它在迭代流时读取和转换单个元素。
可以使用@Query注释提供使用Cypher图形查询语言的查询。
这意味着注释的存储库方法
@Query("MATCH (:Actor {name:{name}})-[:ACTED_IN]->(m:Movie) return m")
将使用提供的查询从Neo4j中检索数据。
命名或索引参数{param}将由实际方法参数替换。节点和关系实体直接处理并转换为它们各自的ID。所有其他参数类型被直接提供(即String,Long等)。
注意:
自定义查询不支持自定义深度。此外,@Query不支持将路径映射到域实体,因此,不应从Cypher查询返回路径。相反,返回节点和关系以将它们映射到域实体。
使用@Query放置在存储库方法上的Cypher查询示例,其中值用方法参数替换,如“ 注释查询”部分所述。
- public interface MovieRepository extends Neo4jRepository<Movie, Long> {
- // returns the node with id equal to idOfMovie parameter
- @Query("MATCH (n) WHERE id(n)={0} RETURN n")
- Movie getMovieFromId(Integer idOfMovie);
-
- // returns the nodes which have a title according to the movieTitle parameter
- @Query("MATCH (movie:Movie {title={0}}) RETURN movie")
- Movie getMovieFromTitle(String movieTitle);
-
- // same with optional result
- @Query("MATCH (movie:Movie {title={0}}) RETURN movie")
- Optional<Movie> getMovieFromTitle(String movieTitle);
-
- // returns a Page of Actors that have a ACTS_IN relationship to the movie node with the title equal to movieTitle parameter.
- @Query(value = "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor", countQuery= "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN count(actor)")
- Page<Actor> getActorsThatActInMovieFromTitle(String movieTitle, PageRequest page);
-
-
- // returns a Page of Actors that have a ACTS_IN relationship to the movie node with the title equal to movieTitle parameter with an accurate total count
- @Query(value = "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor", countQuery = "MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN count(*)")
- Page<Actor> getActorsThatActInMovieFromTitle(String movieTitle, Pageable page);
-
-
- // returns a Slice of Actors that have a ACTS_IN relationship to the movie node with the title equal to movieTitle parameter.
- @Query("MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor")
- Slice<Actor> getActorsThatActInMovieFromTitle(String movieTitle, Pageable page);
-
- // returns users who rated a movie (movie parameter) higher than rating (rating parameter)
- @Query("MATCH (movie:Movie)<-[r:RATED]-(user) " +
- "WHERE id(movie)={movieId} AND r.stars > {rating} " +
- "RETURN user")
- Iterable<User> getUsersWhoRatedMovieFromTitle(@Param("movieId") Movie movie, @Param("rating") Integer rating);
-
-
-
- // returns users who rated a movie based on movie title (movieTitle parameter) higher than rating (rating parameter)
- @Query("MATCH (movie:Movie {title:{0}})<-[r:RATED]-(user) " +
- "WHERE r.stars > {1} " +
- "RETURN user")
- Iterable<User> getUsersWhoRatedMovieFromTitle(String movieTitle, Integer rating);
-
-
- @Query(value = "MATCH (movie:Movie) RETURN movie;")
- Stream<Movie> getAllMovies();
-
- }
使用底层对象 - 图形映射器中的元数据基础结构,可以将查找器方法名称拆分为其语义部分并转换为Cypher查询。沿关系的导航将反映在生成的MATCH子句中,并且具有运算符的属性将最终作为WHERE子句中的表达式。参数将按它们在方法签名中出现的顺序使用,因此它们应与方法名称中指定的表达式对齐。这里的意思是SDN(Spring-Data-Neo4j)的框架,在Repository接口中可以自动为我们按照某种格式定义的方法补全方法体,避免了我们造轮子的行为,我们只需要声明一个方法名就可以了。
- public interface PersonRepository extends Neo4jRepository<Person, Long> {
- // MATCH (person:Person {name={0}}) RETURN person
- Person findByName(String name);
-
-
- // MATCH (person:Person)
- // WHERE person.age = {0} AND person.married = {1}
- // RETURN person
- Iterable<Person> findByAgeAndMarried(int age, boolean married);
-
-
- // MATCH (person:Person)
- // WHERE person.age = {0}
- // RETURN person ORDER BY person.name SKIP {skip} LIMIT {limit}
- Page<Person> findByAge(int age, Pageable pageable);
-
-
-
- // MATCH (person:Person)
- // WHERE person.age = {0}
- // RETURN person ORDER BY person.name
- List<Person> findByAge(int age, Sort sort);
-
-
- // Allow a custom depth as a parameter
- Person findByName(String name, @Depth int depth);
-
-
- // Set a fix depth of 0 for the query
- @Depth(value = 0)
- Person findBySurname(String surname);
- }
将复杂的Cypher查询结果转换为自定义的Java对象。
对于通过@Query存储库方法执行的查询,可以指定将复杂查询结果转换为POJO。然后使用查询结果数据填充这些结果对象。这些POJO更易于处理,并且可以用作数据传输对象(DTO),因为它们不附加到Session任何生命周期并且不参与任何生命周期。要利用此功能,请使用带注释的类@QueryResult作为方法返回类型。
- public interface MovieRepository extends Neo4jRepository<Movie, Long> {
- @Query("MATCH (movie:Movie)-[r:RATING]->(), (movie)<-[:ACTS_IN]-(actor:Actor) " +
- "WHERE movie.id={0} " +
- "RETURN movie as movie, COLLECT(actor) AS cast, AVG(r.stars) AS averageRating")
- MovieData getMovieData(String movieId);
- }
-
-
- @QueryResult
- public class MovieData {
- Movie movie;
- Double averageRating;
- Set<Actor> cast;
- }
Spring Data Neo4j支持在使用Spring Data Pageable和Sort接口时对结果进行排序和分页。
- //基于repository的分页
- Pageable pageable = PageRequest.of(0, 3);
- Page<World> page = worldRepository.findAll(pageable, 0);
-
- //基于repository的排序
- Sort sort = new Sort(Sort.Direction.ASC, "name");
- Iterable<World> worlds = worldRepository.findAll(sort, 0)) {
-
- //基于repository的分页排序
- Pageable pageable = PageRequest.of(0, 3, Sort.Direction.ASC, "name");
- Page<World> page = worldRepository.findAll(pageable, 0);
Spring Data Repositories通常在使用查询方法时返回域模型(作为@NodeEntity或作为a @QueryResult)。但是,有时您可能出于各种原因需要该模型的不同视图。在本节中,您将学习如何定义投影以提供简化和简化的资源视图。
查看以下域模型:
- @NodeEntity
- public class Cinema [w1] {
- private Long id;
- private String name, location;
-
- @Relationship(type = "VISITED", direction = Relationship.INCOMING)
- private Set<User> visited = new HashSet<>();
-
- @Relationship(type = "BLOCKBUSTER", direction = Relationship.OUTGOING)
- private Movie blockbusterOfTheWeek;
- …
-
- }
这Cinema有几个属性:
现在假设我们按如下方式创建相应的存储库:
- public interface CinemaRepository extends Neo4jRepository<Cinema, Long> {
- Cinema findByName(String name);
- }
Spring Data将返回域对象,包括其所有属性以及访问此影院的所有用户。这可能是大量数据,可能导致性能问题。
有如下几种方法可以避免findByName的问题
简单投影
- public interface CinemaNameAndBlockbuster {
- public String getName();
- public Movie getBlockbusterOfTheWeek();
- }
此投影具有以下详细信息:
一个普通的Java接口,使其具有声明性。
仅导出实体的某些属性。
该CinemaNameAndBlockbuster投影仅具有name和blockbusterOfTheWeek的getter方法,意味着它不会提供之前域实体的用户信息,在这种情况下,查询方法定义返回CinemaNameAndBlockbuster而不是Cinema。
- interface CinemaRepository extends Neo4jRepository<Cinema, Long> {
- CinemaNameAndBlockbuster findByName(String name);
- }
投影声明了基础类型与公开属性相关的方法签名之间的规则。因此,需要根据基础类型的属性名称来命名getter方法为getName,否则Spring Data无法查找对应的源属性。这种类型的投影也称为闭合投影
到目前为止,您已经了解了如何使用投影来减少呈现给用户的信息。投影可用于调整公开的数据模型。您可以为投影添加虚拟属性。请看以下投影界面:
- interface RenamedProperty {
-
- @Value("#{target.name}")
- String getCinemaName();
-
- @Value("#{target.blockbusterOfTheWeek.name}")
- String getBlockbusterOfTheWeekName();
- }
此投影具有以下详细信息:
- interface RenamedProperty {
- @Value("#{target.name} #{(target.location == null) ? '' : target.location}")
- String getNameAndLocation();
- }
在此示例中,仅当影院名称可用时,才会将该位置附加到影院名称。
Neo4j是一个事务型数据库,只允许在事务内执行操作。Spring Data Neo4j通过@Transaction支持声明式事务,利用TransactionTemplate支持手动事务处理。
其目的是为非CRUD操作定义事务边界
关于JSON序列化的注释
查看上面给出的示例,可以很容易地发现节点和富关系之间对类级别的循环依赖性。只要不序列化对象,它就不会对您的应用程序产生任何影响。今天使用的一种序列化是使用Jackson映射器的JSON序列化。如果数据在SpringBoot或JavaEE等框架中导出,则将使用此映射器库。遍历对象树时,它会在访问Role后访问一个部分Actor。显然它会找到Actor对象并再次访问它,依此类推。这将最终成为一个StackOverflowError。要打破此解析周期,必须通过为类提供注释来支持映射器。这可以通过添加其中之一来完成@JsonIgnore在导致循环或属性的属性上@JsonIgnoreProperties。
参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。