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

2022-01-19 16:49:18 浏览数 (1)

原标题:Spring认证中国教育管理中心-Spring Data Neo4j教程四(Spring中国教育管理中心)

6.3.3.一般建议

  • 尝试坚持使用不可变对象 ——不可变对象很容易创建,因为实现对象只需调用其构造函数即可。此外,这可以防止您的域对象被允许客户端代码操纵对象状态的 setter 方法乱扔垃圾。如果您需要这些,最好将它们包保护起来,以便它们只能被有限数量的并置类型调用。仅构造函数实现比属性填充快 30%。
  • 提供一个全参数的构造函数 ——即使你不能或不想将你的实体建模为不可变值,提供一个将实体的所有属性作为参数(包括可变属性)的构造函数仍然有价值,因为这允许对象映射以跳过属性填充以获得最佳性能。
  • 使用工厂方法而不是重载的构造函数来避免@PersistenceConstructor - 使用最佳性能所需的全参数构造函数,我们通常希望公开更多特定于应用程序用例的构造函数,这些构造函数省略自动生成的标识符等内容。这是一种既定的模式,而不是使用静态工厂方法来公开这些全参数构造函数的变体。
  • 确保遵守允许使用生成的实例化器和属性访问器类的约束
  • 对于要生成的标识符,仍然使用 final 字段结合 wither 方法
  • 使用 Lombok 避免样板代码 - 由于持久性操作通常需要一个构造函数来获取所有参数,因此它们的声明变成了对字段分配的样板参数的繁琐重复,使用 Lombok 可以最好地避免这种情况@AllArgsConstructor。

关于不可变映射的说明

尽管我们建议尽可能使用不可变映射和构造,但在映射方面存在一些限制。给定一个双向关系,其中A有一个构造函数引用B和B一个引用A,或者更复杂的场景。Spring Data Neo4j 无法解决这种先有后有的情况。在它的实例化过程中,A它迫切需要一个完全实例化的B另一方面,它需要一个. SDN 通常允许这样的模型,但会抛出一个AMappingException如果从数据库返回的数据包含上述星座,则在运行时。在这种情况下,您无法预见返回的数据是什么样的,您更适合使用可变字段来处理关系。

6.3.4.Kotlin 支持

Spring Data 调整了 Kotlin 的细节以允许对象创建和变异。

Kotlin 对象创建

Kotlin 类支持实例化,默认情况下所有类都是不可变的,需要明确的属性声明来定义可变属性。考虑以下data类Person:

代码语言:javascript复制
data class Person(val id: String, val name: String)

上面的类编译为具有显式构造函数的典型类。我们可以通过添加另一个构造函数来自定义这个类,并用注释@PersistenceConstructor来指示构造函数的偏好:

代码语言:javascript复制
data class Person(var id: String, val name: String) {

    @PersistenceConstructor
    constructor(id: String) : this(id, "unknown")
}

Kotlin 通过在未提供参数时允许使用默认值来支持参数可选性。当 Spring Data 检测到具有参数默认值的构造函数时,如果数据存储不提供值(或简单地返回null),它将使这些参数不存在,因此 Kotlin 可以应用参数默认值。考虑以下应用参数默认值的类name

代码语言:javascript复制
data class Person(var id: String, val name: String = "unknown")

每次name参数不是结果的一部分或其值为null时,name默认为unknown。

Kotlin 数据类的属性总体

在 Kotlin 中,默认情况下所有类都是不可变的,并且需要显式的属性声明来定义可变属性。考虑以下data类Person:

代码语言:javascript复制
data class Person(val id: String, val name: String)

这个类实际上是不可变的。它允许创建新实例,因为 Kotlin 生成一个copy(…)创建新对象实例的方法,该方法从现有对象复制所有属性值并将作为参数提供的属性值应用到该方法。

7. 使用 Spring 数据存储库

