当前位置: 首页 > 后端技术 > Java

Spring认证中国教育管理中心-SpringDataCouchbase教程四

时间:2023-04-01 23:02:32 Java

原标题:Spring认证中国教育管理中心-SpringDataCouchbase教程四(Spring中国教育管理中心)SpringDataCouchbase教程四4.3。定义存储库接口定义存储库接口,首先需要定义特定于你的领域类的存储库接口。接口必须扩展Repository并导入到域类和ID类型。如果您想公开该域类型的CRUD方法,请扩展CrudRepository而不是Repository。4.3.1微调存储库定义通常,您的存储库接口扩展Repository、CrudRepository或PagingAndSortingRepository。或者,如果您不想扩展SpringData接口,您也可以使用@RepositoryDe??finition。扩展CrudRepository公开了一整套用于操作实体的方法。如果您想选择公开哪些方法,请将要公开的方法从CrudRepository复制到您的域存储库。这样做可以让您在提供的SpringDataRepositories功能之上定义自己的抽象。以下示例展示了如何有选择地公开CRUD方法(在本例中为findById和save):示例28.有选择地公开CRUD方法@NoRepositoryBeaninterfaceMyBaseRepositoryextendsRepository{OptionalfindById(IDid);Ssave(Sentity);}interfaceUserRepositoryextendsMyBaseRepository{UserfindByEmailAddress(EmailAddressemailAddress);}在前面的例子中,你定义为所有站点库都有一个通用的基础接口并公开findById(...)和save(...)。这些方法被发送到您选择的由Spring提供的底层存储库实现(例如,如果使用JPA存储,则实现为SimpleJpaRepository),因为它们与CrudRepository兼容。所以UserRepository现在可以存储用户,通过ID查找单个用户,并触发查询以通过电子邮件地址查找用户。中间存储库接口标有@NoRepositoryBean。确保将此注释添加到SpringData不应在运行时为其创建实例的所有存储库接口。4.3.2.使用具有多个SpringData模块的存储库在应用程序中拥有唯一的SpringData模块使事情变得更容易,因为定义范围内的所有存储库接口都绑定到SpringData模块。有时一个应用程序需要使用多个SpringData模块。在这种情况下,存储库定义必须区分持久性技术。当在类路径上检测到多个存储库工厂时,SpringData进入严格的存储库配置模式。严格配置使用存储库或域类的详细信息来决定存储库定义的SpringData模块绑定:如果存储库定义扩展了特定于模块的存储库,则它是特定SpringData模块的有效候选者。如果使用特定于模块的类型注释进行注释,则域类是特定SpringData模块的有效候选者。SpringData模块接受第三方注解(如JPA的@Entity)或提供自己的注解(如SpringDataMongoDB和SpringDataElasticsearch的@Document)。以下示例显示了使用模块特定接口(在本例中为JPA)的存储库:示例29.使用模块特定接口接口的存储库定义interfaceMyRepositoryextendsJpaRepository{}@NoRepositoryBeaninterfaceMyBaseRepositoryextendsJpaRepository{...}interfaceUserRepositoryextendsMyBaseRepository{...}MyRepository和UserRepository在其类型层次结构中扩展了JpaRepository。它们是SpringDataJPA模块的有效候选者。以下示例显示了使用通用接口的存储库:示例30.使用通用接口定义存储库interfaceAmbiguousUserRepositoryextendsMyBaseRepository{...}AmbiguousRepository和AmbiguousUserRepository仅在其类型层次结构中扩展Repository和CrudRepository。虽然这在使用唯一的SpringData模块时很好,但是多个模块无法区分这些存储库应该绑定到哪个特定的SpringData。以下示例显示了使用带注释的域类的存储库:示例31.使用带注释的域类的存储库定义>{...}@DocumentclassUser{...}PersonRepositoryreferencesPerson,使用JPA@Entity注解进行注解,因此这个存储库显然属于SpringDataJPA。UserRepositoryreferences用户,使用SpringDataMongoDB的@Document注解进行注解。以下错误示例显示了使用带有混合注释的域类的存储库:示例32.使用带有混合注释的域类的存储库定义..}@Entity@DocumentclassPerson{...}此示例显示了一个使用JPA和SpringDataMongoDB注释的域类。它定义了两个存储库,JpaPersonRepository和MongoDBPersonRepository。一个用于JPA,一个用于MongoDB。SpringData不再能够区分存储库,这会导致未定义的行为。存储库类型详细信息和域类注释用于严格的存储库配置,以识别特定SpringData模块的存储库候选者。可以在同一域类型上使用多个持久性技术特定的注释,并且可以跨多种持久性技术重用域类型。但是,SpringData无法再确定绑定存储库的唯一模块。区分存储库的最后一种方法是确定存储库基础包的范围。基本包定义了扫描存储库接口定义的起点,这意味着将存储库定义放在适当的包中。默认情况下,注释驱动的配置使用一组配置类。需要基于XML配置的基础包。以下示例显示了基础包的注释驱动配置:示例33.基础包的注释驱动配置@EnableJpaRepositories(basePackages="com.acme.repositories.jpa")@EnableMongoRepositories(basePackages="com.acme.repositories.mongo")类配置{...}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.查询创建SpringData存储库基础结构中内置的查询构建器机制对于在存储库实体上构建受限查询非常有用。以下示例显示了如何创建多个查询:示例34.从方法名称创建查询ListfindDistinctPeopleByLastnameOrFirstname(Stringlastname,Stringfirstname);ListfindPeopleDistinctByLastnameOrFirstname(Stringlastname,Stringfirstname);//为单个属性启用忽略大小写Stringfirstname);//为查询启用静态ORDERBYListfindByLastnameOrderByFirstnameAsc(Stringlastname);ListfindByLastnameOrderByFirstnameDesc);名称分为主语和谓语。第一部分(find…By,exists…By)定义查询的主题,第二部分构成谓词。介绍性从句(主语)可以包含更多的表达。find(或其他介绍性关键字)和By之间的任何文本都被认为是描述性的,除非使用了结果限制关键字之一,例如Distinct在要创建的查询上设置不同的标志或Top/First限制搜索结果。附录包含查询方法主题关键字和查询方法谓词关键字的完整列表,包括排序和字母大小写修饰符。但是,第一个By用作分隔符以指示实际条件谓词的开始。在非常基本的层面上,您可以在实体属性上定义条件并将它们与And和Or连接起来。parse方法的实际结果取决于您为其创建查询的持久存储。但是,有一些一般性的事情需要注意:表达式通常是属性遍历与可以连接的运算符相结合。您可以使用带有AND和OR的属性表达式。您还可以获得诸如属性的Between、LessThan、GreaterThan和Like表达式的道具等运算符。支持的运算符可能因数据存储而异,因此请参阅参考文档的相应部分。方法解析器支持单个属性的IgnoreCase标志(例如,findByLastnameIgnoreCase(…))或支持忽略大小写的类型的所有属性(通常是String实例-例如,findByLastnameAndFirstnameAllIgnoreCase(…))。对忽略大小写的支持可能因商店而异,因此请参阅参考文档中的相关部分以了解特定于商店的查询方法。您可以通过将OrderBy子句附加到引用该属性并提供排序方向(Asc或Desc)的查询方法来应用静态排序。要创建支持动态排序的查询方法,请参阅“特殊参数处理”。4.4.3.属性表达式属性表达式只能引用托管实体的直接属性,如前面的示例所示。创建查询时,您已确保解析的属性是托管域类的属性。但是,您也可以通过遍历嵌套属性来定义约束。考虑以下方法签名:ListfindByAddressZipCode(ZipCodezipCode);假设aPerson有Address和ZipCode。在这种情况下,该方法创建一个x.address.zipCode属性遍历。解析算法首先将整个部分(AddressZipCode)解释为一个属性,并在域类中检查具有该名称(未大写)的属性。如果算法成功,它将使用此属性。如果不是,该算法将从右边的驼峰命名法将源拆分为头和尾,并尝试找到相应的属性-在我们的示例中为AddressZip和Code。如果该算法找到具有该头部的属性,它会获取尾部并继续从那里向下构建树,以刚才描述的方式拆分尾部。如果第一个拆分不匹配,则算法将拆分点向左移动(Address,ZipCode)并继续。虽然这在大多数情况下应该有效,但算法可能会选择错误的属性。假设这个Person类也有一个addressZip属性。该算法在第一轮拆分中已经匹配,选择了错误的属性,然后失败了(因为类型addressZip可能没有代码属性)。要解决这种歧义,您可以在方法名称中使用_手动定义遍历点。所以我们的方法名如下:ListfindByAddress_ZipCode(ZipCodezipCode);由于我们将下划线字符视为保留字符,我们强烈建议遵循标准的Java命名约定(即不要在属性名称中使用下划线,使用驼峰式大小写)。4.4.4.特殊参数处理要处理查询中的参数,请定义前面示例中已经看到的方法参数。除其他事项外,基础架构还可以识别某些类型,例如可分页和排序,以动态地将分页和排序应用于您的查询。以下示例演示了这些功能:示例35.在查询方法中使用Pageable、Slice和SortPage,排序排序);ListfindByLastname(Stringlastname,Pageablepageable);API接受Sort并且Pageable期望将非空值传递给该方法。如果您不想应用任何排序或分页,请使用Sort.unsorted()和Pageable.unpaged()。第一种方法允许您将org.springframework.data.domain.Pageable实例传递给查询方法,以便为静态定义的查询动态添加分页。APage知道可用元素和页面的总数。它通过触发基础架构上的计数查询来计算总数来实现这一点。由于这可能很昂贵(取决于所使用的商店),您可以改为返回一个Slice。ASlice只知道下一个Slice是否可用,这在遍历更大的结果集时可能就足够了。排序选项也通过Pageable实例处理。如果您只需要排序,请将org.springframework.data.domain.Sort参数添加到您的方法中。如您所见,返回aList也是可能的。在这种情况下,页面不会创建构建实际实例所需的额外元数据(这反过来意味着不会发出原本需要的额外计数查询)。相反,它将查询限制为仅查找给定范围内的实体。要知道整个查询有多少页,您必须触发一个额外的计数查询。默认情况下,此查询派生自您实际触发的查询。分页和排序您可以使用属性名称定义简单的排序表达式。您可以连接表达式以将多个条件收集到一个表达式中。示例36.定义排序表达式Sortsort=Sort.by("firstname").ascending().and(Sort.by("lastname").descending());要使用更安全的方式来定义排序表达式,请从要定义排序表达式的类型开始,然后使用方法引用来定义要排序的属性。示例37.使用类型安全API定义排序表达式TypedSortperson=Sort.sort(Person.class);Sortsort=person.by(Person::getFirstname).ascending().and(person.by(Person::getLastname).descending());TypedSort.by(…)通过(通常)使用CGlib使用运行时代理,这在使用GraalVMNative等工具时可能会干扰本机图像编译。如果您的商店实现支持Querydsl,您还可以使用生成的元模型类型来定义排序表达式:示例38.使用QuerydslAPI定义排序表达式QSortsort=QSort.by(QPerson.firstname.asc()).and(QSort.通过(QPerson.lastname.desc()));