【Spring连载】使用Spring Data访问 MongoDB(十四)----Mongodb特有的查询方法
一、定义通用查询方法
见定义查询方法。
二、MongoDB特有的查询方法
通常在存储库上触发的大多数数据访问操作都会导致对MongoDB数据库执行查询。定义这样的查询需要在存储库接口上声明一个方法,如下例所示:
具有查询方法的PersonRepository
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
List<Person> findByLastname(String lastname); --------1
Page<Person> findByFirstname(String firstname, Pageable pageable); --------2
Person findByShippingAddresses(Address address); --------3
Person findFirstByLastname(String lastname); --------4
Stream<Person> findAllBy(); --------5
}
1. findByLastname方法显示具有给定lastname的所有人的查询。该查询是通过解析可以与And和Or连接的约束的方法名来派生的。因此,方法名称会产生一个{"lastname" : lastname}的查询表达式。
2. 对查询应用分页。你可以为方法签名配备一个Pageable参数,并让该方法返回一个Page实例,Spring Data会相应地自动对查询进行分页。
3. 显示可以基于非原始类型的属性进行查询。如果找到多个匹配项,则引发IncorrectResultSizeDataAccessException。
4. 使用First关键字将查询仅限于第一个结果。与<3>不同,如果找到多个匹配项,此方法不会引发异常。
5. 使用Java 8流,在迭代流的同时读取和转换单个元素。
框架不支持引用域类中映射为DBRef的参数。
查询方法支持的关键字
关键字 | 示例 | 逻辑上的结果 |
---|---|---|
After | findByBirthdateAfter(Date date) | {“birthdate” : {“$gt” : date}} |
GreaterThan | findByAgeGreaterThan(int age) | {“age” : {“$gt” : age}} |
GreaterThanEqual | findByAgeGreaterThanEqual(int age) | {“age” : {“$gte” : age}} |
Before | findByBirthdateBefore(Date date) | {“birthdate” : {“$lt” : date}} |
LessThan | findByAgeLessThan(int age) | {“age” : {“$lt” : age}} |
LessThanEqual | findByAgeLessThanEqual(int age) | {“age” : {“$lte” : age}} |
Between | findByAgeBetween(int from, int to) findByAgeBetween(Range<Integer> range) | {“age” : {“$gt” : from, “$lt” : to}} lower / upper bounds ($gt / $gte & $lt / $lte) according to Range |
In | findByAgeIn(Collection ages) | {“age” : {“$in” : [ages…]}} |
NotIn | findByAgeNotIn(Collection ages) | {“age” : {“$nin” : [ages…]}} |
IsNotNull, NotNull | findByFirstnameNotNull() | {“firstname” : {“$ne” : null}} |
IsNull, Null | findByFirstnameNull() | {“firstname” : null} |
Like, StartingWith, EndingWith | findByFirstnameLike(String name) | {“firstname” : name} (name as regex) |
NotLike, IsNotLike | findByFirstnameNotLike(String name) | {“firstname” : { “$not” : name }} (name as regex) |
Containing on String | findByFirstnameContaining(String name) | {“firstname” : name} (name as regex) |
NotContaining on String | findByFirstnameNotContaining(String name) | {“firstname” : { “$not” : name}} (name as regex) |
Containing on Collection | findByAddressesContaining(Address address) | {“addresses” : { “$in” : address}} |
NotContaining on Collection | findByAddressesNotContaining(Address address) | {“addresses” : { “$not” : { “$in” : address}}} |
Regex | findByFirstnameRegex(String firstname) | {“firstname” : {“$regex” : firstname }} |
(No keyword) | findByFirstname(String name) | {“firstname” : name} |
Not | findByFirstnameNot(String name) | {“firstname” : {“$ne” : name}} |
Near | findByLocationNear(Point point) | {“location” : {“$near” : [x,y]}} |
Near | findByLocationNear(Point point, Distance max) | {“location” : {“$near” : [x,y], “$maxDistance” : max}} |
Near | findByLocationNear(Point point, Distance min, Distance max) | {“location” : {“$near” : [x,y], “$minDistance” : min, “$maxDistance” : max}} |
Within | findByLocationWithin(Circle circle) | {“location” : {“$geoWithin” : {“$center” : [ [x, y], distance]}}} |
Within | findByLocationWithin(Box box) | {“location” : {“$geoWithin” : {“$box” : [ [x1, y1], x2, y2]}}} |
IsTrue, True | findByActiveIsTrue() | {“active” : true} |
IsFalse, False | findByActiveIsFalse() | {“active” : false} |
Exists | findByLocationExists(boolean exists) | {“location” : {“$exists” : exists }} |
IgnoreCase | findByUsernameIgnoreCase(String username) | {“username” : {“$regex” : “^username$”, “$options” : “i” }} |
如果属性标准比较一个document,那么字段的顺序和document中的确切相等性很重要。
2.1 地理空间查询Geo-spatial Queries
正如你在前面的关键字表中所看到的,一些关键字会触发MongoDB查询中的地理空间操作。Near关键字允许进行一些进一步的修改,如下面的几个示例所示。
以下示例展示了如何定义一个near查询,该查询可查找与给定点相距给定距离的所有人员:
高级Near查询
public interface PersonRepository extends MongoRepository<Person, String> {
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
List<Person> findByLocationNear(Point location, Distance distance);
}
在查询方法中添加Distance参数可以将结果限制在给定距离内。如果设置的Distance包含一个Metric,框架将透明地使用$nearSphere而不是$code,如下例所示:
例1:将Distance与Metrics结合使用
Point point = new Point(43.7, 48.8);
Distance distance = new Distance(200, Metrics.KILOMETERS);
… = repository.findByLocationNear(point, distance);
{'location' : {'$nearSphere' : [43.7, 48.8], '$maxDistance' : 0.03135711885774796}}
响应式(Reactive)地理空间存储库查询支持响应包装器类型内的域类型和GeoResult<T>结果。不支持GeoPage和GeoResults,因为它们与预先计算平均距离的延迟结果方法相矛盾。但是,你仍然可以自己将Pageable参数传递给页面结果。
将Distance与Metric一起使用会导致添加$nearSphere(而不是普通的$near)子句。除此之外,实际距离将根据所使用的Metrics进行计算。
(请注意,Metric不是指公制单位。它可以是英里而不是公里。相反,Metric指的是测量系统的概念,无论你使用哪种系统。)
在目标属性上使用@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)会强制使用$nearSphere运算符。
public interface PersonRepository extends MongoRepository<Person, String> {
// {'geoNear' : 'location', 'near' : [x, y] }
GeoResults<Person> findByLocationNear(Point location);
// No metric: {'geoNear' : 'person', 'near' : [x, y], maxDistance : distance }
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'maxDistance' : distance,
// 'distanceMultiplier' : metric.multiplier, 'spherical' : true }
GeoResults<Person> findByLocationNear(Point location, Distance distance);
// Metric: {'geoNear' : 'person', 'near' : [x, y], 'minDistance' : min,
// 'maxDistance' : max, 'distanceMultiplier' : metric.multiplier,
// 'spherical' : true }
GeoResults<Person> findByLocationNear(Point location, Distance min, Distance max);
// {'geoNear' : 'location', 'near' : [x, y] }
GeoResults<Person> findByLocationNear(Point location);
}
2.2 基于JSON的查询方法和字段限制
通过将“org.springframework.data.mongodb.repository.Query”注解添加到存储库查询方法中,你可以指定要使用的MongoDB JSON查询字符串,而不是从方法名称派生查询,如下例所示:
public interface PersonRepository extends MongoRepository<Person, String> {
@Query("{ 'firstname' : ?0 }")
List<Person> findByThePersonsFirstname(String firstname);
}
这个?0占位符允许你将方法参数中的值替换为JSON查询字符串。
字符串参数值在绑定过程中被转义,这意味着不可能通过参数添加MongoDB特定的运算符。
你还可以使用filter属性来限制映射到Java对象中的属性集,如下例所示:
public interface PersonRepository extends MongoRepository<Person, String> {
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
List<Person> findByThePersonsFirstname(String firstname);
}
上例中的查询只返回Person对象的firstname、lastname和Id属性。age属性java.lang.Integer没有设置,因此其值为null。
2.3 使用SpEL表达式的基于JSON的查询
查询字符串和字段定义可以与SpEL表达式一起使用,以在运行时创建动态查询。SpEL表达式可以提供谓词(predicate)值,并可用于使用子文档扩展谓词。
表达式通过包含所有参数的数组公开方法参数。以下查询使用[0]来声明lastname的谓词值(相当于?0参数绑定):
public interface PersonRepository extends MongoRepository<Person, String> {
@Query("{'lastname': ?#{[0]} }")
List<Person> findByQueryWithExpression(String param0);
}
表达式可用于调用函数、计算条件和构造值。与JSON结合使用的SpEL表达式揭示了一个副作用,因为SpEL内部的类Map声明读起来像JSON,如下面的示例所示:
public interface PersonRepository extends MongoRepository<Person, String> {
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
List<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
}
查询字符串中的SpEL是增强查询的一种强大方法。然而,他们也可以接受一系列不必要的争论。在将字符串传递给查询之前,请确保对其进行清理,以避免对查询造成漏洞或不必要的更改。
表达式支持可通过查询SPI扩展:EvaluationContextExtension&ReactiveEvaluationContextExtension查询SPI可以提供属性和函数,并可以自定义根对象。扩展是在生成查询时进行SpEL评估时从应用程序上下文中检索的。以下示例显示了如何使用评估上下文扩展:
public class SampleEvaluationContextExtension extends EvaluationContextExtensionSupport {
@Override
public String getExtensionId() {
return "security";
}
@Override
public Map<String, Object> getProperties() {
return Collections.singletonMap("principal", SecurityContextHolder.getCurrent().getPrincipal());
}
}
自己引导MongoRepositoryFactory是没有应用程序上下文感知的,需要进一步配置以拾取查询SPI扩展。
响应式(Reactive)查询方法可以使用org.springframework.data.spel.spi.ReactiveEvaluationContextExtension。
2.4 全文检索查询
MongoDB的全文搜索功能是特定于存储的,因此可以在MongoRepository上找到,而不是在更通用的CrudRepository上。我们需要一个带有全文索引的document(请参阅“文本索引”以了解如何创建全文索引)。
MongoRepository上的额外方法将TextCriteria作为输入参数。除了这些显式方法之外,还可以添加TextCriteria派生的存储库方法。这些条件将作为附加的AND条件添加。一旦实体包含@TextScore注解的属性,就可以检索文档的全文分数。此外,@TextScore注解还可以根据文档的分数进行排序,如下例所示:
@Document
class FullTextDocument {
@Id String id;
@TextIndexed String title;
@TextIndexed String content;
@TextScore Float score;
}
interface FullTextRepository extends Repository<FullTextDocument, String> {
// Execute a full-text search and define sorting dynamically
List<FullTextDocument> findAllBy(TextCriteria criteria, Sort sort);
// Paginate over a full-text search result
Page<FullTextDocument> findAllBy(TextCriteria criteria, Pageable pageable);
// Combine a derived query with a full-text search
List<FullTextDocument> findByTitleOrderByScoreDesc(String title, TextCriteria criteria);
}
Sort sort = Sort.by("score");
TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny("spring", "data");
List<FullTextDocument> result = repository.findAllBy(criteria, sort);
criteria = TextCriteria.forDefaultLanguage().matching("film");
Page<FullTextDocument> page = repository.findAllBy(criteria, PageRequest.of(1, 1, sort));
List<FullTextDocument> result = repository.findByTitleOrderByScoreDesc("mongodb", criteria);
2.5 聚合方法Aggregation Methods
存储库层提供了通过带注解的存储库查询方法与聚合框架交互的方法。与基于JSON的查询类似,你可以使用org.springframework.data.mongodb.repository.Aggregation注解定义管道。定义可能包含简单的占位符,如?0以及SpEL表达式?#{ … }.
例2:聚合存储库方法
public interface PersonRepository extends CrudRepository<Person, String> {
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
List<PersonAggregate> groupByLastnameAndFirstnames(); --------1
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
List<PersonAggregate> groupByLastnameAndFirstnames(Sort sort); --------2
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : ?0 } } }")
List<PersonAggregate> groupByLastnameAnd(String property); --------3
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : ?0 } } }")
Slice<PersonAggregate> groupByLastnameAnd(String property, Pageable page); --------4
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
Stream<PersonAggregate> groupByLastnameAndFirstnamesAsStream(); --------5
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
SumValue sumAgeUsingValueWrapper(); --------6
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
Long sumAge(); --------7
@Aggregation("{ $group : { _id : null, total : { $sum : $age } } }")
AggregationResults<SumValue> sumAgeRaw(); --------8
@Aggregation("{ '$project': { '_id' : '$lastname' } }")
List<String> findAllLastnames(); --------9
@Aggregation(pipeline = {
"{ $group : { _id : '$author', books: { $push: '$title' } } }",
"{ $out : 'authors' }"
})
void groupAndOutSkippingOutput(); --------10
}
public class PersonAggregate {
private @Id String lastname; --------2
private List<String> names;
public PersonAggregate(String lastname, List<String> names) {
// ...
}
// Getter / Setter omitted
}
public class SumValue {
private final Long total; --------6,8
public SumValue(Long total) {
// ...
}
// Getter omitted
}
1. 聚合管道(Aggregation pipeline)用于在Person集合中按lastname对first names进行分组,并将这些名字作为PersonAggregate返回。
2. 如果存在Sort参数,则$sort会附加在声明的管道阶段之后,这样它只会在通过所有其他聚合阶段之后影响最终结果的顺序。因此,排序属性映射到方法返回类型PersonAggregate,该类型将Sort.by("lastname")转换为{ $sort : { '_id', 1 } } ,因为PersonAggregate.lastname是用@Id注解的。
3. 将?0替换为动态聚合管道属性的给定值。
4. $skip、$limit和$sort可以通过Pageable参数传递。与<2>中相同,运算符被附加到管道定义中。接受Pageable的方法可以返回Slice以便于分页。
5. 聚合方法可以返回Stream以直接使用底层游标的结果。通过调用close()或通过try-with-resources,确保在使用流后关闭流以释放服务器端游标。
6. 将返回单个Document的聚合结果映射到所需SumValue目标类型的实例。
7. 可以直接从结果Document中提取导致单个document仅包含累积结果(例如$sum)的聚合。为了获得更多的控制权,你可以将AggregationResult视为方法返回类型,如<7>所示。
8. 获取映射到通用目标包装器类型SumValue或org.bson.Document的原始AggregationResults。
9. 与<6>中一样,可以直接从多个结果Documents中获得单个值。
10. 当返回类型为void时,跳过$out阶段的输出。
在某些情况下,聚合可能需要额外的选项,如最长运行时间、额外的日志注解或将数据临时写入磁盘的权限。使用@Meta注解通过maxExecutionTimeMs、comment或allowDiskUse设置这些选项。
interface PersonRepository extends CrudRepository<Person, String> {
@Meta(allowDiskUse = true)
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
List<PersonAggregate> groupByLastnameAndFirstnames();
}
或者使用@Meta创建您自己的注解,如下面的示例所示。
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@Meta(allowDiskUse = true)
@interface AllowDiskUse { }
interface PersonRepository extends CrudRepository<Person, String> {
@AllowDiskUse
@Aggregation("{ $group: { _id : $lastname, names : { $addToSet : $firstname } } }")
List<PersonAggregate> groupByLastnameAndFirstnames();
}
简单类型的单一结果检查返回的Document并检查以下内容:
- document中只有一个条目,请将其返回。
- 两个条目,一个是_id值。退回另一个。
- 返回可分配给返回类型的第一个值。
- 如果以上都不适用,则引发异常。
使用@Aggregation的存储库方法不支持Page返回类型。但是,你可以使用Pageable参数将$skip、$limit和$sort添加到管道中,并让该方法返回Slice。
2.6 Query by Example
本章主要内容见这里。
2.7 滚动Scrolling
滚动是一种更细粒度的方法,用于在较大的结果集块中进行迭代。滚动包括稳定排序、滚动类型(基于偏移或键集的滚动)和结果限制。你可以使用属性名称定义简单的排序表达式,并通过查询派生使用Top或First关键字定义静态结果限制。可以将表达式连接起来,将多个条件收集到一个表达式中。
滚动查询返回一个Window<T>,允许获取滚动位置以继续获取下一个Window<T>,直到你的应用程序消耗完整个查询结果。类似于通过获取下一批结果来使用Java Iterator<List<…>>,查询结果滚动允许你通过Window.positionAt(…)访问一个ScrollPosition。
Window<User> users = repository.findFirst10ByLastnameOrderByFirstname("Doe", ScrollPosition.offset());
do {
for (User u : users) {
// consume the user
}
// obtain the next Scroll
users = repository.findFirst10ByLastnameOrderByFirstname("Doe", users.positionAt(users.size() - 1));
} while (!users.isEmpty() && users.hasNext());
WindowIterator提供了一个实用程序,通过消除检查下一个Window是否存在和应用ScrollPosition的需要,简化了在Windows上滚动。
WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
.startingAt(OffsetScrollPosition.initial());
while (users.hasNext()) {
User u = users.next();
// consume the user
}
2.7.1 使用偏移量滚动
偏移量滚动使用类似于分页的偏移量计数器来跳过许多结果,并让数据源只返回从给定偏移量开始的结果。这种简单的机制避免了将大的结果发送到客户端应用程序。但是,在服务器返回结果之前,大多数数据库都需要具体化(materializing)完整的查询结果。
例6:将OffsetScrollPosition与存储库查询方法结合使用
interface UserRepository extends Repository<User, Long> {
Window<User> findFirst10ByLastnameOrderByFirstname(String lastname, OffsetScrollPosition position);
}
WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
.startingAt(OffsetScrollPosition.initial()); --------1
1. 从位置0的初始偏移量开始。
2.7.2 使用Keyset-Filtering滚动
基于偏移量的滚动要求大多数数据库都要在服务器返回结果之前具体化(materializing)整个结果。因此,虽然客户端只看到请求结果的一部分,但服务器需要构建完整的结果,这会导致额外的负载。
Keyset-Filtering通过利用数据库的内置功能来实现结果子集检索,旨在减少单个查询的计算和I/O需求。这种方法维护一组键,通过将键传递到查询中来以恢复滚动,从而有效地修改你的筛选条件。
Keyset-Filtering的核心思想是开始使用稳定的排序顺序检索结果。一旦你想滚动到下一个区块,你就会获得一个ScrollPosition,用于重建排序结果中的位置。ScrollPosition捕获当前窗口中最后一个实体的keyset。要运行查询,重构将重写criteria子句以包括所有排序字段和主键,这样数据库就可以利用潜在的索引来运行查询。数据库只需要从给定的keyset位置构建一个小得多的结果,而不需要完全具体化(materialize)一个大的结果,然后跳过结果,直到达到特定的偏移量。
Keyset-Filtering选要求keyset属性(用于排序的属性)不可为null。这一限制适用于比较运算符的特定于存储的null值处理,以及对索引源运行查询的需要。对可为null的属性进行Keyset-Filtering将导致意外结果。
将KeysetScrollPosition与存储库查询方法结合使用
interface UserRepository extends Repository<User, Long> {
Window<User> findFirst10ByLastnameOrderByFirstname(String lastname, KeysetScrollPosition position);
}
WindowIterator<User> users = WindowIterator.of(position -> repository.findFirst10ByLastnameOrderByFirstname("Doe", position))
.startingAt(ScrollPosition.keyset()); --------1
1. 从最前处开始,不要应用额外的过滤。
当数据库包含与排序字段匹配的索引时,Keyset-Filtering效果最佳,因此静态排序效果良好。应用Keyset-Filtering的滚动查询要求查询返回排序顺序中使用的属性,并且这些属性必须映射到返回的实体中。
你可以使用接口和DTO投影,但请确保包含已排序的所有属性,以避免keyset提取失败。
指定排序顺序时,只需包含与查询相关的排序属性即可;如果不希望,则不需要确保查询结果唯一。keyset查询机制通过包括主键(或组合主键的任何剩余部分)来修改排序顺序,以确保每个查询结果都是唯一的。
2.8 排序结果
MongoDB存储库允许使用各种方法来定义排序顺序。让我们看一下下面的示例:
排序查询结果
public interface PersonRepository extends MongoRepository<Person, String> {
List<Person> findByFirstnameSortByAgeDesc(String firstname); --------1
List<Person> findByFirstname(String firstname, Sort sort); --------2
@Query(sort = "{ age : -1 }")
List<Person> findByFirstname(String firstname); --------3
@Query(sort = "{ age : -1 }")
List<Person> findByLastname(String lastname, Sort sort); --------4
}
1. 从方法名称派生的静态排序。SortByAgeDesc为排序参数生成 { age : -1 }。
2. 使用方法参数的动态排序。Sort.by(DESC, "age")为排序参数创建{ age : -1 }。
3. 通过Query注解进行静态排序。按照排序属性中的说明应用排序参数。
4. 通过Query注解的默认排序与通过方法参数的动态排序相结合。Sort.unsorted()生成{ age : -1 }。使用Sort.by(ASC, "age")覆盖默认值并创建{ age : 1 }。Sort.by (ASC, "firstname")更改默认值并生成 { age : -1, firstname : 1 }。
2.9 索引提示Index Hints
@Hint注解允许覆盖MongoDB的默认索引选择,并强制数据库使用指定的索引。
例7:索引提示示例
@Hint("lastname-idx") --------1
List<Person> findByLastname(String lastname);
@Query(value = "{ 'firstname' : ?0 }", hint = "firstname-idx") --------2
List<Person> findByFirstname(String firstname);
1. 使用名为lastname-idx的索引。
2. @Query注解定义了提示别名,相当于添加了@Hint注解。
有关索引创建的更多信息,请参阅索引和集合管理部分。
2.10 排序规则(Collation)的支持
除了常规排序规则支持存储库之外,还允许为各种操作定义排序规则。
public interface PersonRepository extends MongoRepository<Person, String> {
@Query(collation = "en_US") --------1
List<Person> findByFirstname(String firstname);
@Query(collation = "{ 'locale' : 'en_US' }") --------2
List<Person> findPersonByFirstname(String firstname);
@Query(collation = "?1") --------3
List<Person> findByFirstname(String firstname, Object collation);
@Query(collation = "{ 'locale' : '?1' }") -------4
List<Person> findByFirstname(String firstname, String collation);
List<Person> findByFirstname(String firstname, Collation collation); --------5
@Query(collation = "{ 'locale' : 'en_US' }")
List<Person> findByFirstname(String firstname, @Nullable Collation collation); --------6
}
1. 静态排序定义导致{ 'locale' : 'en_US' }。
2. 静态排序定义导致{ 'locale' : 'en_US' }。
3. 动态排序取决于第二个方法参数。允许的类型包括字符串(例如。'en_US'), Locale(例如:Locale.US)和Document(例如:new Document("locale", "en_US"))
4. 根据第二个方法参数进行动态排序。
5. 将排序规则方法参数应用于查询。
6. 如果Collation方法参数不为空,则覆盖@Query的默认排序规则。
如果你为存储库查找器方法启用了自动索引创建,则在创建索引时将包括潜在的静态排序规则定义,如(1)和(2)所示。
最具体的排序规则覆盖了可能定义的其他排序规则。这意味着方法参数优先于查询方法注解,查询方法注解优先于域类型注解。
为了简化整个代码库中排序规则属性的使用,还可以使用@Collation注解,它可以作为上述注解的元注解。同样的规则和位置也适用,此外,@Collation的直接使用将取代在@Query和其他注解上定义的任何排序规则值。这意味着,如果排序规则是通过@Query和@Collation声明的,那么就会选择@Collation。
例8:使用@Collation
@Collation("en_US") --------1
class Game {
// ...
}
interface GameRepository extends Repository<Game, String> {
@Collation("en_GB") --------2
List<Game> findByTitle(String title);
@Collation("de_AT") --------3
@Query(collation="en_GB")
List<Game> findByDescriptionContaining(String keyword);
}
1. 不是@Document(collation=…)。
2. 不是@Query(collation=…)。
3. 倾向于使用@Collation而不是meta。
2.11 读取首选项Read Preferences
@ReadPreference注解允许您配置MongoDB的ReadPreferences。
例9:读取首选项的例子
@ReadPreference("primaryPreferred") --------1
public interface PersonRepository extends CrudRepository<Person, String> {
@ReadPreference("secondaryPreferred") --------2
List<Person> findWithReadPreferenceAnnotationByLastname(String lastname);
@Query(readPreference = "nearest") --------3
List<Person> findWithReadPreferenceAtTagByFirstname(String firstname);
List<Person> findWithReadPreferenceAtTagByFirstname(String firstname); --------4
1. 为没有查询级定义的所有存储库操作(包括继承的、非自定义实现的操作)配置读取首选项。因此,在这种情况下,读首选模式将是primaryPreferred
2. 使用在注解ReadPreference中定义的读首选模式,在这种情况下是secondaryPreferred
3. @Query注解定义了读首选模式别名,相当于添加@ReadPreference注解。
4. 该查询将使用存储库中定义的读首选项模式。
MongoOperations和Query API为ReadPreference提供了更细粒度的控制。