Spring Data repository 抽象的目标是显着减少为各种持久性存储实现数据访问层所需的样板代码量。

Spring Data 存储库文档和您的模块

本章介绍 Spring Data 存储库的核心概念和接口。本章中的信息来自 Spring Data Commons 模块。它使用 Java Persistence API (JPA) 模块的配置和代码示例。您应该调整 XML 名称空间声明和要扩展的类型,以适应您使用的特定模块的等效项。“ [ repositories.namespace-reference] ”涵盖了 XML 配置,所有支持存储库 API 的 Spring Data 模块都支持该配置。“附录A ”涵盖了存储库抽象一般支持的查询方法关键字。

7.1核心概念

Spring Data 存储库抽象中的中央接口是Repository. 它需要域类来管理以及域类的 ID 类型作为类型参数。此接口主要用作标记接口,以捕获要使用的类型并帮助您发现扩展此接口的接口。该CrudRepository接口为被管理的实体类提供了复杂的 CRUD 功能。

示例 13.CrudRepository接口

代码语言:javascript复制
public interface CrudRepository<T, ID> extends Repository<T, ID> {

  <S extends T> S save(S entity);      

  Optional<T> findById(ID primaryKey); 

  Iterable<T> findAll();               

  long count();                        

  void delete(T entity);               

  boolean existsById(ID primaryKey);   

  // … more functionality omitted.
}

保存给定的实体。

返回由给定 ID 标识的实体。

返回所有实体。

返回实体的数量。

删除给定的实体。

指示具有给定 ID 的实体是否存在。

