【SpringData&JPA从入门到精通】01-SpringData-Repository

笔记来源:尚硅谷SpringData教程(springdata经典,spring data快速上手)

Repository

1、Repository 接口

Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法(空接口/标记接口),开发者需要在自己定义的接口中声明需要的方法

public interface Repository<T,ID extends Serializable>{}

Spring Data 可以让我们只定义接口,只要遵循 Spring Data 的规范,就无需写实现类

若我们定义的接口继承了 Repository,则该接口会被 IOC 容器识别为一个 Repository Bean 纳入到 IOC 容器中,进而可以在该接口中定义满足一定规范的方法

public interface PersonRepository extends Repository<Person, Integer>
{
    Person getByLastName(String lastName);
}

与继承 Repository 等价的一种方式,就是在持久层接口上使用 @RepositoryDefinition 注解,并为其指定 domainClassidClass 属性

@RepositoryDefinition(domainClass = Person.class, idClass = Integer.class)
public interface PersonRepository
{
    Person getByLastName(String lastName);
}

2、Repository 子接口

基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。它们的继承关系如下:

  • Repository:仅仅是一个标识,表明任何继承它的均为 仓库接口类
  • CrudRepository:继承 Repository,实现了一组 CRUD 相关的方法
  • PagingAndSortingRepository:继承 CrudRepository,实现了一组分页排序相关的方法
  • JpaRepository:继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
  • 自定义的 XxxRepository:需要继承 JpaRepository,这样的 XxxRepository 接口就具备了通用的数据访问控制层的能力
  • JpaSpecificationExecutor:不属于 Repository 体系,实现一组 JPA Criteria 查询相关的方法
Repository (org.springframework.data.repository)
----CrudRepository (org.springframework.data.repository)
--------PagingAndSortingRepository (org.springframework.data.repository)
------------JpaRepository (org.springframework.data.jpa.repository)
----------------SimpleJpaRepository (org.springframework.data.jpa.repository.support)
--------------------QueryDslJpaRepository (org.springframework.data.jpa.repository.support)

3、查询方法定义规范

在 Repository 子接口中声明方法不是随便声明的,而需要符合一定的规范

3.1、简单条件查询

简单条件查询:查询某一个实体类或者集合

按照 Spring Data 的规范,查询方法以 find | read | get 开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性以首字母大写

例如:定义一个 Entity 实体类

class User{
    private String firstName;
    private String lastName;
}

使用 And 条件连接时,应这样写:

findByLastNameAndFirstName(String lastName, String firstName);

条件的属性名称与个数要与参数的位置与个数一对应

3.2、支持的关键字

直接在接口中定义查询方法,如果是符合规范的,可以不用写实现。目前支持的关键字写法如下:

KeywordSampleJPQL snippet
AndfindByLastnameAndFirstname... where x.lastname=?1 and x.firstname=?2
OrfindByLastnameOrFirstname... where x.lastname=?1 or x.firstname=?2
BetweenfindByStartDateBetween... where x.startDate between 1? and ?2
LessThanfindByAgeLessThan... where x.age<?1
GreaterThanfindByAgeGreaterThan... where x.age>?1
AfterfindByStartDateAfter... where x.startDate>?1
BeforefindByStartDateBefore... where x.startDate<?1
IsNullfindByAgelsNull... where x.age is null
IsNotNull, NotNullfindByAge(Is)NotNull... where x.age not null
LikefindByFirstnameLike... where x.firstname like ?1
NotlikefindByFirstnameNotlike... where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith... where x.firstname like ?1 (parameter bound with appended %)
EndingwithfindByFirstnameEndingWith... where x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContaining... where x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDesc... where x.age=?1 order by x.lastname desc
NotfindByLastnameNot... where x.lastname<>?1
InfindByAgeIn(Collection<Age> ages)... where x.age in ?1
NotinfindByAgeNotin(Colection<Age> ages)... where x.age not in ?1
TRUEfindByActiveTrue()... where x.active=true
FALSEfindByActiveFALSE()... where x.active=false

测试方法 1

PersonRepository 类

List<Person> findByLastNameStartingWithAndIdLessThan(String lastname, Integer id);

Test 类

@Test
public void testFindByLastNameEndingWithAndIdLessThan() {
    List<Person> personList = repository.findByLastNameStartingWithAndIdLessThan("X", 30);
    System.out.println(personList);
}

日志信息

Hibernate: 
    select
        person0_.id as id1_0_,
        person0_.birth_day as birth_da2_0_,
        person0_.email as email3_0_,
        person0_.last_name as last_nam4_0_ 
    from
        jpa_persons person0_ 
    where
        (
            person0_.last_name like ?
        ) 
        and person0_.id<?
[Person{id=24, lastName='XX', email='XX@qq.com', birthDay=2022-05-21 22:48:34.0}, Person{id=27, lastName='XY', email='XY@qq.com', birthDay=2022-05-21 22:48:34.0}]

