2

在试图理解“Spring Data”的目的时,我偶然发现了这篇文章,并尝试了一些关于 Gemfire/Redis 存储库及其相应 Spring-Data 组件的示例。有人可以帮我解决以下问题。

场景:当 GemFire 是我的数据存储时,我必须使用GemFireTemplate 上的创建、获取和删除方法来执行 CRUD 操作。当 Redis 是我的数据存储时,我必须在 RedisTemplate 上使用.opsForHash().put 、 .opsForHash().get 和 .opsForHash().delete方法来执行相同的 CRUD 操作。

问题

Spring-Data 不应该为底层数据存储提供一定程度的抽象吗?如果我希望了解各自的 CRUD 方法并基于底层数据存储有不同的 API,那么 Spring-Data 组件会带来什么样的抽象?我不能直接使用 Gemfire 的 Jedis 或 Java 客户端来执行这些数据存储特定的 CRUD 操作吗?

这个链接似乎解释了这一点,但是,看起来我需要一些帮助来理解它。

没有适用于所有持久性存储的通用 API。差异太根本了。但是 Spring Data 项目确实提供了一个通用的编程模型来访问你的数据

4

3 回答 3

6

感谢您的问题,因为 Master Slave 已经很好地指出 Spring Data 提供的好处是多方面的,但有时在商店模块之间支持的功能意义上存在功能差异。这些部分是由特定底层数据存储的不同特征和功能以及焦点和时间限制所推动的。

Redis 和 Gemfire 在某种程度上通过提供类似存储和访问模型的键值对类似的存储技术进行了扩展。然而,Gemfire 提供了比 Redis 更丰富的查询功能。这就是 Spring Data Gemfire 已经拥有Repository 抽象很长一段时间的原因之一。

另一方面,Redis 是一个不同的故事 - 正如 Master Slave 已经提到的,您可以轻松构建自己的自定义 Redis 存储库 - 这是许多人所做的 - 但通常与 Redis 的交互对于用例来说是非常独特的,所以我们没有没有看到太多的功能重叠来激发通用实现,而不仅仅是“保存”和“加载”。

我们有一张将 Repository 抽象添加到 Spring Data Redis 的票,但我们还没有时间去做。

于 2015-03-10T15:19:00.760 回答
3

Spring 数据确实为底层数据存储提供了一个抽象级别。然而,NoSQL 存储是一个非常多样化的范围,一个是图形数据库,另一个是专门用于存储文档,第三个是存储键值对等。尽管如此,这些不是你想要抽象掉的差异,这些是使您首先选择它们的独特功能。

另一方面,Spring Data 为您提供了将已知模式应用于这些不同存储的一致性和方法

  • 它为您提供了一种一致的方式来配置资源以访问商店
  • 底层类型与Java类型的映射与转换
  • 大多数商店的存储库抽象归结为仅声明基本 CRUD 操作的接口。实现和特定于商店的东西由 Spring Data 处理
  • 提供回调以访问本机 API 的专用模板实现

名单还在继续。关键是 Spring Data 确实提供了尽可能多的抽象级别,但它也没有将公开的功能限制在一个鲜为人知的共同点上,因为这会带走不同存储的好处。

于 2015-03-10T13:34:50.820 回答
3

关于这个主题的一些额外想法......

使用 Spring 模板进行特定于数据存储的访问(例如 GemfireTemplate)的众多优势之一是它们使开发人员免受模板通常围绕的底层数据存储 API 更改的影响。如果数据存储供应商对 API 接口进行了重大更改(GemFire 就是这种情况),那么模板可以为底层 API 提供一层抽象/适应,而无需更改用户应用程序。

此外,模板可以利用其他 Spring 基础设施管理,例如通常不会固有地内置到底层数据存储 API 本身的事务。模板还可以为底层数据存储产品中的几个不同 API 提供外观和便利层。

例如,GemfireTemplate 封装了“获取”和“放置”数据以及“查询”感兴趣的数据的操作。

要在 GemFire 中执行前者,您需要执行以下操作...

Region<String, SomeValue> example = gemfireCache.getRegion("/Example");

example.put("someKey", new SomeValue(..));
...
SomeValue someValue = example.get("someKey");

在查询“/Example”中的数据时,您会...

Region<String, SomeValue> example = gemfireCache.getRegion("/Example");
QueryService queryService = example.getQueryService();
Query query = queryService.newQuery("SELECT * FROM /Example WHERE ...");
Object result = query.execute(<any args for query parameters>);

if (result instanceof SelectResults) {
  SelectResults<SomeValue> someValues = (SelectResults) result;
  // process the query results
}
else {
  // figure out what happened
}

现在,将其与获取/放置数据的 Spring 模板进行对比

gemfireTemplate.put("someKey", new SomeValue(..));
...
SomeValue someValule = gemfireTemplate.get("someKey");

并查询...

SelectResults<SomeValue> results = gemfireTemplate.find("SELECT * FROM /Example WHERE ...", args);

// process the query results

如果发生异常,或者无法收集结果,则 GemFires 的异常/错误会统一映射到核心 Spring Framework DAO 异常层次结构,这是另一个优势,这使得切换底层数据存储更容易,以及一致的事务处理。

最后,虽然 Thomas Darimont 和 Master Slave 都很好地总结了使用Spring Data Repository 抽象的优势,但这并不是一个新概念。

在 Spring Data 的 Repository 抽象的表达能力和便利性之前,应用程序开发人员将使用 DAO 模式从他们的应用程序服务层中抽象出 CRUD 和其他数据访问操作(例如查询)。

但是,Spring Data 为您提供的是通过简单地为允许的数据访问操作声明合同(Java 接口)来执行此操作的能力,而 Spring Data(包括提供程序特定的实现,如 Spring Data GemFire)负责其余的工作,插入一个“默认”数据存储特定的实现,它是高度可定制的。

显然,我是有偏见的,但是使用接口方法的名称来表达一个查询,使用方法的参数作为 SQL/OQL/etc 查询参数,这简直是太酷了,而且功能强大。

如果查询不复杂或不具体,这可以很容易地从一个数据存储迁移到另一个,甚至在关系和键/值存储之间(例如,考虑到 SQL 和 OQL 之间的相似性的 GemFire)。

希望这可以帮助。干杯!

于 2015-03-10T19:14:30.933 回答