spring.svg

【Spring Data JPA】基本操作とクエリ実装

SpringBoot

導入

Spring Data JPA を使用するために、以下を追加します。 ドライバーは使用する DB に合わせて追加してください。

Gradle
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}

application.yml(.properties)に、データベースの接続設定をします。 以下は MySQL の設定例です。

application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb
    username: hoge
    password: fugafuga
    driver-class-name: com.mysql.cj.jdbc.Driver

基本操作

Entity の作成

Spring Data JPA では、テーブルに対応する Entity クラスを作成する必要があります。 Entity クラスには、以下のように@Entityを付与します。

User.java
@Entity
@Table(name = 'user')
public class User {
  @Id
  @Column(name = "id")
  private String id;

  @Column(name = "name")
  private String name;

  @Column(name = "age")
  private int age;

  //セッター・ゲッターは省略
}

@Tableは、テーブル名とクラス名が同じであれば省略できます。 @Columnも同様に、テーブルのフィールド名とクラスのフィールド名が同じであれば省略できます。

キー項目には@Idを付与します。

Repository の作成

DB 操作用の Repository インターフェースを以下のように作成します。

UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, String>{
}

@Repositoryを付与し、JpaRepository<>を継承します。 JpaRepository<>のジェネリクスには、対象となる Entity クラスと、キー項目の型を指定します。

結果の取得

作成した Repository を Inject して、CRUD に対応するメソッドを使用します。

※ 下例では、わかりやすさのために Controller で使用していますが、実際は Service で使用してください。

UserController.java
@RestController
@RequestMapping("/api/user")
public class UserController {

  @Autowired
  private UserRepository repository;

  @GetMapping
  public List<User> getAll() {
    //全件取得
    return repository.findAll();
  }

  @GetMapping("{id}")
  public User getById(@PathVariable("id") String id) {
    //IDで検索
    return repository.findById(id).orElseThrow();
  }

  @PostMapping
  public void save(@RequestMapping User user) {
    //更新
    repository.save(user);
  }

  @DeleteMapping("{id}")
  public User delete(@PathVariable("id") String id) {
    //削除
    repository.deleteById(id);
  }
}

以上が Spring Data JPA の基本的な使用方法になります。

クエリ実装

基本クエリ

Repository では、上記で使用したような CRUD に関するメソッドがいくつかデフォルトで生成されます。 以下はそのメソッドの一部になります。

メソッド引数戻り値説明
findAll()List<T>すべてのレコードを取得
findById()キーOptional<T>キーに該当するレコードを取得
getById()キーTキーに該当するレコードを取得
count()longレコード数を取得
existsById()キーbooleanキーに該当するレコードが存在するかを取得
save()TTキーが存在する場合は更新、存在しない場合は登録
saveAll()List<T>List<T>Entity の数だけsave()を実行
deleteById()キーキーに該当するレコードを削除
deleteAllById()List<キー>キーの数だけdeleteById()を実行

カスタムクエリ

例えばnameを条件として検索し、レコードを取得したいとします。 これを実現するためには、Repository に以下のメソッドを追加します。

UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, String>{
  List<User> findByName(String name);
}

あとは必要な箇所でこのメソッドを実行するだけです。

List<User> users = repository.findByName("太郎");

Spring Data JPA では、Repository にルールに従ってメソッドを定義することで、 自身でクエリを生成をせずに、目的の結果を得ることができます。 上記の場合は、以下のクエリが実行されたことになります。

SQL
select * from user
where name = ?

ルールというのは、メソッドの命名です。 決まったキーワードを組み合わせることで目的を満たすクエリが自動で生成されます。

まず初めに目的を設定します。 上記はレコードの取得のためにfindByというキーワードを使用しました。 その他以下のキーワードがあります。

