原标题:Spring认证中国教育管理中心-Spring Data Neo4j教程四(Spring中国教育管理中心)
尽管我们建议尽可能使用不可变映射和构造,但在映射方面存在一些限制。给定一个双向关系,其中A有一个构造函数引用B和B一个引用A,或者更复杂的场景。Spring Data Neo4j 无法解决这种先有后有的情况。在它的实例化过程中,A它迫切需要一个完全实例化的,B另一方面,它需要一个. SDN 通常允许这样的模型,但会抛出一个AMappingException如果从数据库返回的数据包含上述星座,则在运行时。在这种情况下,您无法预见返回的数据是什么样的,您更适合使用可变字段来处理关系。
Spring Data 调整了 Kotlin 的细节以允许对象创建和变异。
Kotlin 类支持实例化,默认情况下所有类都是不可变的,需要明确的属性声明来定义可变属性。考虑以下data类Person:
data class Person(val id: String, val name: String)
上面的类编译为具有显式构造函数的典型类。我们可以通过添加另一个构造函数来自定义这个类,并用注释@PersistenceConstructor来指示构造函数的偏好:
data class Person(var id: String, val name: String) {
@PersistenceConstructor
constructor(id: String) : this(id, "unknown")
}
Kotlin 通过在未提供参数时允许使用默认值来支持参数可选性。当 Spring Data 检测到具有参数默认值的构造函数时,如果数据存储不提供值(或简单地返回null),它将使这些参数不存在,因此 Kotlin 可以应用参数默认值。考虑以下应用参数默认值的类name
data class Person(var id: String, val name: String = "unknown")
每次name参数不是结果的一部分或其值为null时,name默认为unknown。
在 Kotlin 中,默认情况下所有类都是不可变的,并且需要显式的属性声明来定义可变属性。考虑以下data类Person:
data class Person(val id: String, val name: String)
这个类实际上是不可变的。它允许创建新实例,因为 Kotlin 生成一个copy(…)创建新对象实例的方法,该方法从现有对象复制所有属性值并将作为参数提供的属性值应用到该方法。
Spring Data repository 抽象的目标是显着减少为各种持久性存储实现数据访问层所需的样板代码量。
Spring Data 存储库文档和您的模块
本章介绍 Spring Data 存储库的核心概念和接口。本章中的信息来自 Spring Data Commons 模块。它使用 Java Persistence API (JPA) 模块的配置和代码示例。您应该调整 XML 名称空间声明和要扩展的类型,以适应您使用的特定模块的等效项。“ [
repositories.namespace-reference] ”涵盖了 XML 配置,所有支持存储库 API 的 Spring Data 模块都支持该配置。“附录A ”涵盖了存储库抽象一般支持的查询方法关键字。
Spring Data 存储库抽象中的中央接口是Repository. 它需要域类来管理以及域类的 ID 类型作为类型参数。此接口主要用作标记接口,以捕获要使用的类型并帮助您发现扩展此接口的接口。该CrudRepository接口为被管理的实体类提供了复杂的 CRUD 功能。
示例 13.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抽象,它添加了额外的方法来简化对实体的分页访问:
示例
14.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));
除了查询方法之外,还可以使用计数和删除查询的查询派生。以下列表显示了派生计数查询的接口定义:
示例 15. 派生计数查询
interface UserRepository extends CrudRepository {
long countByLastname(String lastname);
}
以下清单显示了派生删除查询的接口定义:
示例 16. 派生删除查询
interface UserRepository extends CrudRepository {
long deleteByLastname(String lastname);
List removeByLastname(String lastname);
}
标准 CRUD 功能存储库通常对底层数据存储进行查询。使用 Spring Data,声明这些查询变成了一个四步过程:
1.声明一个扩展 Repository 或其子接口之一的接口,并将其键入应处理的域类和 ID 类型,如以下示例所示:
interface PersonRepository extends Repository { … }
2.在接口上声明查询方法。
interface PersonRepository extends Repository {
List findByLastname(String lastname);
}
3.设置 Spring 以使用JavaConfig或XML configuration为这些接口创建代理实例。
a.要使用 Java 配置,请创建一个类似于以下内容的类:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config { … }
b.要使用 XML 配置,请定义一个类似于以下内容的 bean:
此示例中使用了 JPA 命名空间。如果您将存储库抽象用于任何其他存储,则需要将其更改为存储模块的适当命名空间声明。换句话说,您应该交换jpa,例如,mongodb。另外请注意,JavaConfig 变体没有显式配置包,因为默认使用带注释的类的包。要自定义要扫描的包,请使用特定于数据存储的存储库的-annotation的basePackage…属性之一。@Enable${store}Repositories
4.注入存储库实例并使用它,如下例所示:
class SomeClient {
private final PersonRepository repository;
SomeClient(PersonRepository repository) {
this.repository = repository;
}
void doSomething() {
List persons = repository.findByLastname("Matthews");
}
}
要定义存储库接口,您首先需要定义特定于域类的存储库接口。接口必须扩展Repository并输入到域类和 ID 类型。如果要公开该域类型的 CRUD 方法,请扩展CrudRepository而不是Repository.
通常,您的存储库接口会扩展Repository、CrudRepository或
PagingAndSortingRepository. 或者,如果您不想扩展 Spring Data 接口,也可以使用@RepositoryDefinition. 扩展CrudRepository公开了一整套操作实体的方法。如果您希望对公开的方法有选择性,请将要公开的方法复制CrudRepository到您的域存储库中。
这样做可以让您在提供的 Spring Data Repositories 功能之上定义自己的抽象。
以下示例显示了如何选择性地公开 CRUD 方法(在本例中为findById和save):
示例 17. 选择性地公开 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 Data 提供的您选择的存储的基本存储库实现(例如,如果您使用 JPA,实现是SimpleJpaRepository),因为它们与CrudRepository. 所以UserRepository现在可以保存用户,通过 ID 查找单个用户,并触发查询以Users通过电子邮件地址查找。
中间存储库接口用@NoRepositoryBean. 确保将该注释添加到 Spring Data 不应在运行时为其创建实例的所有存储库接口。
在应用程序中使用唯一的 Spring Data 模块会使事情变得简单,因为定义范围内的所有存储库接口都绑定到 Spring Data 模块。有时,应用程序需要使用多个 Spring Data 模块。在这种情况下,存储库定义必须区分持久性技术。当检测到类路径上有多个存储库工厂时,Spring Data 进入严格的存储库配置模式。严格配置使用存储库或域类的详细信息来决定存储库定义的 Spring Data 模块绑定:
以下示例显示了使用特定于模块的接口(在本例中为 JPA)的存储库:
示例 18. 使用模块特定接口的存储库定义
interface MyRepository extends JpaRepository { }
@NoRepositoryBean
interface MyBaseRepository extends JpaRepository { … }
interface UserRepository extends MyBaseRepository { … }
MyRepository并在它们的类型层次结构中UserRepository扩展。JpaRepository它们是 Spring Data JPA 模块的有效候选者。
以下示例显示了使用通用接口的存储库:
示例 19. 使用通用接口的存储库定义
interface AmbiguousRepository extends Repository { … }
@NoRepositoryBean
interface MyBaseRepository extends CrudRepository { … }
interface AmbiguousUserRepository extends MyBaseRepository { … }
AmbiguousRepository并仅在其类型层次结构中AmbiguousUserRepository扩展。虽然在使用唯一的 Spring Data 模块时这很好,但多个模块无法区分这些存储库应该绑定到哪个特定的 Spring Data。RepositoryCrudRepository
以下示例显示了一个使用带注释的域类的存储库:
示例 20. 使用带注释的域类的存储库定义
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注解。
以下错误示例显示了一个使用具有混合注释的域类的存储库:
示例 21. 使用具有混合注释的域类的存储库定义
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 的配置中的基本包是必需的。
以下示例显示了基本包的注释驱动配置:
示例 22. 基本包的注释驱动配置
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }