通过Spring Data Neo4J操作您的图形数据库

  在前面的一篇文章《图形数据库Neo4J简介》中,我们已经对其内部所使用的各种机制进行了简单地介绍。而在我们尝试对Neo4J进行大版本升级时,我发现网络上并没有任何成型的样例代码以及简介,而其自身的文档也对如何使用Spring Data Neo4J介绍得语焉不详。因此在本文中,我们就将简单地介绍如何使用Spring Data Neo4J。

  本文中所使用的所有的代码都是基于Spring Data Neo4J 4.1.1的。我已经将这些代码放置在https://github.com/loveis715/Spring-Neo4J中。读者可以自行下载并查看其所包含的各次更改。该样例项目内部是按照版本进行组织的。也就是说,一旦我发现其它后续版本再次出现大范围的API改动,那么我会创建一个新版本文件夹,并在其中添加相应的代码。

 

添加Spring Data Neo4J的支持

  如果想要使用Spring Data Neo4J,我们要做的第一步便是在项目中添加对Spring Data Neo4J的支持。如果您是使用Maven对项目进行管理,那么您首先要在项目中添加使用Spring Data Neo4J所需要的各个依赖项:

 1 <dependency>
 2    <groupId>org.neo4j</groupId>
 3    <artifactId>neo4j-ogm-embedded-driver</artifactId>
 4    <version>2.0.1</version>
 5 </dependency>
 6 <dependency>
 7    <groupId>org.springframework</groupId>
 8    <artifactId>spring-test</artifactId>
 9    <version>4.2.5.RELEASE</version>
10 </dependency>
11 <dependency>
12    <groupId>junit</groupId>
13    <artifactId>junit</artifactId>
14    <version>4.9</version>
15 </dependency>
16 <dependency>
17    <groupId>org.springframework.data</groupId>
18    <artifactId>spring-data-neo4j</artifactId>
19    <version>4.1.1.RELEASE</version>
20 </dependency>

  首先来让我们来看看依赖项neo4j-ogm-embedded-driver。其主要用来提供对内嵌Driver的支持。Spring Data Neo4J主要支持两类Driver:内嵌Driver以及Http Driver。内嵌Driver会直接连接本地图形数据库,因此较适合开发环境;而Http Driver则会通过Http与图形数据库实例沟通,因此更加适合生产环境。

  第二个依赖项spring-test以及第三个依赖项JUnit则添加了基于Spring Framework的测试功能的支持。这里我们所使用的Spring Framework的版本是4.2.5.RELEASE,也是与Spring Data Neo4J所兼容的最低版本。

  而最后一个依赖项spring-data-neo4j也不必多说。其是Spring Data Neo4J所对应的依赖项。

  添加完这些依赖项之后,我们就需要对Spring Data Neo4J进行配置了。在之前的版本中,我们只需要通过Spring的配置文件标明图形数据库的地址以及图形数据库数据类型所在的包即可。而Spring Data Neo4J 4.1.1已经不再支持这种配置方式了,甚至用来处理http://www.springframework.org/schema/data/neo4j这个XML命名空间的Neo4jNamespaceHandler类都已经被移除。作为替代,我们需要从Neo4jConfiguration类派生,以指定Spring Data Neo4J运行时所需要使用的配置:

 1 @Configuration
 2 @EnableNeo4jRepositories(basePackages = "com.ambergarden.samples.neo4j.repositories")
 3 @EnableTransactionManagement
 4 public class GraphDBConfiguration extends Neo4jConfiguration {
 5 
 6    @Bean
 7    public org.neo4j.ogm.config.Configuration getConfiguration() {
 8       org.neo4j.ogm.config.Configuration config =
 9                new org.neo4j.ogm.config.Configuration();
10       // TODO: Temporary uses the embedded driver. We need to switch to http
11       // driver. Then we can horizontally scale neo4j
12       config.driverConfiguration()
13             .setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver")
14             .setURI("file:/D:/neo4j/graph.db/");
15       return config;
16    }
17 
18    @Override
19    @Bean
20    public SessionFactory getSessionFactory() {
21       // Return the session factory which also includes the persistent entities
22       return new SessionFactory(getConfiguration(), "com.ambergarden.samples.neo4j.entities");
23    }
24 }

  上面的代码主要指出了三个配置项:Spring Data Neo4J所需要使用的各个Repository主要存在于com.ambergarden.samples.neo4j.repositories包中,而其所操作的各个数据类型则主要存在于com.ambergarden.samples.neo4j.entities包中。同时我们还标明了我们将使用内嵌Driver,并将数据暂时存储在D:\neo4j\graph.db文件夹下。

  而为了能让Spring能够探测到该配置类,我们首先需要在该类型之上添加@Configuration标记,然后还需要在Spring的配置文件中通过component-scan元素来让Spring Framework搜索配置文件所在的包com.ambergarden.samples.neo4j:

