Spring Data是一个高级SpringSource项目,其目的是统一和简化对关系数据库系统和NoSQL数据存储等各种持久性存储的访问。
对于每种类型的持久性存储,您的存储库(也称为DAO或数据访问对象)通常在单个域对象,查找器方法,排序和分页中提供CRUD(创建-读取-更新-删除)操作。 Spring Data为这些方面(CrudRepository,PagingAndSortingRepository)以及针对持久性存储的特定实现提供了通用接口。
您可能已经使用过Spring模板对象之一(例如JdbcTemplate )来编写自定义存储库实现。 尽管模板对象功能强大,但我们可以做得更好。 使用Spring Data的存储库,您只需要编写一个具有根据给定约定定义的finder方法的接口 (该方法可能会因所使用的持久性存储的类型而异)。 Spring Data将在运行时提供该接口的适当实现。 举个例子:
|
在本文中,我们将比较JPA,MongoDB和Neo4j的三个子项目。 JPA是JEE堆栈的一部分,并定义了用于访问关系数据库和执行O / R映射的统一API。 MongoDB是一个可扩展的,高性能,开源,面向文档的数据库。 Neo4j是一个图形数据库,这是一个完全事务性的数据库,用于存储结构化为图形的数据。
所有这些Spring Data项目都支持以下方面:
- 模板化
- 对象/数据存储映射
- 仓库支持
其他Spring Data项目(例如Spring Data Redis或Spring Data Riak)实际上仅提供模板,因为相应的数据存储保留了无法映射或查询的非结构化数据。
让我们详细了解模板,对象映射和存储库支持。
范本
Spring Data模板(和所有其他Spring模板)的主要目的是资源分配和异常转换。
在我们的例子中,资源是数据存储区,通常可以通过TCP / IP连接进行远程访问。 以下示例显示了如何配置MongoDB模板:
|
首先,我们定义某种连接工厂,然后由MongoTemplate引用。 对于MongoDB,Spring Data项目依赖于底层MongoDB Java驱动程序 。
通常,这样的低级数据存储区API将具有其自己的异常处理策略。 Spring处理异常的方法是使用开发人员可以捕获但不必捕获的未经检查的异常。 为了弥合这一差距,模板实现捕获了低级异常并抛出了相应的未经检查的Spring异常,该异常是Spring的DataAccessException的子类。
模板提供了存储特定的操作,例如保存,更新和删除单个记录,或用于执行查询或映射/减少作业。 但是所有这些方法仅适用于相应的基础数据存储。
Spring Data JPA不提供模板,因为JPA实现本身已经是JDBC API之上的抽象层。 JPA的EntityManager是模板的对应物。 异常转换由存储库实现处理。
对象/数据存储映射
JPA引入了O / R映射的标准(即,将对象图映射到关系数据库表)。 Hibernate可能是实现JPA规范的最常见的O / R映射器。
使用Spring Data,此支持扩展到具有类对象数据结构的NoSQL数据存储。 但是这些数据结构可能彼此完全不同,因此很难为对象/数据存储区映射建立通用的API。 每种类型的数据存储区都有其自己的注释集,以提供映射所需的元信息。 让我们看看如何将一个简单的域对象User映射到我们的数据存储区:
JPA | MongoDB | Neo4j |
|
|
|
如果您熟悉JPA实体,则可以识别标准的JPA批注。 Spring Data重用了它们; 没有引入其他注释。 映射本身由您使用的JPA实现完成。 MongoDB和Neo4j需要类似的注释集。 您在类级别有一个将类映射到集合(在MongoDB中,集合类似于关系数据库中的表)或节点类型(节点和边是图数据库(如Neo4j)的主要数据类型) 。
每个JPA实体必须具有唯一的标识符。 MongoDB文档和Neo4j节点也是如此。
MongoDB使用@Id注释(与JPA不同,它在org.springframework.data.annotation包中)Neo4j @GraphId。 保留域对象后,将填充这些属性的值。 对于其他持久属性,如果MongoDB文档中的属性名称与Java属性不匹配,则使用@Field批注。
还支持对其他对象的引用。 我们用户的角色可以这样保持:
JPA | MongoDB | Neo4j |
|
|
|
在JPA中,您使用@OneToMany关系,将n端存储在另一个表中,通常使用联接查询。 MongoDB不支持集合之间的联接,默认情况下,引用的对象就位存储在同一文档中。 您也可以引用其他文档,从而导致客户端连接。 在Neo4j中,关系是调用边,这是基本数据类型之一。
概括起来:MongoDB和Neo4j使用的对象映射与众所周知的JPA O / R映射相似,但是由于数据结构不同,它并不完全相同。 但是,所有映射背后的概念都是相同的:将Java对象映射到持久性存储的数据结构。
储存库支持
如果您曾经在业务应用程序中保存过数据,则可能编写了某种DAO。 通常,您为单个记录实现CRUD操作,并为每个持久类实现一堆finder方法。 Finder方法使用在执行之前放入查询中的参数。
随着JPA的出现,至少可以通过EntityManager接口使用CRUD操作。 尽管编写自定义查找器仍然很无聊:创建一个命名查询,设置每个参数,执行该查询。 例如:
|
通过使用TypedQuery的流畅接口可以略微减少此问题。
|
...但是您仍然在实现一种方法,该方法调用设置器并为每个查询执行查询。 使用Spring Data JPA ,相同的查询归结为以下代码:
|
使用Spring Data JPA时 ,不必在相应JPA实体的类文件中将JPQL查询声明为@NamedQuerys。 相反,查询是存储库方法(!)的注释:
|
以上所有内容对于Spring Data MongoDB和Spring Data Neo4j也适用。 以下示例使用Cipher查询语言查询Neo4j数据库:
|
当然,查找器方法的命名约定因持久性存储而异。 例如,MongoDB支持地理空间查询 ,因此您可以编写如下查询:
|
通过为所有持久性存储的finder方法提供特殊参数,还对分页和排序提供了通用支持。
存储库支持的主要优点是:
- 开发人员编写的样板代码少得多
- 可以在finder方法及其文档旁边定义查询
- 另外,JPQL查询在Spring上下文组装好后立即编译,而不是在您第一次使用查询时进行编译,这使检测语法错误更加容易
摘要
本文概述了许多复杂的技术,并试图发现相似之处和不同之处。 有关Spring Data项目的更多详细信息,我建议您看一下项目的主页:
要回答标题中的问题:不,没有针对所有持久性存储的通用API。 差异太根本了。 但是Spring Data项目确实提供了一种通用的编程模型来访问您的数据:
存储库支持是物理数据层的抽象,也使编写许多查找程序方法变得非常容易。 通过对象映射,您的域对象可以转换为持久性存储的数据类型。 模板提供了对存储特定功能的低级别访问。
关于作者
Tobias Trelle是Codecentric AG的高级IT顾问,在IT业务方面拥有超过15年的经验。 他的主要兴趣是软件体系结构,EAI和云计算。 他是定期的博客作者,还提供培训并在会议上发表演讲。
Twitter / 博客 / 链接到 / G +