Перейти к основному содержимому

Соединение таблиц с Spring Data JPA Спецификации

· 3 мин. чтения

1. Обзор

В этом кратком руководстве мы обсудим более продвинутую функцию Spring Data JPA Specifications, которая позволяет нам объединять таблицы при создании запроса.

Давайте начнем с краткого обзора спецификаций JPA и их использования.

2. Спецификации JPA

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 whenFindByLastNameAndFirstNameLike_thenOneAuthorIsReturned() {

Specification<Author> specification = hasLastName("Martin")
.and(hasFirstNameLike("Robert"));

List<Author> authors = repository.findAll(specification);

assertThat(authors).hasSize(1);
}

3. Объединение таблиц со спецификациями JPA

Из нашей модели данных мы можем наблюдать, что сущность Author имеет отношение «один ко многим» с сущностью Book :

@Entity
public class Book {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String title;

// getters and setters
}

API Criteria Query позволяет нам соединить две таблицы при создании спецификации . В результате мы сможем включать поля из сущности 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 для запроса таблицы на основе одной из связанных с ней сущностей.

Спецификации Spring Data JPA обеспечивают плавный, динамичный и многоразовый способ создания запросов.

Как обычно, исходный код доступен на GitHub .