1 <context:component-scan base-package="com.ambergarden.samples.neo4j" />

  至此为止,我们已经完成了对Spring Data Neo4J的配置。那么好,让我们创建一个简单的测试类来验证这些配置的正确性:

 1 @NodeEntity
 2 public class Person {
 3    @GraphId
 4    private Long id;
 5 
 6    public Long getId() {
 7       return id;
 8    }
 9 
10    public void setId(Long id) {
11       this.id = id;
12    }
13 }

  接下来,我们就需要添加对该类型进行CRUD的Repository:

1 public interface PersonRepository extends GraphRepository<Person> {
2 }

  最后,编写一小段测试代码来看看这些类型是否能够正常工作:

 1 @RunWith(SpringJUnit4ClassRunner.class)
 2 @ContextConfiguration({ "classpath*:/spring/dal-test-context.xml" })
 3 public class PersonRepositoryTest {
 4    @Autowired
 5    private PersonRepository personRepository;
 6 
 7    @Test
 8    public void testCRUDPerson() {
 9       Person person = new Person();
10       person = personRepository.save(person);
11       assertNotNull(person);
12       assertNotNull(person.getId());
13 
14       Long personId = person.getId();
15       personRepository.delete(person);
16       person = personRepository.findOne(personId);
17       assertNull(person);
18    }
19 }

  如果您的Eclipse能正确地运行这些单元测试,那么恭喜,您已经成功地配置了Spring Data Neo4J。

 

定义数据实体

  在确认了我们为使用Spring Data Neo4J所进行的项目配置不存在问题之后,我们就需要开始尝试定义操作Neo4J所需的实体了。如果需要将一个类型声明为Neo4J实体,那么我们就需要在相应的类型定义上使用@NodeEntity标记:

1 @NodeEntity
2 public class Person {
3    ……
4 }

  除此之外,我们还需要通过@GraphId标记标明用来记录ID的属性:

 1 @NodeEntity
 2 public class Person {
 3    @GraphId
 4    private Long id;
 5 
 6    public Long getId() {
 7       return id;
 8    }
 9 
10    public void setId(Long id) {
11       this.id = id;
12    }
13 }

  由于该域是如此常用,因此我们常常会将其移到基类实现中,并根据该域的值来重写hashCode()和equals()方法:

 1 public abstract class AbstractEntity {
 2 
 3    @GraphId
 4    private Long id;
 5    ……
 6 
 7    @Override
 8    public boolean equals(Object obj) {
 9       ……
10    }
11 
12    @Override
13    public int hashCode() {
14       ……
15    }
16 }

  此时,所有的Neo4J实体只需要从该类派生就能得到equals()和hashCode()的支持了。至于如何正确地实现equals()以及如何正确地实现hashCode(),网络上的讲解很多,这里就不再赘述。感兴趣的读者可以自行查看GitHub上的示例代码。

  好,有了实体,下一步要做的就是定义实体之间的关系了。先让我们看一个如何创建简单关系的示例:

 1 @NodeEntity
 2 public class Person extends NamedEntity {
 3    @Relationship(type="READER_OF")
 4    private Set<Book> books;
 5    ……
 6 }
 7 
 8 @NodeEntity
 9 public class Book extends DescriptiveEntity {
10    @Relationship(type="READER_OF", direction=Relationship.INCOMING)
11    private Set<Person> readers;
12    ……
13 }

  上面的代码展示了如何在Person类和Book类之间创建简单的关系。可以看到,我们只需要创建对关系另一端实例的引用,并通过@Relationship标记在这些引用之上标明关系的名称即可。如果一个关系是有向的,例如READER_OF关系需要被解读为Person p is READER_OF Book b,那么我们就需要在关系的两端标明关系的方向。在上面的示例中,READER_OF关系的方向便是由Person指向Book。当然,我们也可以通过UNDIRECTED来标示一个关系是没有方向的。

  当然,我们是为了方便才在Book中添加了指向Person实例的集合。如果在您的Book实体中添加这么个域并不会为您带来方便,那么您完全可以省略该域。而且该做法还会在一定程度上简化您编写代码的复杂度。让我们来看下面的一段用来在Person实例和Book实例之间建立关系的示例代码(详见测试代码BookRepositoryTest.testCRUDRelationships()):

 1 @Test
 2 public void testCRUDRelationships() {
 3    ……
 4    // Test add readers
 5    Book book2 = new Book();
 6    book2.setName(TEST_BOOK_NAME_2);
 7    book2 = bookRepository.save(book2);
 8 
 9    readers = new HashSet<Person>();
10    readers.add(person1);
11    person1.getBooks().add(book2);
12    book2.setReaders(readers);
13    book2 = bookRepository.save(book2);
14    ……
15 }

  您可能会问:为什么向Person实例添加一个对Book类实例的引用需要这么多行代码?答案就是,如果我们只更新了关系的一端却没有更新关系的另一端,那么关系两端所记录的关系就会出现不一致,进而导致Neo4J根本无法判断应该根据哪一端所记录的数据对数据库进行操作。您说是不?

  往更深一步说,图形数据库中一个非常容易犯错的地方就是对图的修改会导致图处于一个非一致状态。在这种情况下,对这些数据的保存将可能导致图本身处于一个非期望的状态,并逐渐发展为混乱状态。例如在删除一个实例时没有维护图中其它结点对它的引用,那么该删除操作就可能会导致这些结点中所记录的引用是非法的,甚至使得整个数据库无法加载。您别不信,我们真遇到过。

  所以说,图形数据库编程中,您一定要在每个对图的操作中保证图是处于一个合法的一致状态。这也便是为何我们常常在关系的两端都记录关系的原因:一旦其中一个结点有变,我们可以快速找到对它的引用,并对该引用进行维护。

  您可能还有一个问题,那就是,图形数据库不是用来记录富关系的么?那我们应该如何在关系中记录额外的数据呢?此时我们就需要通过@RelationshipEntity标记来定义一个富关系,并进而在各个实体定义中使用该关系:

 1 @RelationshipEntity(type="WRITER_OF")
 2 public class WriterOf extends AbstractEntity {
 3    @StartNode
 4    private Person writer;
 5 
 6    @EndNode
 7    private Book book;
 8 
 9    private Date startDate;
10    private Date endDate;
11    ……
12 }
13 
14 @NodeEntity
15 public class Person extends NamedEntity {
16    @Relationship(type="WRITER_OF")
17    private Set<WriterOf> writings;
18 
19    @Relationship(type="READER_OF")
20    private Set<Book> books;
21    ……
22 }

  此时在我们的实体定义中所记录的不再是富关系另一端的实体集合,而是富关系本身。

 

  相信经过这么一篇简短的介绍,您已经了解了如何使用Spring Data Neo4J来操作数据库了。如果您感兴趣,可以下载我放置在https://github.com/loveis715/Spring-Neo4J中的示例代码。

 

