1. 概要

この短いチュートリアルでは、クエリの作成時にテーブルを結合できる Spring DataJPA仕様のより高度な機能について説明します。

まず、JPA仕様の簡単な要約から始めましょう。

2. JPA仕様

Spring Data JPAは、再利用可能なコンポーネントを使用して動的クエリを作成できるようにするための仕様インターフェイスを導入しました。

この記事のコード例では、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
}

作成者エンティティの動的クエリを作成するために、仕様インターフェイスの実装を使用できます。

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);
    }
}

最後に、 JpaSpecificationExecutor を拡張するには、AuthorRepositoryが必要です。

@Repository
public interface AuthorsRepository extends JpaRepository<Author, Long>, JpaSpecificationExecutor<Author> {
}

その結果、2つの仕様をチェーンして、それらを使用してクエリを作成できるようになりました。

@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エンティティと1対多の関係を共有していることがわかります。

@Entity
public class Book {

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

    private String title;

    // getters and setters
}

The 基準クエリAPI 作成時に2つのテーブルを結合できます仕様 。 その結果、からのフィールドを含めることができるようになりますクエリ内のエンティティ:

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仕様を使用して、関連付けられたエンティティの1つに基づいてテーブルをクエリする方法を学習しました。

Spring Data JPAの仕様は、流暢で動的で再利用可能なクエリ作成方法につながります。

いつものように、ソースコードはGitHubから入手できます。