1. 概述
在这个简短教程中,我们将讨论Spring Data JPA的一个高级特性:在构建查询时如何连接表。
首先,让我们简要回顾一下JPA Specifications及其用法。
2. JPA Specifications
Spring Data JPA引入了Specification
接口,使我们能够使用可重用组件创建动态查询。
本文示例代码将使用Author
和Book
类:
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
@OneToMany(cascade = CascadeType.ALL)
private List<Book> books;
// getters and setters
}
为了为Author
实体创建动态查询,我们可以使用Specification
接口的实现:
public class AuthorSpecifications {
public static Specification<Author> hasFirstNameLike(String name) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.like(root.<String>get("firstName"), "%" + name + "%");
}
public static Specification<Author> hasLastName(String name) {
return (root, query, cb) ->
cb.equal(root.<String>get("lastName"), name);
}
}
最后,我们需要AuthorRepository
继承JpaSpecificationExecutor
:
@Repository
public interface AuthorsRepository extends JpaRepository<Author, Long>, JpaSpecificationExecutor<Author> {
}
这样,我们就可以将两个规格串联起来,并使用它们创建查询:
@Test
public void whenSearchingByLastNameAndFirstNameLike_thenOneAuthorIsReturned() {
Specification<Author> specification = hasLastName("Martin")
.and(hasFirstNameLike("Robert"));
List<Author> authors = repository.findAll(specification);
assertThat(authors).hasSize(1);
}
3. 使用JPA Specifications连接表
根据我们的数据模型,Author
实体与Book
实体之间存在一对多关系:
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
// getters and setters
}
Criteria Query API(Spring Data Criteria Queries)允许我们在创建Specification
时连接这两个表,从而可以在查询中包含Book
实体的字段:
public static Specification<Author> hasBookWithTitle(String bookTitle) {
return (root, query, criteriaBuilder) -> {
Join<Book, Author> authorsBook = root.join("books");
return criteriaBuilder.equal(authorsBook.get("title"), bookTitle);
};
}
现在,让我们将这个新的规格与之前创建的规格结合起来:
@Test
public void whenSearchingByBookTitleAndAuthorName_thenOneAuthorIsReturned() {
Specification<Author> specification = hasLastName("Martin")
.and(hasBookWithTitle("Clean Code"));
List<Author> authors = repository.findAll(specification);
assertThat(authors).hasSize(1);
}
最后,让我们查看生成的SQL并看到JOIN
子句:
select
author0_.id as id1_1_,
author0_.first_name as first_na2_1_,
author0_.last_name as last_nam3_1_
from
author author0_
inner join author_books books1_ on author0_.id = books1_.author_id
inner join book book2_ on books1_.books_id = book2_.id
where
author0_.last_name = ?
and book2_.title = ?
4. 总结
在这篇文章中,我们学习了如何使用JPA Specifications根据关联实体查询表。
Spring Data JPA的Specifications提供了一种流畅、动态且可重用的方式来创建查询。
如往常一样,源代码可在GitHub上获取。