转载请注明原文地址并标明转载:http://www.cnblogs.com/loveis715/p/5425790.html

商业转载请事先与我联系:silverfox715@sina.com

公众号一定帮忙别标成原创,因为协调起来太麻烦了。。。

转载于:https://www.cnblogs.com/loveis715/p/5425790.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/255519.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像金字塔

图像金字塔被广泛用于各种视觉应用中。图像金字塔是一个图像集合&#xff0c;集合中所有的图像都源于同一个原始图像&#xff0c;而且是通过对原始图像连续降采样活得&#xff0c;直到达到某个中止条件才停止降采样。&#xff08;当然&#xff0c;降为一个像素肯定是中止条件。…

python使用git进行版本控制-分支管理

1、远程克隆 最好的方式是先创建远程库&#xff0c;然后&#xff0c;从远程库克隆&#xff1a; 首先在github上创建一个新的仓库&#xff0c;名字叫gitskills 我们勾选Initialize this repository with a README&#xff0c;这样GitHub会自动为我们创建一个README.md文件。 下一…

【python数字信号处理】——Z变换

目录 一、公式 二、代码 三、结果 一、公式 频域变量&#xff1a;z 时域变量&#xff1a;n 常见序列的Z变换&#xff1a;信号与系统复习归纳&#xff08;十一&#xff09;&#xff1a;Z变换例题_百把人的博客-CSDN博客_z变换例题基于东南大学陈从颜译《信号、系统和变换》和…

九宫格拼图 支持44 55等

代码下载转载于:https://www.cnblogs.com/ygcool/p/5395343.html

144. Binary Tree Preorder Traversal

