Spring Data Couchbase 2.0

工程 | Josh Long | February 10, 2016 | ...

这是一篇来自 Simon BASLÉ (@simonbasle) 的交叉发布博客,他来自 Couchbase。你可以在 twitter (@simonbasle) 或 github 上找到他。如需了解更多关于 Couchbase 和 Couchbase Java SDK 的信息,请访问开发者门户。再次感谢 Simon 和你的团队的杰出工作!-Josh

Spring Data Couchbase 2.0 是对原有 Spring Data Couchbase 1.4.x 连接器的重写。它基于 Couchbase Java 2.2 SDK,并大量使用了新的查询语言 N1QL(在 Couchbase Server 4.0 中引入),为 Spring Data 用户提供更多功能。

第一个里程碑版本已于去年八月发布,随后是候选发布版本,自那以后又实现了更多功能(和错误修复),现在 GA 版本可以向公众发布了。

让我们快速浏览一下有哪些变化(使用 ⭐ 到 ⭐⭐⭐ 表示我们认为每个功能有多棒和多重要?)

Spring Data Couchbase 2.0 中的新功能

Spring Data Couchbase 的 1.x 版本与 2.x 版本之间的主要区别在于

  • 配置元素更贴近 Couchbase 实际:环境(Environment)、集群(Cluster)、桶(Bucket)(这可能允许你创建连接到不同桶甚至不同集群的 CouchbaseTemplate 实例!)
  • 后备自定义存储库方法不再总是使用视图完成,它(默认情况下)现在通过 N1QL 完成,这更加灵活,并且需要更少的服务器端维护。
  • 使用视图的自定义方法已经稍作修改,以更好地遵循 Spring Data 的理念。这稍微降低了灵活性,但实现是通过方法名称(通过“查询派生”)生成的。
  • 现在你可以使用视图对数据进行地理空间查询(如果超出三维,也可以进行多维查询)。

当然,你仍然可以使用 CouchbaseTemplate 而非 CouchbaseRepository 接口来访问较低级别的 API,你甚至可以直接从 SDK 访问底层的 Bucket

通过 N1QL 实现的存储库方法

⭐⭐⭐

Couchbase 4.0 的重大新功能是 N1QL,它是一个工作在 JSON 文档上的 SQL 扩展(因此它为 SQL 添加了与 JSON 相关的特定性)。

这对于 Spring Data 中的 Repository 模式和查询派生尤其有利,因为绝大多数查询派生关键字都可以轻松转换为 N1QL。

N1QL 现在是存储库方法的默认后备 Couchbase 功能。如果你想明确指定执行的查询,也可以选择使用 @Query 接口。

public interface UserRepository extends Repository<User, String> {

  User findByUsernameEquals(String username);

  List<User> findByUsernameContains(String contains);

  @Query //optional for N1QL query derivation but more explicit
  List<User> findByAgeBetween(int minAge, int maxAge);
}

通过视图实现的存储库方法

⭐⭐

此版本的一个重大变化是,现在基于视图的存储库查询(即自定义存储库方法)更符合 Spring Data 的理念。它们也必须显式使用 @View(viewName="something") 进行注解。

这意味着不应有任何 Couchbase 特定的东西泄露到你的存储库接口中。相反,你可以对大多数查询使用查询派生机制。

查询派生在一定程度上也是可能的,一些关键字可以在视图支持的方法中被接受。

public interface UserRepository extends Repository<User, String> {

  @Override
  @View(designDocument = "user", viewName = "customFindAllView")
  Iterable<User> findAll();

  @View(viewName = "customFindByNameView")
  User findByUsernameIs(String lowKey);

  @View(viewName = "customFindByNameView")
  List<User> findByUsernameBetween(String lowKey, String highKey);
}

使用视图中的 reduce 函数

另一个以前不支持的新功能是执行 reduce 函数(如果存在)。现在,为了执行它,你只需在 @View 注解中将 reduce 标志设置为 true。

如果对你有意义(即你实际使用了“count”reduce 函数),你也可以将方法前缀改为“count”而不是“find”。

请注意,Couchbase 中的 reduce 函数可以不同于预先存在的 _count 函数,它甚至可以返回除了 long 之外的其他类型,比如 JsonObject,就像内置的 _stats 那样。

