LoginSignup
29
30

More than 3 years have passed since last update.

Spring Bootキャンプ:Spring Boot + Spring Data JDBC編

Last updated at Posted at 2019-07-11

Spring Bootキャンプシリーズ、Spring Boot + Spring Data JDBC編です。

今回の目的

Spring BootアプリでSpring Data JDBCを利用してDBアクセスを行います。

今回使用するライブラリ

  • spring-boot-starter:2.2.0.M4
  • spring-boot-starter-data-jdbc:2.2.0.M4
  • spring-boot-starter-test:2.2.0.M4
  • h2:1.4.199
  • lombok:1.18.8

以降の手順のいくつかはSpring Initializrでプロジェクトを作成することにより解決されます。

DBアクセス

Spring Data JDBCの実装を行う前に、まずはDBアクセスするための設定を行います。

DBアクセスの定義

Spring BootではDBにアクセスするDataSource等のBean定義を自動的に行ってくれます。
デフォルトではインメモリのH2データベースにアクセスするため、依存関係にh2を追加する必要があります。

pom.xml

    <dependencies>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

Note.
Spring Bootの設定ファイルでドライバと接続先を変更すれば、別DBにアクセスできます。(依存関係に対応するドライバを追加する必要があります)

src/main/resources/application.yml

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/testdb
    username: postgres
    password: postgres

src/main/resources直下に以下のSQLファイルを作成すると、アプリ起動時にデータベースの初期化も自動的に行うこともできます。
デフォルトで認識されるSQLファイルは以下の通りです。

  • schema.sql
  • schema-${platform}.sql
  • data.sql
  • data-${platform}.sql

見たままですが、schema.sqlにはDDLを定義し、data.sqlにはDMLを定義します。

Note.
${platform}spring.datasource.platformプロパティで指定します。
単体テスト時はH2、結合テスト時はPostgresqlといった使い分けができそうですね。

今回はschema.sqlを作成してテーブルを作成します。

src/main/resources/schema.sql

create table if not exists todo (
    todo_id identity,
    todo_title varchar(30),
    finished boolean,
    created_at timestamp
);

spring-boot-starter-data-jdbc

Spring BootでSpring Data JDBCを利用するためのスターターです。
ぶっちゃけ詳細は開発者の@kazuki43zooさんの記事が詳しいので、そちらを見たほうが良いですw

Spring BootのAuto Configurationの仕組みを利用することで、Spring BootアプリでSpring Data JDBCを使用するためのBean定義を自動的に行ってくれます。開発者は依存関係にspring-boot-starter-data-jdbcを追加するだけでOKです。

pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
    </dependencies>

src/main/java/*/Application.java

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

メインクラスはSpring Bootアプリのデフォルトから何も変更していません。

そもそもSpring Data JDBCとは?

SpringのDBアクセスといえばSpring DataのJdbcTemplateかSpring Data JPAですが、Spring Data JDBCはその両方をハイブリッドしたようなものです。

Spring Data JPAのような文法でRepositoryを定義すると、JdbcTemplate(NamedParameterJdbcTemplate)を利用してSQLが実行されます。

Spring Data JDBCがサポートする主な機能は以下の通りです。

  • CRUDを実現するシンプルなAPIを提供する
  • @Queryでカスタムクエリを定義できる
  • SQL実行前後などのイベントをインターセプトする
  • Spring Data JDBCを経由してMyBatisのSQLを実行できる -> 別エントリで!
  • これらをAuto-Configする

Spring Data JPAと比べると、ぱっと見以下のような違いがあります。

  • カスタムクエリはJPQLではなくSQL(NamedParameterJdbcTemplateの文法)で定義する
  • Lazy Fetch(遅延読み込み)のような機能がない

Domainクラス

DBのデータをマッピングするドメインクラスには、プライマリキーを識別する@IDを付与します。
以下では、GetterやSetterの実装を省略するため、Lombokの@Dataを利用しています。

src/main/java/*/domain/Todo.java

@Data
public class Todo {
    @Id
    private String todoId;
    private String todoTitle;
    private boolean finished;
    private LocalDateTime createdAt;
}