Given a binary tree, return the preorder traversal of its nodes values. For example:Given binary tree {1,#,2,3}, 1\2/3return [1,2,3]. 该题是对树做前序遍历 下面分别是递归&#xff0c;非递归&#xff0c;分治三种思路的解题结果 #递归写法 class Solution(object):d…

一体化点焊机将要取代分体式焊钳在汽车制造生产线上的使用

目前大多数汽车制造厂及相关配套钣金件厂家选用的是悬挂式点焊机及分体式焊钳&#xff0c;从焊接变压器的功率参数看&#xff0c;约70 % 为160KVA 的&#xff0c;约30 % 为200 kVA 的。原因主要有两方面&#xff0c;一是新材料如镀锌钢板、高强度钢板、铝合金板的应用&#xff…

【python数字信号处理】——线性卷积

目录 一、公式概念 二、代码 1、numpy库 2、自定义打印出每一步结果 三、结果 一、公式概念 线性卷积_百度百科线性卷积(linear convolution) 在时域描述线性系统输入和输出之间关系的一种运算。这种运算在线性系统分析和信号处理中应用很多&#xff0c;通常简称卷积。中文…

activiti web流程设计器 整合视频 教程 SSM和独立部署的方式

本视频为activiti工作流的web流程设计器整合视频教程整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器&#xff09;本视频共讲了两种整合方式1. 流程设计器和其它工作流项目分开部署的方式2. 流程设计器和SSM框架项目整合在一起的方式视频大小 1.13 GB ~【…

移动端判断横屏竖屏

1. CSS判断横屏竖屏 写在同一个CSS中 media screen and (orientation: portrait) {   /*竖屏 css*/} media screen and (orientation: landscape) and (min-width:450px){   /*横屏 css*/}分开写在2个CSS 竖屏<link rel"stylesheet" media"all and (orie…

第五章 - 图像形态学 - 基于图像金字塔的图像分割(cvPyrSegmentation)

本例程涉及到几个数据结构及方法&#xff0c;CvMemStorage、cvPyrSegmentation()、CvConnectedComp、cvGetSeqElem(). CvMemStorage CvMemStorage Growing memory storage typedef struct CvMemStorage { struct CvMemBlock* bottom;/* first allocated block */ struct CvM…

泛型参数转换的问题

泛型不同参数类型生成的对象是相互独立的。 //如 Tuple<string> ts; Tuple<object> to; //ts to 是两个类型的对象。很多时候&#xff0c;我们希望实现 to ts 这种操作&#xff0c;为什么&#xff1f;因为看上去它应该如此。 为了达到这个目的&#xff0c;就要解决…

【python数字信号处理】——循环卷积(也叫圆圈卷积)

目录 一、公式 二、代码 方法一:遍历 方法二:利用卷积定理 一、公式

UI-UIScrollView

- (void)viewDidLoad { [super viewDidLoad]; scrollView [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)]; scrollView.backgroundColor [UIColor redColor]; // 是否支持滑动最顶端 // scrollView.scrollsToTop NO; scrollView.delegate self; // 设…

[COCI2015]ZGODAN

题目大意&#xff1a;   给你一个数$n(n\leq10^1000)$&#xff0c;定义一个数是“美丽数”当且仅当这个数各个数位上的数奇偶性不同。   求最接近$n$的“美丽数”&#xff0c;若有多个&#xff0c;则依次输出。 思路&#xff1a;   贪心高精度。   首先找出$n$的第一个…

OpenCV学习笔记(三)——Mat,图像的新容器

自从版本2.0&#xff0c;OpenCV采用了新的数据结构&#xff0c;用Mat类结构取代了之前用extended C写的cvMat和lplImage&#xff0c;更加好用啦&#xff0c;最大的好处就是更加方便的进行内存管理&#xff0c;对写更大的程序是很好的消息。 需要注意的几点&#xff1a;1. Mat的…

jq实现事件委托

事件委托首 页产品展示公司简介关于我们联系我们转载于:https://www.cnblogs.com/haley168/p/eventTarget.html

【python数字信号处理】——scipy库设计滤波器(IIR为例)、绘制滤波器频谱响应、IIR滤波器滤波、读写wav音频文件

目录 一、参考文献 1、scipy接口 2、scipy库介绍+IIR滤波器设计(含GUI)+绘制频谱响应

关于SQL查询效率,100w数据,查询只要1秒

原文:关于SQL查询效率&#xff0c;100w数据&#xff0c;查询只要1秒1.关于SQL查询效率&#xff0c;100w数据&#xff0c;查询只要1秒&#xff0c;与您分享:机器情况p4: 2.4内存: 1 Gos: windows 2003数据库: ms sql server 2000目的: 查询性能测试,比较两种查询的性能SQL查询效…

OpenCV学习笔记(五十四)——概述FaceRecognizer人脸识别类contrib

在最新版的2.4.2中&#xff0c;文档的更新也是一大亮点&#xff0c;refrence manual扩充了200多页的内容&#xff0c;添加了contrib部分的文档。contrib就是指OpenCV中新添加的模块&#xff0c;但又不是很稳定&#xff0c;可以认为是一个雏形的部分。这次结合refman的阅读&…

【调试】Linux下超强内存检测工具Valgrind

【调试】Linux下超强内存检测工具Valgrind 内容简介 Valgrind是什么&#xff1f;Valgrind的使用Valgrind详细教程1. Valgrind是什么&#xff1f; Valgrind是一套Linux下&#xff0c;开放源代码&#xff08;GPLV2&#xff09;的仿真调试工具的集合。Valgrind由内核&#xff08;c…