类似地,在方法名中添加“topX”或“firstX”变体将导致对请求设置额外的限制(例如,findFirst5ByLastName 将把结果列表限制为 5 个)。

配置一致性,读己所写 (Read Your Own Writes)

⭐⭐⭐

在使用视图和 GSI(支持 N1QL 的新二级索引引擎)等异步填充的二级索引时,经常出现的一个问题是需要立即读取先前写操作所做的修改。

这意味着只要数据仍在索引过程中,视图/N1QL就不应返回结果,因此这将牺牲一些性能以换取一致性。

相反正好相反(以及 Spring Data Couchbase 当前的默认行为)是优先考虑性能,接受返回陈旧数据。

我们通过提供围绕 Consistency 概念的小抽象,增加了全局语义来配置框架通过查询派生构建的所有查询(基于视图或基于 N1QL 的)。

这可以通过覆盖 AbstractCouchbaseConfigurationgetDefaultConsistency() 方法来完成。Consistency 是一个枚举,允许你在 READ_YOUR_OWN_WRITESSTRONGLY_CONSISTENTUPDATE_AFTEREVENTUALLY_CONSISTENT 之间进行选择。有关它们具体如何工作以及在查询时的影响,请参阅官方文档。

你也可以在 XML 中通过使用 <couchbase:template> 标签上的 consistency 属性来完成。

自 GA 版本以来,存储库中的 CRUD 方法现在也考虑了默认配置的一致性。

更改存储 JSON 中的类型信息字段

一些用户报告了 Spring Data 和 Couchbase Mobile 方面的问题,Sync Gateway 会拒绝包含以下划线为前缀的字段的文档。