测试方法 2

PersonRepository 类

List<Person> findByEmailInAndBirthDayLessThan(List<String> emailList, Date birthDay);

Test 类

@Test
public void testFindByEmailInAndBirthDayBetween() {
    List<String> emailList = Arrays.asList("AA@qq.com", "CC@qq.com");
    List<Person> personList = repository.findByEmailInAndBirthDayLessThan(emailList, new Date());
    System.out.println(personList);
}

日志信息

Hibernate: 
    select
        person0_.id as id1_0_,
        person0_.birth_day as birth_da2_0_,
        person0_.email as email3_0_,
        person0_.last_name as last_nam4_0_ 
    from
        jpa_persons person0_ 
    where
        (
            person0_.email in (
                ? , ?
            )
        ) 
        and person0_.birth_day<?
[Person{id=1, lastName='AA', email='AA@qq.com', birthDay=2022-05-21 22:48:34.0}, Person{id=3, lastName='CC', email='CC@qq.com', birthDay=2022-05-21 22:48:34.0}]

4、查询方法解析流程

假如创建如下的查询:findByUserDepUuid(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为 Doc

  • 步骤 1:先判断 userDepUuid(根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性
    • 如果是,则表示根据该属性进行查询
    • 如果没有该属性,继续第二步;
  • 步骤 2:从右往左截取第一个大写字母开头的字符串(此处为 Uuid),然后检查剩下的字符串是否为查询实体的一个属性
    • 如果是,则表示根据该属性进行查询
    • 如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为查询实体的一个属性
  • 步骤 3:接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有 depUuid 属性
    • 如果有,则表示该方法最终是根据 Doc.user.depUuid 的取值进行查询
    • 否则继续按照步骤 2 的规则从右往左截取,最终表示根据 Doc.user.dep.uuid 的值进行查询。
  • 步骤 4:可能会存在一种特殊情况,比如 Doc 包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。可以明确在属性之间加上 _ 以显式表达意图,比如 findByUser_DepUuid() 或者 findByUserDep_uuid()

特殊的参数:还可以直接在方法的参数上加入分页或排序的参数,比如:

Page<UserModel> findByName(String name, Pageable pageable);
List<UserModel> findByName(String name, Sort sor);

测试方法 1

Person 类

@Table(name = "JPA_PERSONS")
@Entity
public class Person
{
    // ...
    private Address address;

    @JoinColumn(name = "ADD_ID")
    @ManyToOne
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

PersonRepository 类

 List<Person> findByAddressId(Integer addressId);

Test 类

@Test
public void testFindByAddressId() {
    List<Person> personList = repository.findByAddressId(123);
    System.out.println(personList);
}

日志信息

Hibernate: 
    select
        person0_.id as id1_1_,
        person0_.address_id as address_5_1_,
        person0_.birth_day as birth_da2_1_,
        person0_.email as email3_1_,
        person0_.last_name as last_nam4_1_ 
    from
        jpa_persons person0_ 
    left outer join
        jpa_addesss address1_ 
            on person0_.address_id=address1_.id 
    where
        address1_.id=?
[]

修改 Person 类

@Table(name = "JPA_PERSONS")
@Entity
public class Person
{
    // ...
    private Address address;
    private Integer addressId;
    
    @JoinColumn(name = "ADD_ID")
    @ManyToOne
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
    
    public Integer getAddressId() {
        return addressId;
    }

    public void setAddressId(Integer addressId) {
        this.addressId = addressId;
    }
}

再次测试,查看日志信息

Hibernate: 
    select
        person0_.id as id1_1_,
        person0_.add_id as add_id6_1_,
        person0_.address_id as address_2_1_,
        person0_.birth_day as birth_da3_1_,
        person0_.email as email4_1_,
        person0_.last_name as last_nam5_1_ 
    from
        jpa_persons person0_ 
    where
        person0_.address_id=?
[]

查询方法支持属性的级联查询。若当前类有符合条件的属性,则优先使用类中的属性,而不使用级联属性

若需要使用级联属性,则属性之间使用进行 _(下划线)连接

测试方法 2

PersonRepository 类

List<Person> findByAddress_Id(Integer addressId);

Test 类

@Test
public void testFindByAddress_Id() {
    List<Person> personList = repository.findByAddress_Id(123);
    System.out.println(personList);
}

日志信息

Hibernate: 
    select
        person0_.id as id1_1_,
        person0_.add_id as add_id6_1_,
        person0_.address_id as address_2_1_,
        person0_.birth_day as birth_da3_1_,
        person0_.email as email4_1_,
        person0_.last_name as last_nam5_1_ 
    from
        jpa_persons person0_ 
    left outer join
        jpa_addesss address1_ 
            on person0_.add_id=address1_.id 
    where
        address1_.id=?
[]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值