我们还提供了特定于持久性技术的抽象,例如JpaRepository或MongoRepository。CrudRepository除了相当通用的与持久性技术无关的接口(例如CrudRepository.

在 之上CrudRepository,还有一个 PagingAndSortingRepository抽象,它添加了额外的方法来简化对实体的分页访问:

示例 14.PagingAndSortingRepository界面

代码语言:javascript复制
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

要访问User页面大小为 20 的第二页,您可以执行以下操作:

代码语言:javascript复制
PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(PageRequest.of(1, 20));

除了查询方法之外,还可以使用计数和删除查询的查询派生。以下列表显示了派生计数查询的接口定义:

示例 15. 派生计数查询

代码语言:javascript复制
interface UserRepository extends CrudRepository<User, Long> {

  long countByLastname(String lastname);
}

以下清单显示了派生删除查询的接口定义:

示例 16. 派生删除查询

代码语言:javascript复制
interface UserRepository extends CrudRepository<User, Long> {

  long deleteByLastname(String lastname);

  List<User> removeByLastname(String lastname);
}

7.2.查询方法

标准 CRUD 功能存储库通常对底层数据存储进行查询。使用 Spring Data,声明这些查询变成了一个四步过程:

1.声明一个扩展 Repository 或其子接口之一的接口,并将其键入应处理的域类和 ID 类型,如以下示例所示:

代码语言:javascript复制
interface PersonRepository extends Repository<Person, Long> { … }

2.在接口上声明查询方法。

代码语言:javascript复制
interface PersonRepository extends Repository<Person, Long> {
  List<Person> findByLastname(String lastname);
}

3.设置 Spring 以使用JavaConfig或XML configuration为这些接口创建代理实例。

a.要使用 Java 配置,请创建一个类似于以下内容的类:

代码语言:javascript复制
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaRepositories
class Config { … }

b.要使用 XML 配置,请定义一个类似于以下内容的 bean:

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jpa="http://www.springframework.org/schema/data/jpa"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
     https://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/data/jpa
     https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

   <jpa:repositories base-package="com.acme.repositories"/>

</beans>

此示例中使用了 JPA 命名空间。如果您将存储库抽象用于任何其他存储,则需要将其更改为存储模块的适当命名空间声明。换句话说,您应该交换jpa,例如,mongodb。另外请注意,JavaConfig 变体没有显式配置包,因为默认使用带注释的类的包。要自定义要扫描的包,请使用特定于数据存储的存储库的-annotation的basePackage…属性之一。@Enable${store}Repositories

4.注入存储库实例并使用它,如下例所示:

代码语言:javascript复制
class SomeClient {

  private final PersonRepository repository;

  SomeClient(PersonRepository repository) {
    this.repository = repository;
  }

  void doSomething() {
    List<Person> persons = repository.findByLastname("Matthews");
  }
}

.3.定义存储库接口

要定义存储库接口,您首先需要定义特定于域类的存储库接口。接口必须扩展Repository并输入到域类和 ID 类型。如果要公开该域类型的 CRUD 方法,请扩展CrudRepository而不是Repository.

7.3.1.微调存储库定义

通常,您的存储库接口会扩展Repository、CrudRepository或 PagingAndSortingRepository. 或者,如果您不想扩展 Spring Data 接口,也可以使用@RepositoryDefinition. 扩展CrudRepository公开了一整套操作实体的方法。如果您希望对公开的方法有选择性,请将要公开的方法复制CrudRepository到您的域存储库中。

这样做可以让您在提供的 Spring Data Repositories 功能之上定义自己的抽象。

以下示例显示了如何选择性地公开 CRUD 方法(在本例中为findById和save):

示例 17. 选择性地公开 CRUD 方法

代码语言:javascript复制
@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 Data 提供的您选择的存储的基本存储库实现(例如,如果您使用 JPA,实现是SimpleJpaRepository),因为它们与CrudRepository. 所以UserRepository现在可以保存用户,通过 ID 查找单个用户,并触发查询以Users通过电子邮件地址查找。

中间存储库接口用@NoRepositoryBean. 确保将该注释添加到 Spring Data 不应在运行时为其创建实例的所有存储库接口。

7.3.2.将存储库与多个 Spring 数据模块一起使用

在应用程序中使用唯一的 Spring Data 模块会使事情变得简单,因为定义范围内的所有存储库接口都绑定到 Spring Data 模块。有时,应用程序需要使用多个 Spring Data 模块。在这种情况下,存储库定义必须区分持久性技术。当检测到类路径上有多个存储库工厂时,Spring Data 进入严格的存储库配置模式。严格配置使用存储库或域类的详细信息来决定存储库定义的 Spring Data 模块绑定:

  1. 如果存储库定义扩展了特定于模块的存储库,则它是特定 Spring Data 模块的有效候选者。
  2. 如果域类使用特定于模块的类型注释进行注释,则它是特定 Spring Data 模块的有效候选者。Spring Data 模块接受第三方注解(例如 JPA's @Entity)或提供自己的注解(例如@DocumentSpring Data MongoDB 和 Spring Data Elasticsearch)。

以下示例显示了使用特定于模块的接口(在本例中为 JPA)的存储库:

示例 18. 使用模块特定接口的存储库定义

代码语言:javascript复制
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 模块的有效候选者。

以下示例显示了使用通用接口的存储库:

示例 19. 使用通用接口的存储库定义

代码语言:javascript复制
interface AmbiguousRepository extends Repository<User, Long> { … }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }

AmbiguousRepository并仅在其类型层次结构中AmbiguousUserRepository扩展。虽然在使用唯一的 Spring Data 模块时这很好,但多个模块无法区分这些存储库应该绑定到哪个特定的 Spring Data。RepositoryCrudRepository

以下示例显示了一个使用带注释的域类的存储库:

示例 20. 使用带注释的域类的存储库定义

代码语言:javascript复制
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注解。

以下错误示例显示了一个使用具有混合注释的域类的存储库:

示例 21. 使用具有混合注释的域类的存储库定义

代码语言:javascript复制
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 的配置中的基本包是必需的。

以下示例显示了基本包的注释驱动配置:

示例 22. 基本包的注释驱动配置

代码语言:javascript复制
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }

0 人点赞