共计 7526 个字符,预计需要花费 19 分钟才能阅读完成。
原题目:Spring 认证中国教育管理中心 -Spring Data Couchbase 教程四(Spring 中国教育管理中心)
Spring Data Couchbase 教程四
4.3. 定义存储库接口
要定义存储库接口,您首先须要定义特定于域类的存储库接口。接口必须扩大 Repository 并输出到域类和 ID 类型。如果要公开该域类型的 CRUD 办法,请扩大 CrudRepository 而不是 Repository.
4.3.1 微调存储库定义
通常状况下,你的资料库接口扩大 Repository,CrudRepository 或
PagingAndSortingRepository。或者,如果您不想扩大 Spring Data 接口,也能够应用 @RepositoryDefinition. 扩大 CrudRepository 公开了一整套操作实体的办法。如果您心愿对公开的办法有选择性,请将要公开的办法复制 CrudRepository 到您的域存储库中。
这样做能够让您在提供的 Spring Data Repositories 性能之上定义本人的形象。
上面的示例示出了如何以选择性地露出 CRUD 办法(findById 和 save,在这种状况下):
示例 28. 选择性地公开 CRUD 办法
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
<S extends T> S save(S entity);
}
interface UserRepository extends MyBaseRepository<User, Long> {
User findByEmailAddress(EmailAddress emailAddress);
}
在后面的例子,你定义为所有站点库一个独特的根底界面和裸露 findById(…),以及 save(…)。这些办法被发送到根底信息库实现你所抉择的由 Spring 提供的数据(例如,如果应用 JPA 商店,实现是 SimpleJpaRepository),因为它们与 CrudRepository. 所以 UserRepository 当初能够保留用户,通过 ID 查找单个用户,并触发查问以 Users 通过电子邮件地址查找。
两头存储库接口用 @NoRepositoryBean. 确保将该正文增加到 Spring Data 不应在运行时为其创立实例的所有存储库接口。
4.3.2. 将存储库与多个 Spring 数据模块一起应用
在应用程序中应用惟一的 Spring Data 模块会使事件变得简略,因为定义范畴内的所有存储库接口都绑定到 Spring Data 模块。有时,应用程序须要应用多个 Spring Data 模块。在这种状况下,存储库定义必须辨别持久性技术。当检测到类门路上有多个存储库工厂时,Spring Data 进入严格的存储库配置模式。严格配置应用存储库或域类的详细信息来决定存储库定义的 Spring Data 模块绑定:
如果存储库定义扩大了特定于模块的存储库,则它是特定 Spring Data 模块的无效候选者。
如果域类应用特定于模块的类型正文进行正文,则它是特定 Spring Data 模块的无效候选者。Spring Data 模块承受第三方注解(例如 JPA’s @Entity)或提供本人的注解(例如 @DocumentSpring Data MongoDB 和 Spring Data Elasticsearch)。
以下示例显示了应用特定于模块的接口(在本例中为 JPA)的存储库:
示例 29. 应用模块特定接口的存储库定义
interface MyRepository extends JpaRepository<User, Long> {}
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> {…}
interface UserRepository extends MyBaseRepository<User, Long> {…}
MyRepository 并在它们的类型层次结构中 UserRepository 扩大 JpaRepository。它们是 Spring Data JPA 模块的无效候选者。
以下示例显示了应用通用接口的存储库:
示例 30. 应用通用接口的存储库定义
interface AmbiguousRepository extends Repository<User, Long> {…}
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> {…}
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> {…}
AmbiguousRepository 和 AmbiguousUserRepository 仅延长 Repository,并 CrudRepository 在他们的类型档次。尽管在应用惟一的 Spring Data 模块时这很好,但多个模块无奈辨别这些存储库应该绑定到哪个特定的 Spring Data。
以下示例显示了一个应用带正文的域类的存储库:
示例 31. 应用带正文的域类的存储库定义
interface PersonRepository extends Repository<Person, Long> {…}
@Entity
class Person {…}
interface UserRepository extends Repository<User, Long> {…}
@Document
class User {…}
PersonRepositoryreferences Person,应用 JPA@Entity 正文进行正文,因而这个存储库显然属于 Spring Data JPA。UserRepositoryreferences User,应用 Spring Data MongoDB 的 @Document 注解进行注解。
以下谬误示例显示了一个应用具备混合正文的域类的存储库:
示例 32. 应用具备混合正文的域类的存储库定义
interface JpaPersonRepository extends Repository<Person, Long> {…}
interface MongoDBPersonRepository extends Repository<Person, Long> {…}
@Entity
@Document
class Person {…}
此示例显示了应用 JPA 和 Spring Data MongoDB 正文的域类。它定义了两个存储库,JpaPersonRepository 并且 MongoDBPersonRepository. 一个用于 JPA,另一个用于 MongoDB。Spring Data 不再可能辨别存储库,这会导致未定义的行为。
存储库类型详细信息和辨别域类正文用于严格的存储库配置,以辨认特定 Spring Data 模块的存储库候选者。在同一域类型上应用多个持久性技术特定的正文是可能的,并且能够跨多个持久性技术重用域类型。然而,Spring Data 无奈再确定绑定存储库的惟一模块。
辨别存储库的最初一种办法是确定存储库根底包的范畴。根本包定义了扫描存储库接口定义的终点,这意味着将存储库定义放在适当的包中。默认状况下,注解驱动配置应用配置类的包。基于 XML 的配置中的根本包是必须的。
以下示例显示了根本包的正文驱动配置:
示例 33. 根本包的正文驱动配置
@EnableJpaRepositories(basePackages = “com.acme.repositories.jpa”)
@EnableMongoRepositories(basePackages = “com.acme.repositories.mongo”)
class Configuration {…}
4.4. 定义查询方法
存储库代理有两种办法能够从办法名称派生特定于存储的查问:
通过间接从办法名称派生查问。
通过应用手动定义的查问。
可用选项取决于理论商店。然而,必须有一种策略来决定创立什么理论查问。下一节将介绍可用的选项。
4.4.1。查问查找策略
以下策略可用于存储库基础架构来解决查问。应用 XML 配置,您能够通过 query-lookup-strategy 属性在命名空间配置策略。对于 Java 配置,您能够应用注解的 queryLookupStrategy 属性 Enable${store}Repositories。特定数据存储可能不反对某些策略。
CREATE 尝试从查询方法名称结构特定于存储的查问。个别的办法是从办法名称中删除一组给定的已知前缀并解析办法的其余部分。您能够在“查问创立”中浏览无关查问结构的更多信息。
USE_DECLARED_QUERY 尝试查找已申明的查问,如果找不到则抛出异样。查问能够由某处的正文定义或通过其余形式申明。请参阅特定商店的文档以查找该商店的可用选项。如果存储库根底构造在疏导时没有找到该办法的申明查问,它就会失败。
CREATE_IF_NOT_FOUND(默认)联合 CREATE 和 USE_DECLARED_QUERY。它首先查找已申明的查问,如果未找到已申明的查问,则创立一个基于自定义办法名称的查问。这是默认查找策略,因而,如果您未明确配置任何内容,则应用此策略。它容许通过办法名称疾速定义查问,还能够通过依据须要引入申明的查问来自定义调整这些查问。
4.4.2. 查问创立
Spring Data 存储库根底构造中内置的查问构建器机制对于在存储库的实体上构建束缚查问很有用。
以下示例显示了如何创立多个查问:
示例 34. 从办法名称创立查问
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
解析查询方法名称分为主语和谓语。第一局部 (find…By, exists…By) 定义查问的主题,第二局部形成谓词。引言从句(主语)能够蕴含进一步的表白。find(或其余介绍关键字)和之间的任何文本都 By 被认为是描述性的,除非应用后果限度关键字之一,例如 Distinct 在要创立的查问上设置不同的标记或 Top/First 来限度查问后果。
附录蕴含查询方法主题关键字和查询方法谓词关键字的残缺列表,包含排序和字母大小写修饰符。然而,第一个 By 用作分隔符以批示理论条件谓词的开始。在十分根本的级别上,您能够在实体属性上定义条件并将它们与 And 和连接起来 Or。
解析办法的理论后果取决于您为其创立查问的持久性存储。然而,有一些个别的事件须要留神:
表达式通常是联合了能够连贯的运算符的属性遍历。您能够将属性表达式与 AND 和联合应用 OR。您还能够失去这样的运营商为撑持 Between,LessThan,GreaterThan,和 Like 该属性的表达式。反对的运算符可能因数据存储而异,因而请参阅参考文档的相应局部。
办法解析器反对 IgnoreCase 为单个属性(例如,findByLastnameIgnoreCase(…))或反对疏忽大小写的类型的所有属性(通常是 String 实例 – 例如,findByLastnameAndFirstnameAllIgnoreCase(…))设置标记。是否反对疏忽大小写可能因商店而异,因而请参阅参考文档中的相干局部以理解商店特定的查询方法。
您能够通过将 OrderBy 子句附加到援用属性的查询方法并提供排序方向(Asc 或 Desc)来利用动态排序。要创立反对动静排序的查询方法,请参阅“非凡参数解决”。
4.4.3. 属性表达式
属性表达式只能援用托管实体的间接属性,如后面的示例所示。在创立查问时,您曾经确保解析的属性是托管域类的属性。然而,您也能够通过遍历嵌套属性来定义束缚。思考以下办法签名:
List<Person> findByAddressZipCode(ZipCode zipCode);
假如 aPerson 有 Address 一个 ZipCode。在这种状况下,该办法会创立 x.address.zipCode 属性遍历。解析算法首先将整个局部 (AddressZipCode)解释为属性,并查看域类中具备该名称(未大写)的属性。如果算法胜利,它将应用该属性。如果不是,该算法将源在驼峰局部从右侧拆分为头部和尾部,并尝试找到相应的属性——在咱们的示例中,AddressZip 和 Code。如果算法找到具备该头部的属性,它将获取尾部并持续从那里向下构建树,以方才形容的形式将尾部拆分。如果第一个宰割不匹配,算法将宰割点向左挪动 (Address,ZipCode) 并持续。
尽管这应该实用于大多数状况,但算法可能会抉择谬误的属性。假如这个 Person 类也有一个 addressZip 属性。该算法曾经在第一个拆分轮中匹配,抉择了谬误的属性,而后失败(因为 的类型 addressZip 可能没有 code 属性)。
要解决这种歧义,您能够_在办法名称中应用手动定义遍历点。所以咱们的办法名称如下:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
因为咱们将下划线字符视为保留字符,咱们强烈建议遵循规范的 Java 命名约定(即,不在属性名称中应用下划线,而是应用驼峰式大小写)。
4.4.4. 非凡参数解决
要解决查问中的参数,请定义后面示例中曾经看到的办法参数。除此之外,该基础架构还能够辨认某些特定类型,例如 Pageableand Sort,以便动静地将分页和排序利用于您的查问。以下示例演示了这些性能:
示例 35. 在查询方法中应用 Pageable, Slice, 和 Sort
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
API 承受 Sort 并 Pageable 冀望将非 null 值传递给办法。如果您不想利用任何排序或分页,请应用 Sort.unsorted()and Pageable.unpaged()。
第一种办法容许您将
org.springframework.data.domain.Pageable 实例传递给查询方法,以动静地将分页增加到动态定义的查问中。APage 晓得可用元素和页面的总数。它通过基础设施触发计数查问来计算总数来实现这一点。因为这可能很低廉(取决于应用的商店),您能够改为返回 Slice. ASlice 只晓得下一个 Slice 是否可用,这在遍历更大的后果集时可能就足够了。
排序选项也通过 Pageable 实例解决。如果您只须要排序,请
org.springframework.data.domain.Sort 在您的办法中增加一个参数。如您所见,返回 aList 也是可能的。在这种状况下,Page 不会创立构建理论实例所需的额定元数据(这反过来意味着不会收回原本须要的额定计数查问)。相同,它将查问限度为仅查找给定范畴的实体。
要理解整个查问取得了多少页,您必须触发额定的计数查问。默认状况下,此查问派生自您理论触发的查问。
分页和排序
您能够应用属性名称定义简略的排序表达式。您能够连贯表达式以将多个条件收集到一个表达式中。
示例 36. 定义排序表达式
Sort sort = Sort.by(“firstname”).ascending()
.and(Sort.by(“lastname”).descending());
要应用更平安的形式来定义排序表达式,请从要为其定义排序表达式的类型开始,并应用办法援用来定义要排序的属性。
示例 37. 应用类型平安 API 定义排序表达式
TypedSort<Person> person = Sort.sort(Person.class);
Sort sort = person.by(Person::getFirstname).ascending()
.and(person.by(Person::getLastname).descending());
TypedSort.by(…) 通过(通常)应用 CGlib 来应用运行时代理,这可能会在应用 Graal VM Native 等工具时烦扰本机映像编译。
如果您的商店实现反对 Querydsl,您还能够应用生成的元模型类型来定义排序表达式:
示例 38. 应用 Querydsl API 定义排序表达式
QSort sort = QSort.by(QPerson.firstname.asc())
.and(QSort.by(QPerson.lastname.desc()));