Spring Data JPA 文档

Spring Data Jpa 学习笔记

1. 核心概念

Spring Data 的核心是 Repository 接口。它将实体类以及实体类的 ID 类型作为类型参数进行管理。此接口主要用作标记接口,用于捕获要使用的类型,并帮助您发现扩展此接口的接口。查看源码可以发现 Repository 什么内容也没有, 说明它就是作为一个标记接口使用的。

@Indexed
public interface Repository<T, ID> {
}

继承了 Repository 接口的 CrudRepository 接口为我们提供针对已实现 Repository 接口或其子接口的的实体类复杂的 CRUD 操作(即增删改查)。

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    // 根据实体对象更新到数据库,对应于 MySQL 中的 insert 、update
    <S extends T> S save(S var1);
    // 没有使用过,试试####
    <S extends T> Iterable<S> saveAll(Iterable<S> var1);
    // 根据实体类的主键查找实体对象
    Optional<T> findById(ID var1);
    // 根据实体类的主键判断实体对象是否存在与数据库中
    boolean existsById(ID var1);
    // 没有使用过,试试####
    Iterable<T> findAll();
    // 没有使用过,试试####
    Iterable<T> findAllById(Iterable<ID> var1);
    // 这个是真的好用,直接根据某一列就可以统计
    long count();
    // 根据实体类的主键删除某一条记录
    void deleteById(ID var1);
    // 根据实体对象删除相应的记录
    void delete(T var1);
    // 没有使用过,试试####
    void deleteAll(Iterable<? extends T> var1);
    // 没有使用过,试试####
    void deleteAll();
}

Spring Data 还提供特定于持久性技术的抽象,比如特定于 MySQL 数据库的 JpaRepository 和特定于 MongoDB 数据库的 MongoRepository 。这些接口继承了 CrudRepository 接口并且提供了提供通用的持久化技术如增删改查等和底层持久化技术。

// 没有使用过,试试####
PagingAndSortingRepository 接口继承了 CrudRepository 接口,并提供了额外的分页查找功能方法。

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort sort);
    Page<T> findAll(Pageable pageable);
}

举一个例子,为了获得 size 为 20 的第 2 页 User ,我应该先获得一个 Bean,然后提起分页请求来查找第 2 页的 20 个 User:

PagingAndSortingRepository<User, Long> repository = // ... get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));

除了查询方法之外,还可以使用派生的 count 和 delete 。

public interface UserRepository extends CrudRepository<User, Long> {
    long countByLastname(String lastname);
    long deleteByLastname(String lastname);
    List<User> removeByLastname(String lastname);
}

2. 查询方法

标准的 CRUD 功能存储库通常可以查询底层的数据存储。使用 Spring Data 声明这些查询将分为 4 个步骤:
1. 声明一个继承 Repository 或其子接口的接口,并且填写接口应处理的实体类和 ID 类型。
2. 在接口中声明查询方法。

interface PersonRepository extends Repository<Person, Long> {
    List<Person> findByLastname(String lastname);
}
  1. 使用 JavaConfigXML configuration 为这些接口创建 Spring 代理对象。
    a. 使用 JavaConfiguraion 将创建类似于下面的类:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config {}

b. 使用 XML configuration 将声明类似于下面的 Bean:

<?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
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    <jpa:repositories base-package="com.acme.repositories"/>
</beans>

在这个例子中使用的是 Jpa 命名空间。我们如果要使用其他模型的 Repository 接口,就需要更改为相应模块的命名空间声明。另外,由于默认使用已注解的包,JavaConfig 变体将不需要显式配置包。为了扫描自定义的包,必须使用 @Enable${store}Repository 注解的 base-package 属性。
4. 注入 Repository 实例并使用它,例子如下:

class SomeClient {
    private final PersonRepository repository;
    SomeClient(PersonRepository repository) {
        this.repository = repository;
    }
    void doSomething() {
        List<Person> persons = repository.findByLastname("Matthews");
    }
}

3. 定义 Repository 接口

首先,定义一个实体类特定的 Repository 接口。这个接口必须继承 Repository 接口,并且填写相应的实体类和 ID 类型。如果想要使用 CRUD 方法,应该继承 CrudRepository 接口。

3.1 Repository 定义

通常,自定义的 Repository 接口应该继承 Repository ,CrudRepository 或 PagingAndSortingRepository 接口。但是,要是不想继承 Spring Data 接口,可以使用 @RepositoryDefinition 来注解自定义的 Repository 接口。继承 CrudRepository 接口将公开一整套操作实体的方法。如果想要有选择地公开方法,从 CrudRepository 接口中复制你想要公开的方法到实体类 Repository 中去。
以下示例显示如何有选择地公开 CRUD 方法(在本例中为findById和save):

@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> 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);
}

中间的 Repository 接口应该用 @NoRepositoryBean 注解。在运行时 Spring Data 不应创建实例的接口上必须添加该注解。

3.2 Repository 方法的 Null 处理

在 Spring Data 2.0,Repository 中返回单个聚合实例的 CRUD 方法将使用 Java 8 的 Optional 类型来表明可能空值。除了 Java 8 的 Optional 类型以外,Spring Data 支持查询方法返回以下包装类型:
- com.google.common.base.Optional
- scala.Option
- io.vavr.control.Option
- javaslang.control.Option (deprecated as Javaslang is deprecated)

另外,查询方法可以选择不使用任何的包装类型。如果查询结果没有数据意味着将返回一个 null 。如果返回类型是集合、包装器或流的 Repository 方法将不返回 null 值,而是返回相应的空表示。细节部分请看“Repository query return types”。

