PR

Spring Data JPAの使い方(サンプルコード付き)

この記事は約17分で読めます。
springDataJPアイキャッチ

Spring Data JDBCは、Spring Bootの依存関係です。
プロジェクトを立ち上げる際、Eclipseも用いると、
設定を進めていくうちに、

この画面で、SQLのリストの中にあり、チェックを入れると
使用することができます。


今回は、このSpring Data JPAの使用方法を
サンプルコードを用いてご紹介します。


なお、使用例についてはJUnitを用い
使用したコードは、以下のGitHubへ載せています。

Spring Data JPAの使用方法は、
Spring Data JDBCの使い方と非常に似ています。

しかし、違いもあります。
個人的にはJPAの方が使いやすいと思ってます。
気になる方は、この記事をぜひ読み進めてみてください。

広告

環境・定義

広告

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.0'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
}

tasks.named('test') {
	useJUnitPlatform()
}

今回、DBはh2データベースを使用し、
GetterやSetterの生成には、lombokを用います。

application.properties

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:hogedb
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.hibernate.ddl-auto=validate
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:schema.sql
spring.sql.init.data-locations=classpath:data.sql

spring.jpa.hibernate.ddl-autoの設定値については、以下に示します。

設定値説明備考
noneDDLスクリプトを生成しない。スキーマの更新は手動。
validateデータベースの更新、作成はしないが、検証をする。テーブルの型とEntityの型が違うなどのことがあるとコンパイルエラーとなる。
updateEntityに対応するテーブルがなければ作成。
createEntityに対応するテーブルが無けれ作成。あればデータを削除する。
create-dropcreateの挙動にプラスし、アプリケーション終了時にテーブルを削除する。

spring.sql.init.modeの設定値については、以下に示します。

設定値説明
alwayssrc/main/resources配下のschema.sql, data.sqlを常に実行
embeddedH2、HSQLなどの組み込みデータベースの際に、src/main/resources配下のschema.sql, data.sqlを実行
neverSQLスクリプトを実行しない

schema.sql

CREATE TABLE test_table
(
  id INT NOT NULL AUTO_INCREMENT,
  first_name VARCHAR(10) NOT NULL,
  last_name VARCHAR(10) NOT NULL,
  birth_day INT NULL,
  PRIMARY KEY(id)
);

data.sql

INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(1,'テスト', '太郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(2,'テスト', '二郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(3,'テスト', '三郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(4,'テスト', '花子', 20250101);

ファイル階層

  • /
    • src/
      • main/
        • java/
          • com/example/demo/
            • Entity/
              • JPAEntity.java
            • Repository/
              • JPARepository.java
        • resources/
          • application.properties
          • shema.sql
          • data.sql
          • ……
      • test/
        • java/com/example/demo/Repository/
          • JPARepositoryTest.java
    • build.gradle
    • ……

サンプルコード

広告

JPAEntity.java

package com.example.demo.Entity;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * JPAのテストエンティティ
 * @author Takumi
 *
 */
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "test_table")
public class JPAEntity {
	
	@Id
	private Integer id;
	private String first_name;
	private String last_name;
	private Integer birth_day;
}

JPARepository.java

package com.example.demo.Repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.demo.Entity.JPAEntity;

public interface JPARepository extends JpaRepository<JPAEntity, Integer>{
}

JPARepositoryTest.java

package com.example.demo.Repository;

import static org.junit.jupiter.api.Assertions.*;

import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.Entity.JPAEntity;

/**
 * {@link JPARepository}のテスト
 * @author Takumi
 *
 */
@SpringBootTest
@Transactional
class JPARepositoryTest {

	@Autowired
	private JPARepository repository;

	@Test
	void findAllTest() {
		List<JPAEntity> data = repository.findAll();
		
		assertEquals(data.size(), 4);
		assertEquals(data.get(0).getId(), 1);
		assertEquals(data.get(0).getFirst_name().strip(), "テスト");
		assertEquals(data.get(0).getLast_name().strip(), "太郎");
		assertEquals(data.get(0).getBirth_day(), 20240101);
	}

	@Test
	void findByIdTest() {
		Optional<JPAEntity> dataOpt = repository.findById(1);
		JPAEntity data = dataOpt.orElse(null);
		
		assertEquals(data.getId(), 1);
		assertEquals(data.getFirst_name().strip(), "テスト");
		assertEquals(data.getLast_name().strip(), "太郎");
		assertEquals(data.getBirth_day(), 20240101);
	}
	
	@Test
	void saveTest() {
		assertFalse(repository.existsById(9));
		
		JPAEntity insertEntity = new JPAEntity(9,"テストくん", "テストちゃん", 20200202);
		repository.save(insertEntity);
		
		assertTrue(repository.existsById(9));
		
		Optional<JPAEntity> insertDataOpt = repository.findById(9);
		JPAEntity insertData = insertDataOpt.orElse(null);
		
		assertEquals(insertData.getFirst_name().strip(), "テストくん");
		assertEquals(insertData.getLast_name().strip(), "テストちゃん");
		assertEquals(insertData.getBirth_day(), 20200202);
	}
	
	@Test
	void deleteTest() {
		assertTrue(repository.existsById(1));
		
		repository.deleteById(1);
		
		assertFalse(repository.existsById(1));
	}
}

サンプルコード詳細説明

Spring Data JPAに関する部分のみご説明します。

JPAEntity.java

このクラスは、テーブルとのマッピングをします。

schema,sqlで定義しているテーブルとマッピングしています。

マッピングするにはまず、
@Entity、@Table、そして@Idが必要になります。

もちろん@Tableはテーブル名であり、
@Idはプライマリキーを指定します。

今回は、テーブルのカラム名と
Entityのフィールド名を一致させているため必要ありませんが、
もしフィールド名をカラム名と一致させていない場合は、
@Columnを用いてマッピングさせる必要があります。

余談ですが、
Spring Data JDBCとの違いは、
テーブル名に日本語でも英語の小文字でも用いることができる
ということです。
ただ、テーブル名にキャメルケース1を用いた場合は、
スネークケース2として判断されるようですので、注意が必要そうです。

JPARepository.java

このinterfaceは、jpaRepositoryを継承するのが肝です。

ここがSpring Data JPAの肝です。

第一引数にマッピングしたテーブルのEntityクラスを、
第二引数にEntityクラスの@Idをつけているフィールドの型、
つまり、テーブルのプライマリキーの型を渡して継承
します。

このクラスを継承することで、
findAll、findById、delete、saveなどのメソッドを提供してくれ、
単純なSQL文であれば書く必要がなくなります。

JPARepositoryTest.java

テストケースごとに、Spring Data JPAに関連するところのみ
説明します。

findAllTest

findAllはテーブルにあるデータを全て取得するメソッドです。

	@Test
	void findAllTest() {
		List<JPAEntity> data = repository.findAll();
		
		assertEquals(data.size(), 4);
		assertEquals(data.get(0).getId(), 1);
		assertEquals(data.get(0).getFirst_name().strip(), "テスト");
		assertEquals(data.get(0).getLast_name().strip(), "太郎");
		assertEquals(data.get(0).getBirth_day(), 20240101);
	}

3行目

List<JPAEntity> data = repository.findAll();

で全てのデータを取得しています。

その後、
5〜9行目

assertEquals(data.size(), 4);
assertEquals(data.get(0).getId(), 1);
assertEquals(data.get(0).getFirst_name().strip(), “テスト”);
assertEquals(data.get(0).getLast_name().strip(), “太郎”);
assertEquals(data.get(0).getBirth_day(), 20240101);

でデータが正しく取得できていることを確認しています。
(本来ならば、テーブルにある4データすべての確認をするべきですが、
ここでは省略します。)

findByIdTest

findByIdは引数に取得したいデータのプライマリキーの値を渡すことで、
データを取得するメソッドです。

	@Test
	void findByIdTest() {
		Optional<JPAEntity> dataOpt = repository.findById(1);
		JPAEntity data = dataOpt.orElse(null);
		
		assertEquals(data.getId(), 1);
		assertEquals(data.getFirst_name().strip(), "テスト");
		assertEquals(data.getLast_name().strip(), "太郎");
		assertEquals(data.getBirth_day(), 20240101);
	}

3〜4行目

Optional<JPAEntity> dataOpt = repository.findById(1);
JPAEntity data = dataOpt.orElse(null);

で、データを取得しています。

その後、
6〜9行目

assertEquals(data.getId(), 1);
assertEquals(data.getFirst_name().strip(), “テスト”);
assertEquals(data.getLast_name().strip(), “太郎”);
assertEquals(data.getBirth_day(), 20240101);

でデータがきちんと取得できているかを
確認しています。

saveTest

saveメソッドは、引数に渡したマッピングしたEntityクラスのデータを
テーブルへインサート、もしくはテーブルをアップデートするメソッドです。

今回は、インサートの例です。

	@Test
	void saveTest() {
		assertFalse(repository.existsById(9));
		
		JPAEntity insertEntity = new JPAEntity(9,"テストくん", "テストちゃん", 20200202);
		repository.save(insertEntity);

		assertTrue(repository.existsById(9));
		
		Optional<JPAEntity> insertDataOpt = repository.findById(9);
		JPAEntity insertData = insertDataOpt.orElse(null);
		
		assertEquals(insertData.getFirst_name().strip(), "テストくん");
		assertEquals(insertData.getLast_name().strip(), "テストちゃん");
		assertEquals(insertData.getBirth_day(), 20200202);
	}

3行目

assertFalse(repository.existsById(9));

で、Idが9のデータが存在しないことを確認しています。
その後、
5〜6行目

JPAEntity insertEntity = new JPAEntity(9,”テストくん”, “テストちゃん”, 20200202);
repository.save(insertEntity);

で、Idが9のデータをインサートし、
8行目

assertTrue(repository.existsById(9));

で、インサートされたのかを確認しています。

最後に、
10〜15行目

Optional<JPAEntity> insertDataOpt = repository.findById(9);
JPAEntity insertData = insertDataOpt.orElse(null);

assertEquals(insertData.getFirst_name().strip(), “テストくん”);
assertEquals(insertData.getLast_name().strip(), “テストちゃん”);
assertEquals(insertData.getBirth_day(), 20200202);

で、データが正しくインサートされているのかを確認しています。

deleteTest

deleteByIdは、引数に消したいデータのプライマリキーの値を渡すことで、
そのデータを削除するメソッドです。

	@Test
	void deleteTest() {
		assertTrue(repository.existsById(1));
		
		repository.deleteById(1);
		
		assertFalse(repository.existsById(1));
	}

3行目

assertTrue(repository.existsById(1));

でIdが1のデータが損じすることを確認しています。
その後、
5行目

repository.deleteById(1);

で、Idが1のデータを削除し、
7行目

assertFalse(repository.existsById(1));

で、Idが1のデータが存在しなくなったことを確認しています。


  1. 単語間の区切り文字を大文字にしているもの(例) testTable ↩︎
  2. 単語間の区切り文字をアンダーバーにしているもの(例)test_table ↩︎
広告
タイトルとURLをコピーしました