キーワードメソッドサンプル説明
findByfindByName(String name)条件に一致するレコードの取得
existsByexistsByName(String name)条件に一致するレコードが存在するか(true/false
countBycountByName(String name)条件に一致するレコード数
deleteBydeleteByName(String name)条件に一致するレコードの削除

次に条件を設定します。 上記では、findByNameとすることで「nameと一致する」という条件を設定しています。 等価条件以外は、以下のキーワードを用いて設定できます。 複数フィールドを条件とする場合は、メソッドで指定したフィールド順序とパラメーターの順序を同じにします。

キーワードメソッドサンプルクエリ
NotfindByNameNot(String name)... where name <> ?1
AndfindByNameAndAge(String name, int age)... where name = ?1 and age = ?2
OrfindByNameOrAge(String name, int age)... where name = ?1 or age = ?2
LessThanfindByAgeLessThan(int age)... where age < ?1
LessThanEqualfindByAgeLessThanEqual(int age)... where age <= ?1
GreaterThanfindByAgeGreaterThan(int age)... where age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual(int age)... where age <= ?1
BetweenfindByAgeBetween(int min, int max)... where age between ?1 and ?2
IsNullfindByNameIsNull()... where name is null
IsNotNullfindByNameIsNotNull()... where name is not null
InfindByNameIn(String... name)... where name in ?1
NotInfindByNameNotIn(String... name)... where name not in ?1
LikefindByNameLike(String name)... where name like ?1
NotLikefindByNameNotLike(String name)... wehre name not like ?1
StartingWithfindByNameStartingWith(String name)... where name like ?1%
EndingWithfindByNameEndingWith(String name)... where name like %?1
ContainingfindByNameContaining(String name)... where name like %?1%
TruefindByActiveTrue()... where active = true
FalsefindByActiveFalse()... where active = false
IgnoreCasefindByIdIgnoreCase(String id)... where UPPER(id) = UPPER(?1)

最後に取得する結果に対して手を加えます。 以下のキーワードを使用することで、並び替えや取得レコード数を指定することができます。

キーワードメソッドサンプル説明
OrderByfindByNameOrderByAgeDesc(String name)レコード順序の指定
Top, FirstfindTop3ByName(String name)指定した数だけ上位のレコードを取得(TopFirstは同義)
DistinctfindDistinctByName(String name)同一結果のレコードの集約

JPQL

JPQL とは

JPQL は Java Persistence Query Language の略で、JPA で使用できるクエリです。 後述しますが、JPQL を使って独自のクエリを作成することができます。

基本的な文法はネイティブなクエリと同じですが、テーブルではなく Entity をベースに作成する点が異なります。 例えば、nameを条件にレコードを取得するクエリは以下のようになります。

select u from User u where u.name = ?1

FROM 句には、Entity 名と識別変数(AS 句)が必要になります。 SELECT 句や WHERE 句などでは、識別変数を使用してフィールドを指定します。

パラメーターは、「?1, ?2, ?3, ...」と順に設定します。d または:nameのように、名前付きパラメーターも使用できます。

また JPQL では以下の句、演算子、関数が使用できます。

  • SELECT, UPDATE, DELETE
  • WHERE, CASE
  • ORDER BY
  • GROUP BY, HAVING
  • JOIN(INNER, OUTER)
  • 副問い合わせ(WHERE, HAVING 内でのみ)

演算子

分類演算子
四則演算子+, -, *, /
論理演算子AND, OR, NOT
関係演算子=, <>, <, >, <=, >=
特殊演算子BETWEEN, IN, LIKE, IS NULL, IN EMPTY

関数

分類関数説明
集約関数
COUNT
レコード数
MAX
最大値
MIN
最小値
AVG
平均値
SUM
合計値
文字列関数
CONCAT
文字列連結
SUBSTRING
文字列抽出
TRIM
空白削除
LOWER
小文字変換
UPPER
大文字変換
LENGTH
文字列の長さ
LOCATE
文字列検索位置
算術関数
ABS
絶対値
SQRT
平方根
MOD
剰余
SIZE
コレクションの要素数
INDEX
要素の位置
日時関数
CURRENT_DATE
現在日
CURRENT_TIME
現在時刻
CURRENT_TIMESTAMP
タイムスタンプ

@NamedQuery

JPQL を用いたクエリの作成方法の 1 つとして、@NamedQueryがあります。 これは、Entity に付与するアノテーションで、プロパティにnamequeryを指定します。

User.java
@Entity
@NamedQuery(name = "myFindByAge", query = "select u from User u where u.age = ?1")
public class User {
  //...
}

使用する場合は、Repository にnameで指定した名前のメソッドを定義します。

UserRepository.java
List<User> myFindByAge(int age);

メソッドパラメーターの順序と?1の順序を合わせる必要があります。

@Query

もう 1 つの実装方法として、@Queryがあります。 @NamedQueryは Entity に付与しましたが、@Queryは Repository のメソッドに付与します。

UserRepository.java
@Query("select u from User u where u.age = ?1")
List<User> myFindByAge(int age);

メソッドパラメーターに@Paramを付与することで、名前付きパラメーターを使用することができます。

UserRepository.java
@Query("select u from User u where u.age = :age")
List<User> myFindByAge(@Param("age") int age);

また以下のようにnativeQueryプロパティを設定することで、JPQL ではなくネイティブなクエリを指定することができます。

UserRepository.java
@Query(value = "select * from user where age = ?1", nativeQuery = true)
List<User> myFindByAge(int age);

@Modifying

@Modifyingは、@Queryで更新形(UPDATE、DELETE)のクエリを指定した場合に必要となります。 またこの場合、Repository に@Transactionalを付与する必要もあります。

UserRepository.java
@Transactional
@Repository
public interface UserRepository extends JpaRepository<User, String> {
  @Modifying
  @Query("update User u set age = :age where id = :id")
  void setAge(@Param("age") int age, @Param("id") String id);
}