kikki's tech note

技術ブログです。UnityやSpine、MS、Javaなど技術色々について解説しています。

SpringでJPAを使ってDB操作を行う -2-

本章では、JOINを利用したクエリの作り方や並び替え、LIMIT・TOP句の使い方までを共有します。

JOIN句の使い方

まず JOINでの定義方法について、紹介します。Entityの関係は以下の通りです。
HogeParent
└ HogeChild

親子で「HogeParent : HogeChild = 1 : 多」の関係を想定して話を進めます。説明については、コードのコメントアウト中に記述しました。
JOIN先のコンテンツをEntityに持たせる場合、Springではクラス内に対象のメンバ変数を用意するだけです。ただ、アノテーションにひと工夫が必要です。以下サンプルです。

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.Version;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@Data
@Entity
// JPAでは、DBで実際に使用されているテーブル名とクラス名を別に定義できます
// ↓は、実際に使用されているテーブル名
@Table(name = "hoge_parent")
@EntityListeners(AuditingEntityListener.class)
public class HogeParent {

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

    @Column(name = "name")
    @Basic(Optional = false)
    private String name;

    @Column(name = "gender")
    @Basic(Optional = false)
    private Gender gender;

    @Column(name = "created_date_time")
    @Basic(optional = false)
    @CreatedDate
    private ZonedDateTime createdDateTime;

    @Column(name = "created_by")
    @Basic(optional = false)
    @CreatedBy
    private String createdBy;

    @Version
    @Column(name = "last_modified_date_time")
    @Basic(optional = false)
    @LastModifiedDate
    private ZonedDateTime lastModifiedDateTime;

    @Column(name = "last_modified_by")
    @Basic(optional = false)
    @LastModifiedBy
    private String lastModifiedBy;

    @Column(name = "deleted")
    @Basic(optional = false)
    private boolean deleted;

}

@Data
@Entity
// JPAでは、DBで実際に使用されているテーブル名とクラス名を別に定義できます
// ↓は、実際に使用されているテーブル名
@Table(name = "hoge_parent")
@EntityListeners(AuditingEntityListener.class)
public class HogeParent {

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

    @Column(name = "name")
    @Basic(Optional = false)
    private String name;

    @Column(name = "gender")
    @Basic(Optional = false)
    private Gender gender;

    @Column(name = "parent_id")
    @Basic(optional = false)
    private String parentId;

    // joinColumn では、nameに自身のDB上のカラム物理名、referencedColumnName に参照先のカラム物理名を指定します
    // また同じEntity内に、自身のカラムがある場合、「insertable = false, updatable = false」が必要です
    // (※サンプルではparentId)
    @JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
    @ManyToOne(fetch = FetchType.LAZY)
    private HogeParent  hogeParent;

    @Column(name = "created_date_time")
    @Basic(optional = false)
    @CreatedDate
    private ZonedDateTime createdDateTime;

    @Column(name = "created_by")
    @Basic(optional = false)
    @CreatedBy
    private String createdBy;

    @Version
    @Column(name = "last_modified_date_time")
    @Basic(optional = false)
    @LastModifiedDate
    private ZonedDateTime lastModifiedDateTime;

    @Column(name = "last_modified_by")
    @Basic(optional = false)
    @LastModifiedBy
    private String lastModifiedBy;

    @Column(name = "deleted")
    @Basic(optional = false)
    private boolean deleted;
}

LIMIT句の使い方

JPAでは、LIMIT句として、「Pageable」を使用します。Pageableは、並び順の指定、取得件数の情報が含まれます。主に、ページングを実現する際に利用されます。
まずはRepositoryクラスです。JPQLとPageableを併用して、LIMIT句を実現させています。

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface HogeChildRepository
        extends JpaRepository<HogeChild, Long>,
        JpaSpecificationExecutor<HogeChild> {
    @Query("SELECT e FROM HogeChild e "
            + "WHERE e.hogeParent.name = :name")
    Page<HogeChild> findChildren(
            @Param("name") String name, Pageable pageable);
}

サービス層は以下のとおりです。Sortクラスで並び順を指定しつつ、PageRequestクラスで取得件数も指定しています。

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

@Service
@Transactional
public class ChildServiceImpl implements ChildService {
    public List<HogeChild> findLatestChild(String name, int limit) {
        List<HogeChild> children = childRepository
                .findChildren(
                        name,
                        new PageRequest(0, limit, new Sort(
                                new Sort.Order(Sort.Direction.DESC,
                                        "createdDateTime"),
                                new Sort.Order(Sort.Direction.DESC, "id"))))
                .getContent(); // getContent で、Pageの型からListの型に変更します
        return children;
    }
}

筆休め

JPAで並び替え後に件数を指定して、データを取得する場合、ORDER句を直接JPQLとして記述するより、Pageableを利用する方が良いです。調査して文献が見当たらなかったので、備忘として記録しました。
以上、「SpringでJPAを使ってDB操作を行う -2-」でした。


※無断転載禁止 Copyright (C) kikkisnrdec All Rights Reserved.