这对 Spring Data 来说是个问题,因为它默认将类型信息存储在 _class 字段中 :(

解决方案是通过配置允许修改该类型信息字段的名称。你可以通过覆盖 AbstractCouchbaseConfiguration 中的 typeKey() 方法来实现。例如,你可以使用常量 MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE(其值为 "javaClass")。

这个字段是生成的 N1QL 查询用来过滤只对应于存储库实体的文档的字段。

支持在 N1QL 派生查询中使用 Pageable/PageRequest

⭐⭐

使用 N1QL,对于通过查询派生生成的查询,现在支持 PageableSort 参数。

  • 基于 N1QL 的 PagingAndSortingRepository 支持。
  • 增加了两个 findAll 方法,它们依赖 N1QL 进行分页和/或排序。使用默认配置的一致性。

使用空间视图 (Spatial Views) 进行地理空间和多维查询 (Multi-Dimensional querying)

⭐⭐⭐

使用坐标查询 Couchbase!如果你的实体有 Point(或 xy)位置信息,你可以通过以下方式查找:

  • 边界框:findByLocationWithin(Box area)
  • 圆形:findByLocationWithin(Circle area), findByLocationWithin(Point center, Distance radius)
  • 多边形:findByLocationWithin(Polygon area), findByLocationWithin(Point[] polygon)
  • 距离:findByLocationNear(Point near, Distance maxDistance)

圆形和多边形之类的查询在服务器端会作为边界框近似快速执行,然后框架会在呈现结果之前消除误报。

你可以利用 Couchbase 空间视图的多维特性,为你的查询添加额外维度(例如,查询城市中深夜仍开放的商店等)。

public interface DimensionalPartyRepository extends CrudRepository<Party, String> {

  @Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
  List<Party> findByLocationNear(Point p, Distance d);

  @Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
  List<Party> findByLocationWithin(Box boundingBox);

  @Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation")
  List<Party> findByLocationWithin(Polygon zone);
  
  @Dimensional(designDocument = "partyGeo", spatialViewName = "byLocationAndAttendees", dimensions = 3)
  List<Party> findByLocationWithinAndAttendeesGreaterThan(Polygon zone, double minAttendees);
}

注意:如果你想重用注解,你也可以这样做(对 @View@Query 也有效)

public interface DimensionalPartyRepository extends CrudRepository<Party, String> {

  //define your own meta-annotation
  @Dimensional(designDocument = "partyGeo", spatialViewName = "byLocation", dimensions = 2)
  @Retention(RetentionPolicy.RUNTIME)
  @interface IndexedByLocation { }
  
  //use it :)
  @IndexedByLocation
  List<Party> findByLocationNear(Point p, Distance d);

  @IndexedByLocation
  List<Party> findByLocationWithin(Box boundingBox);

  @IndexedByLocation
  List<Party> findByLocationWithin(Polygon zone);
  
  //here we use a variation with 3 dimensions, so we need to revert to @Dimensional
  @Dimensional(designDocument = "partyGeo", spatialViewName = "byLocationAndAttendees", dimensions = 3)
  List<Party> findByLocationWithinAndAttendeesGreaterThan(Polygon zone, double minAttendees);
}

内联 N1QL @Query 现在支持 SpEL

⭐⭐⭐

内联查询可以使用 SpEL 表达式来:

  1. 确保正确选择和过滤应用于语句,以便构建和返回实体:使用 #{#n1ql.selectEntity} 生成 SELECT ... FROM ... 子句,并在 WHERE 子句中使用 #{#n1ql.filter} 将查询限制到正确的实体。
  2. 计算值或从 Spring 上下文中配置的外部 SpEL 值提供者检索数据。

存储库“主要”索引的创建可以自动触发

⭐⭐

⚠️ 重要:这被认为是开发/测试期间的辅助手段,不建议在生产环境中使用

为了确保给定存储库中实体的 N1QL 索引在开发或预生产环境中被激活,可以使用 @N1qlPrimaryIndexed 注解(启用桶范围内的自由查询)和 @N1qlSecondaryIndexed 注解(仅索引对应于实体类型的文档,类似于 SpEL #{#n1ql.filter} 生成的 WHERE 子句)。

此外,CRUD 操作的后备视图可以通过在存储库上使用 @ViewIndexed 注解来自动创建(你需要提供设计文档名称,该名称应与实体的简单类名相对应,首字母小写)。

此功能必须通过在 AbstractCouchbaseConfiguration 中重新定义 indexManager bean 来额外启用。

使用单行投影时现在支持简单返回类型(基本类型和 String

⭐⭐

这尤其适用于带有聚合函数(如 COUNT(*)AVG(field) 等)的内联 N1QL 查询... 查询必须返回单行且只有单个投影。

支持在 N1QL 内联查询中使用命名参数

⭐⭐

使用命名参数或位置参数,但不能同时使用两者。命名参数的语法是 $paramName,要求每个方法参数都用 @Param("paramName") 注解。

其他功能

其他功能包括

  • 修复 bean 命名,使 Spring Data Couchbase 创建的所有 bean 都带有 "couchbase" 前缀,以避免与其他存储发生冲突。
  • 现在支持更改所有存储库的基类(按照通用 Spring Data 文档中记录的过程进行)
  • 如果索引陈旧,已删除的文档将从 CouchbaseTemplate 中的 find 方法中排除。
  • 过期时间可以在 @Document 上设置,格式为 long + timeUnit

还实现了一些相对于 RC1 的错误修复和改进。

文档

⭐⭐⭐

文档也得到了改进,增加了面向 Couchbase 的示例,如如何向存储库添加自定义方法的实现、如何更改所有存储库的基类、如何在内联查询中处理 SpEL 等。

关于 Spring Cache 的说明

Spring Cache 支持已从 Spring Data 存储库中移出。它仍然存在,我们计划对其进行改进。目前你可以在 github 上的 Couchbase 存储库中找到它,但它很快会重新整合到官方 Spring 项目家族中。

获取 Spring Data Couchbase

你可以在项目的 pom.xml 中添加以下内容以获取此 GA 版本(在 dependencies 部分)

<!--<dependencies>-->
  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-couchbase</artifactId>
    <version>2.0.0.RELEASE</version>
  </dependency>
<!--</dependencies>-->

我们希望你喜欢此版本及其带来的所有新功能。下一步将是重新加入 Hopper 发布列车,预计在夏季前发布 2.1 版本。

订阅 Spring 新闻通讯

订阅 Spring 新闻通讯,保持联系

订阅

抢先一步

VMware 提供培训和认证,助你快速提升。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部