共计 9584 个字符,预计需要花费 24 分钟才能阅读完成。
原题目:Spring 认证 | 应用 Spring Data Repositories(上)
Spring Data repository 形象的指标是显着缩小为各种持久性存储实现数据拜访层所需的样板代码量。
Spring Data 存储库文档和您的模块
本章解释了 Spring Data 存储库的外围概念和接口。本章中的信息来自 Spring Data Commons 模块。它应用 Java Persistence API (JPA) 模块的配置和代码示例。您应该将 XML 命名空间申明和要扩大的类型调整为您应用的特定模块的等效项。“命名空间参考”涵盖了所有反对存储库 API 的 Spring Data 模块都反对的 XML 配置。“存储库查问关键字”涵盖了存储库形象反对的查询方法关键字。无关模块特定性能的详细信息,请参阅本文档中无关该模块的章节。
4.1. 外围概念
Spring Data 存储库形象中的核心接口是 Repository. 它须要域类来治理以及域类的 ID 类型作为类型参数。此接口次要用作标记接口,以捕捉要应用的类型并帮忙您发现扩大此接口的接口。该 CrudRepository 接口为正在治理的实体类提供简单的 CRUD 性能。
示例 5.CrudRepository 接口
public interface CrudRepository extends Repository {
S save(S entity);
Optional findById(ID primaryKey);
Iterable findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
// … more functionality omitted.
保留给定的实体、返回由给定 ID 标识的实体、返回所有实体、返回实体的数量、删除给定的实体、批示具备给定 ID 的实体是否存在。
咱们还提供特定于持久性技术的形象,例如 JpaRepository 或 MongoRepository。这些接口扩大 CrudRepository,并露出上面的长久化技术在另外的能力,以比拟通用的持久性与技术无关的接口,如 CrudRepository。
在 之上 CrudRepository,有一个 PagingAndSortingRepository 形象,它增加了额定的办法来简化对实体的分页拜访:
例 6.PagingAndSortingRepository 界面
public interface PagingAndSortingRepository extends CrudRepository {
Iterable findAll(Sort sort);
Page findAll(Pageable pageable);
要拜访 User 页面大小为 20 的第二页,您能够执行以下操作:
PagingAndSortingRepository repository = // … get access to a bean
Page users = repository.findAll(PageRequest.of(1, 20));
除了查询方法,计数和删除查问的查问派生也是可用的。以下列表显示了派生计数查问的接口定义:
示例 7. 派生计数查问
interface UserRepository extends CrudRepository {
long countByLastname(String lastname);
}
以下清单显示了派生删除查问的接口定义:
示例 8. 派生删除查问
interface UserRepository extends CrudRepository {
long deleteByLastname(String lastname);
List removeByLastname(String lastname);
}
4.2. 查询方法
规范 CRUD 性能存储库通常对底层数据存储进行查问。应用 Spring Data,申明这些查问变成了一个四步过程:
申明一个扩大 Repository 或其子接口之一的接口,并将其键入它应该解决的域类和 ID 类型,如以下示例所示:
interface PersonRepository extends Repository {…}
在接口上申明查询方法。
interface PersonRepository extends Repository {
List findByLastname(String lastname);
}
设置 Spring 以应用 JavaConfig 或 XML 配置为这些接口创立代理实例。
要应用 Java 配置,请创立一个相似于以下内容的类:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config {…}
要应用 XML 配置,请定义一个相似于以下内容的 bean:
本示例中应用了 JPA 命名空间。如果您对任何其余商店应用存储库形象,则须要将其更改为商店模块的适当命名空间申明。换句话说,您应该替换 jpa 以反对,例如 mongodb。
另请留神,JavaConfig 变体并未显式配置包,因为默认状况下应用带正文的类的包。要自定义要扫描的包,请应用 basePackage…特定于数据存储的存储库的 -@Enable$Repositories 正文的属性之一。
注入存储库实例并应用它,如以下示例所示:
class SomeClient {private final PersonRepository repository; SomeClient(PersonRepository repository) {this.repository = repository;} void doSomething() { List persons = repository.findByLastname(“Matthews”); }}
4.3. 定义存储库接口
要定义存储库接口,首先须要定义特定于域类的存储库接口。接口必须扩大 Repository 并键入域类和 ID 类型。如果要公开该域类型的 CRUD 办法,请应用扩大 CrudRepository 而不是 Repository.
4.3.1. 微调存储库定义
通常状况下,你的资料库接口扩大 Repository,CrudRepository 或 PagingAndSortingRepository。或者,如果您不想扩大 Spring Data 接口,也能够应用 @RepositoryDefinition. 扩大 CrudRepository 公开了一套残缺的办法来操作您的实体。如果您更违心抉择公开的办法,请将要公开的办法复制 CrudRepository 到域存储库中。
这样做能够让您在提供的 Spring Data Repositories 性能之上定义本人的形象。
上面的示例示出了如何以选择性地露出 CRUD 办法(findById 和 save,在这种状况下):
示例 9. 有选择地公开 CRUD 办法
@NoRepositoryBean
interface MyBaseRepository extends Repository {
Optional findById(ID id);
S save(S entity);
}
interface UserRepository extends MyBaseRepository {
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)的存储库:
示例 10. 应用模块特定接口的存储库定义
interface MyRepository extends JpaRepository {}
@NoRepositoryBean
interface MyBaseRepository extends JpaRepository {…}
interface UserRepository extends MyBaseRepository {…}
MyRepository 并在它们的类型层次结构中 UserRepository 扩大 JpaRepository。它们是 Spring Data JPA 模块的无效候选者。
以下示例显示了应用通用接口的存储库:
示例 11. 应用通用接口的存储库定义
interface AmbiguousRepository extends Repository {…}
@NoRepositoryBean
interface MyBaseRepository extends CrudRepository {…}
interface AmbiguousUserRepository extends MyBaseRepository {…}
AmbiguousRepository 和 AmbiguousUserRepository 仅延长 Repository,并 CrudRepository 在他们的类型档次。尽管这在应用惟一的 Spring Data 模块时很好,但多个模块无奈辨别这些存储库应该绑定到哪个特定的 Spring Data。
以下示例显示了应用带正文的域类的存储库:
示例 12. 应用带正文的域类的存储库定义
interface PersonRepository extends Repository {…}
@Entity
class Person {…}
interface UserRepository extends Repository {…}
@Document
class User {…}
PersonRepositoryReferences Person,它用 JPA@Entity 正文进行了正文,所以这个存储库显然属于 Spring Data JPA。UserRepositoryReferences User,应用 Spring Data MongoDB 的 @Document 注解进行注解。
以下谬误示例显示了应用具备混合正文的域类的存储库:
示例 13. 应用带有混合正文的域类的存储库定义
interface JpaPersonRepository extends Repository {…}
interface MongoDBPersonRepository extends Repository {…}
@Entity
@Document
class Person {…}
此示例显示应用 JPA 和 Spring Data MongoDB 正文的域类。它定义了两个存储库,JpaPersonRepository 以及 MongoDBPersonRepository. 一个用于 JPA,另一个用于 MongoDB。Spring Data 不再可能辨别存储库,这会导致未定义的行为。
存储库类型详细信息和辨别域类正文用于严格的存储库配置,以辨认特定 Spring Data 模块的存储库候选者。在同一域类型上应用多个特定于持久性技术的正文是可能的,并且容许跨多个持久性技术重用域类型。然而,Spring Data 无奈再确定与存储库绑定的惟一模块。
辨别存储库的最初一种办法是确定存储库根底包的范畴。根底包定义了扫描存储库接口定义的终点,这意味着存储库定义位于适当的包中。默认状况下,注解驱动的配置应用配置类的包。基于 XML 的配置中的根本包是必须的。
以下示例显示了根底包的正文驱动配置:
示例 14. 根底包的注解驱动配置
@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$Repositories。特定数据存储可能不反对某些策略。
CREATE 尝试从查询方法名称结构特定于商店的查问。个别的办法是从办法名称中删除一组给定的家喻户晓的前缀并解析办法的其余部分。您能够在“查问创立”中浏览无关查问结构的更多信息。
USE_DECLARED_QUERY 尝试查找已申明的查问,如果找不到则抛出异样。查问能够由某个中央的正文定义或通过其余形式申明。请参阅特定商店的文档以查找该商店的可用选项。如果存储库根底构造在疏导时未找到该办法的申明查问,则它会失败。
CREATE_IF_NOT_FOUND(默认)联合 CREATE 和 USE_DECLARED_QUERY。它首先查找申明的查问,如果没有找到申明的查问,它会创立一个自定义的基于办法名称的查问。这是默认的查找策略,因而,如果您没有明确配置任何内容,就会应用它。它容许按办法名称疾速定义查问,还容许通过依据须要引入申明的查问来自定义这些查问。
4.4.2. 查问创立
Spring Data 存储库根底构造中内置的查问构建器机制对于构建对存储库实体的束缚查问十分有用。
以下示例显示了如何创立多个查问:
示例 15. 从办法名称创立查问
interface PersonRepository extends Repository {
List findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List findByLastnameOrderByFirstnameAsc(String lastname);
List 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 findByAddressZipCode(ZipCode zipCode);
假如 aPerson 有 Address 一个 ZipCode。在这种状况下,该办法会创立 x.address.zipCode 属性遍历。解析算法首先将整个局部 (AddressZipCode) 解释为属性并查看具备该名称(未大写)的属性的域类。如果算法胜利,它将应用该属性。如果不是,则算法将来自右侧的驼峰式局部的源分成头部和尾部,并尝试找到相应的属性 – 在咱们的示例中,AddressZip 和 Code。如果算法找到具备该头部的属性,它会取尾部并持续从那里向下构建树,以方才形容的形式将尾部拆分。如果第一个宰割不匹配,算法将宰割点向左挪动 (Address,ZipCode) 并持续。
只管这应该实用于大多数状况,但算法可能会抉择谬误的属性。假如这个 Person 类也有一个 addressZip 属性。该算法将在第一个宰割轮中匹配,抉择谬误的属性,并失败(因为 的类型 addressZip 可能没有 code 属性)。
要解决这种歧义,您能够_在办法名称中应用手动定义遍历点。所以咱们的办法名称如下:
List findByAddress_ZipCode(ZipCode zipCode);
因为咱们将下划线字符视为保留字符,所以咱们强烈建议遵循规范的 Java 命名约定(即,不要在属性名称中应用下划线,而是应用驼峰式大小写)。
4.4.4. 非凡参数解决
要解决查问中的参数,请定义方法参数,如后面示例中所示。除此之外,基础设施辨认某些特定类型,如 Pageable 和 Sort,以动静地将分页和排序利用于您的查问。以下示例演示了这些性能:
示例 16. 在查询方法中应用 Pageable、Slice 和 Sort
Page findByLastname(String lastname, Pageable pageable);
Slice findByLastname(String lastname, Pageable pageable);
List findByLastname(String lastname, Sort sort);
List findByLastname(String lastname, Pageable pageable);
API 承受 Sort 并 Pageable 冀望将非 null 值传递给办法。如果您不想利用任何排序或分页,请应用 Sort.unsorted() 和 Pageable.unpaged()。
第一种办法容许您将 org.springframework.data.domain.Pageable 实例传递给查询方法,以将分页动静增加到动态定义的查问中。APage 晓得可用的元素和页面的总数。它通过根底构造触发计数查问来计算总数。因为这可能很低廉(取决于应用的商店),您能够改为返回 Slice. ASlice 只晓得下一个 Slice 是否可用,这在遍历更大的后果集时可能就足够了。
排序选项也通过 Pageable 实例解决。如果您只须要排序,请 org.springframework.data.domain.Sort 在您的办法中增加一个参数。如您所见,返回 aList 也是可能的。在这种状况下,Page 不会创立构建理论实例所需的额定元数据(这意味着不会收回原本须要的额定计数查问)。相同,它限度查问仅查找给定范畴的实体。
要理解整个查问取得了多少页,您必须触发额定的计数查问。默认状况下,此查问源自您理论触发的查问。
分页和排序
您能够应用属性名称定义简略的排序表达式。您能够连贯表达式以将多个条件收集到一个表达式中。
示例 17. 定义排序表达式
Sort sort = Sort.by(“firstname”).ascending()
.and(Sort.by(“lastname”).descending());
对于定义排序表达式的更类型平安的办法,从定义排序表达式的类型开始,并应用办法援用来定义排序的属性。
示例 18. 应用类型平安 API 定义排序表达式
TypedSort 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,您还能够应用生成的元模型类型来定义排序表达式:
示例 19. 应用 Querydsl API 定义排序表达式
QSort sort = QSort.by(QPerson.firstname.asc())
.and(QSort.by(QPerson.lastname.desc()));
内容起源:(Spring 中国教育管理中心)
应用 Spring Data Repositories,未完待续 …..