Note.
テーブル名とDomainクラス名が異なるときは、クラスに@Table("テーブル名")を付与すればOKです。

Repositoryインターフェイス

SpringのRepositoryインターフェイスは、Spring DataのCrudRepositoryインターフェイスを継承します。

src/main/java/*/repository/TodoRepository.java

// (1)
public interface TodoRepository extends CrudRepository<Todo, String> {

   // (2)

   // (3)
   @Query("SELECT COUNT(*) FROM todo WHERE finished = :finished")
   long countByFinished(@Param("finished") boolean finished);
}

(1) RepositoryインターフェイスはCrudRepositoryを継承します。ジェネリクスは<Domainクラスの型, IDの型>です。

(2) CrudRepositoryを継承すると、自動的にCRUDをサポートするデフォルトメソッド(クエリ)が利用可能になります。提供されるメソッドはCrudRepositoryのJavaDocを確認すると分かります。

(3) カスタムクエリを実装したければ、@Queryをメソッドに付与してSQLを記述します。@Paramを付与した引数が、SQL内の:パラメータ名に埋め込まれます。分かりやすいですね。

Note.
更新系のカスタムクエリには、@Query + @Modifyingを付与する必要があります。

複数行のSQL

上ではSQLを1行で記載していますが、可読性が悪いので複数行で記載することをお勧めします。
こんな感じで文字列結合する形になります。

   @Query("SELECT"
           + " COUNT(*)"
           + " FROM todo"
           + " WHERE"
           + " finished = :finished")
   long countByFinished(@Param("finished") boolean finished);

CRUDのデフォルトメソッドを使わないとき

テーブル構成が複雑でテーブル単位のCRUDを使えないときは、CrudRepositoryの代わりにRepositoryインターフェイスを継承すればOKです。

カスタムクエリを実装する要領で、@Queryメソッドを実装していきます。

public interface TodoRepository extends Repository<Todo, String> {

   @Query("SELECT ...")
   long findAll();
}

動的SQL

Spring Data JDBCでは、動的SQLの組み立てはサポートされていないと思われます。
Spring Data JPAのCrudRepositoryインターフェイスにはExample引数を受け取るメソッドが定義されていますが、Spring DataのCrudRepositoryインターフェイスには定義されておらず、非常に簡素です。

spring-boot-starter-test

Spring Bootをテストするためのスターターです。

Spring BootのAuto Configurationの仕組みを利用することで、Spring BootアプリをテストするためのBean定義を自動的に行ってくれます。開発者は依存関係にspring-boot-starter-testを追加して、少しの設定をするだけでOKです。

pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

JUnitテストケース

JUnitテストケースでは、クラスに@DataJdbcTestを付与してテスト対象のリポジトリを@AutowiredするだけでOKです。

src/test/java/*/repository/TodoRepositoryTest.java

@DataJdbcTest
@Transactional
class TodoRepositoryTest {

    @Autowired
    TodoRepository todoRepository;

    @Test
    @Sql(statements = "INSERT INTO todo (todo_title, finished, created_at) VALUES ('sample todo', false, '2019-01-01')")
    void testFindAll() {
        // execute
        Iterable<Todo> todos = todoRepository.findAll();

        // assert
        assertThat(todos)
                .hasSize(1)
                .extracting(Todo::getTodoTitle, Todo::isFinished, Todo::getCreatedAt)
                .containsExactly(tuple("sample todo", false, LocalDate.of(2019, 1, 1).atStartOfDay()));
    }

@DataJdbcTestはSpring Data JDBCを利用するためのBean定義、DBにアクセスするDataSource等のBean定義を自動的に行ってくれます。

@DataJdbcTest@Transactionalを付与してくれないので、テスト終了後にロールバックするためには自分で@Transactionalを付与する必要があります。@DataJpaTestは付与してくれるのに、なぜだ、、、

Note.
Spring Boot 2.2.2から@DataJdbcTest@Transactionalが付与されるようになりました。

まとめ

Spring Data JDBCのスターターを利用することで、設定周りを省略してRepositoryの実装に注力することができました。

Spring Data JDBCからMyBatisの連携については、別エントリで。

29
30
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
30