Null 注解

通过使用 Spring Framework’s nullability annotions 可以为 Repository 方法表示空的约束。它们提供了一个工具友好的方法和运行时的 null 检查,如下所示:
- @NonNullApi:在包级别上声明参数或返回值不接受或产生 null 值。
- @NonNull:声明参数或返回值不能为 null 。
- @Nullable:声明参数或返回值可以为 null 。

一旦设置默认是 non-null,调用 Repository 的查询方法将在运行时验证可空性约束。如果查询执行结果违反了定义的约束,则抛出异常。这将发生在声明了非 null 但方法返回 null 的情况下。如果想再次使用可 null 的结果,可在各个方法上有选择地使用 @Nullable 。使用在开头部分提到的结果包装器类型继续按预期工作:一个空的结果表示没有值。
以下例子展示了刚才描述的许多技术:

// Repository 所在包被定义为 non-null 行为
@org.springframework.lang.NonNullApi;
package com.acme;                                                       
import org.springframework.lang.Nullable;
public interface UserRepository extends Repository<User, Long> {
    // 当执行的查询未产生结果时,抛出 EmptyResultDataAccessException 。
    // 当传递给方法的 emailAddress 为 null 时,抛出 IllegalArgumentException 。
    User getByEmailAddress(EmailAddress emailAddress);                    
    // 当执行的查询未产生结果时返回 null 。
    // 同时接受 null 作为 emailAddress 的值。
    @Nullable
    User findByEmailAddress(@Nullable EmailAddress emailAdress);          
    // 当执行的查询未产生结果时返回 Optional.empty()。
    // 当传递给方法的 emailAddress 为 null 时,抛出 IllegalArgumentException 。
    Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); 
}

3.3 使用多 Spring 数据模块的存储库

在应用中使用单一的 Spring 数据模块使事务简单,因为定义范围内的所有 Repository 接口都要与 Spring 数据模块绑定。有时,应用要求使用多于一个的 Spring 数据模块。在这样的情况下,一个 Repository 定义必须能区分不同的持久性技术。当它在类路径上检测到多 Repository 工厂时,Spring 数据进入严格的 Repository 配置模式。严格的配置使用 Repository 或实体类上的详细信息确定 Repository定义的 Spring 数据模块的绑定:
1. 如果 Repository 定义继承特定模块的 Repository ,那么它是特定 Spring 数据模块有效的候选者。
2. 如果使用特定模块类型的注解对实体类进行注解,那么它是特定 Spring 数据模块有效的候选者。Spring 数据模块接受第三方注解(如 JPA 的 @Entity ),也提供了它们自己的注解(如使用于 Spring Data MongoDB 和 Spring Data Elastisearch 的 @Document )。

以下例子展示了一个使用特定模块接口 JPA 的 Repository:

public interface MyRepository extends JpaRepository<User, Long> { }
@NoRepositoryBean
public interface MyBaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
    …
}
public interface UserRepository extends MyBaseRepository<User, Long> {
    …
}
// 在类型层次结构中,MyRepository 和 UserRepository 继承了 JpaRepository,
// 它们都是 Spring Data JPA 模块的有效候选者。

以下例子展示了一个使用通用接口的 Repository:

public interface AmbiguousRepository extends Repository<User, Long> {
    …
}
@NoRepositoryBean
public interface MyBaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    …
}
public interface AmbiguousUserRepository extends MyBaseRepository<User, Long> {
    …
}
// 在类型层次结构中,AmbiguousRepository 和 AmbiguousUserRepository 只继承了 Repository 和 CrudRepository 。
// 虽然在使用单一的 Spring Data 模块时是完全正常的,
// 但是多模块无法区分这些 Repository 应该绑定哪个特定的 Spring Data 。

以下例子展示了一个使用带注解的实体类的 Repository:

interface PersonRepository extends Repository<Person, Long> {
    …
}
@Entity
class Person {
    …
}
interface UserRepository extends Repository<User, Long> {
    …
}
@Document
class User {
    …
}
// PersonRepository 引用使用 JPA @Entiry 注解的 Person ,
// 因此该 Repository 属于 Spring Data JPA 。
// UserRepository 引用使用 Spring Data MongoDB @Document 注解的 User。

以下的错误例子展示了一个使用带混杂注解的实体类的 Repository:

interface JpaPersonRepository extends Repository<Person, Long> {
    …
}
interface MongoDBPersonRepository extends Repository<Person, Long> {
    …
}
@Entity
@Document
class Person {
    …
}
// Spring Data 不能区分 Repository,导致未定义错误。

Repository 类型详细信息和区分实体类的注解用于为特定 Spring Data 模块区分候选者的严格 Repository 配置。在同一个实体类上使用多个特定的持久性技术注解是可能的,并且允许跨多持久性技术重用实体类型。然而,Spring Data 不能确定单一的模块来绑定 Repository 。
区分 Repository 的最后一个方法是使用 Repository 基础包。基础包定义在扫描 Repository 接口定义的开始点,这意味着 Repository 定义放在相应的包中。默认情况下,注解驱动的配置使用配置类的包。基础包在 XML 配置中是强制的。
以下例子展示了基本包的注解驱动配置:

@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
interface Configuration { }

4. 定义查询方法

Repository 代理有两种方法可以从方法名称派生特定于存储的查询:
- 直接从方法名称派生查询
- 使用手动定义的查询

可用选项取决于实际存储。然而,有一个策略确定产生的实际查询是什么。接下来的部分描述这些可用选项。

4.1 Query Lookup 策略

……………………….

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值