2018年10月14日日曜日

Java 11 + Spring Boot 2 + Spring Data JPA で PostgreSQL にアクセスする

こんな感じのことをします。(察して...)

DB アクセスのエッセンスに注目したかったので、 CLI アプリとして作ります。

環境

  • OS: Windows 10 Pro
  • Java: openjdk version "11" 2018-09-25
  • Docker: Docker version 18.06.1-ce, build e68fc7a

依存定義

次の jar を依存関係として定義した。

Java9(11?) からの変更の影響により、 jaxb-apijavassist を明示的に追加する必要が出てきたらしい。

dependencies {
    compile("org.springframework.boot:spring-boot-starter:2.0.5.RELEASE")
    compile('org.springframework.boot:spring-boot-starter-data-jpa:2.0.5.RELEASE')
    runtime('org.postgresql:postgresql')

    // https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api
    compile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'

    // https://mvnrepository.com/artifact/org.javassist/javassist
    compile group: 'org.javassist', name: 'javassist', version: '3.23.1-GA'
}

PostgreSQL 環境の構築

構成

  • サーバー: PostgreSQL
  • DB: postgres

こんな感じで構築する。

               List of relations
 Schema |      Name      |   Type   |  Owner
--------+----------------+----------+----------
 public | message        | table    | postgres
 public | message_id_seq | sequence | postgres
(2 rows)
                                    Table "public.message"
 Column  |          Type          | Collation | Nullable |               Defaul
t
---------+------------------------+-----------+----------+-------------------------------------
 id      | integer                |           | not null | nextval('message_id_seq'::regclass)
 message | character varying(255) |           | not null |
Indexes:
    "message_pkey" PRIMARY KEY, btree (id)

環境を作る

Docker オフィシャルの PostgreSQL イメージを使って作成する。

Docker オフィシャルの PostgreSQL イメージは、コンテナ起動時に指定した sql を実行する機能があるので、その仕組みを使ってデータを入れる。

初期データ作成 sql

$(pwd)/schema/schema.sql を作成。

DROP TABLE IF EXISTS message;
DROP SEQUENCE IF EXISTS message_id_seq;

CREATE SEQUENCE message_id_seq START WITH 1 INCREMENT BY 1;

CREATE TABLE message (
  id INTEGER DEFAULT nextval('message_id_seq') PRIMARY KEY,
  message VARCHAR(255) NOT NULL
);

INSERT INTO message(message)
    VALUES
        ('テスト1'),
        ('テスト2');

コンテナ起動 + データ作成

次のコマンドで、 PostgreSQL サーバー起動と、前述の初期データの作成が完了する。

docker run -it --rm --name postgres -v "$(pwd)/schema:/docker-entrypoint-initdb.d" -p 5432:5432 postgres

プログラミング

エンティティの作成

まず、 DB から取得するレコードを表す Bean を作成する。

  • 今回は、 idmessage を取得したいので、それらをフィールドに持つ Bean を作る
  • アノテーション @Entity を付ける
  • プライマリキーに @Id アノテーションを付ける
  • id はシーケンスによる自動採番なので、それ関係のアノテーションを付ける
    • @SequenceGenerator
    • @GeneratedValue
package gs.postgres;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;

/**
 * Message
 */
@Entity
@SequenceGenerator(name = "message_id_seq", sequenceName = "message_id_seq", allocationSize = 1, initialValue = 1)
public class Message {
    @Id
    @GeneratedValue(generator="message_id_seq")
    private Long id;

    private String message;

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return String.format("id:%d, message=%s", this.id, this.message);
    }
}

リポジトリインターフェースを作成

DB への操作を抽象化してくれるやつを作る。

JpaRepository を継承したインターフェース定義だけ定義すれば、 Spring が良しなに実装を作ってくれるらしい。

継承する JpaRepository は、 JpaRepository<エンティティの型, エンティティのプライマリキーの型> とする。

package gs.postgres;

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

/**
 * MessageRepository
 */
public interface MessageRepository extends JpaRepository<Message, Long> {
}

DB にアクセスするプログラムを作る

情報取得と挿入、標準出力するプログラムを作成する。

  • repo フィールドに @Autowired アノテーションを付けているため、 Spring によって自動で MessageRepository のインスタンス化と代入が行われる。
    • MessageRepository は、SpringApplication.run(App.class, args) をした時点で、よしなに実装が作られるみたい
  • repoJpaRepository (Spring Data JPA 2.1.0.RELEASE API) のインターフェースが実装されているので、これを使って DB 操作ができる
package gs.postgres;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.domain.Sort;

@SpringBootApplication
public class App implements CommandLineRunner {

    // `SpringApplication.run` で実行した時点で、
    // よしなに `MessageRepository` クラスが注入されるらしい。
    @Autowired
    private MessageRepository repo;

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

    /**
     * App 主処理
     *
     * 1. メッセージを全件取得して標準出力
     * 2. メッセージインサート
     * 3. メッセージを全件取得して標準出力
     *
     */
    @Override
    public void run(String... args) {

        // DB(postgres/message) から全件取得する
        List<Message> messages1 = repo.findAll(new Sort(Sort.Direction.ASC, "id"));

        // 取得した全件標準出力
        for (Message message : messages1) {
            System.out.println(message);
        }

        // 新規メッセージのインサート
        // ID はシーケンスからの自動採番なので null のままで良い
        Message insertMessage = new Message();
        insertMessage.setMessage("インサートメッセージ");
        repo.save(insertMessage);

        System.out.println("Message added.");

        // もう一度全件取得する
        List<Message> messages2 = repo.findAll(new Sort(Sort.Direction.ASC, "id"));

        // 取得した全件標準出力
        for (Message message : messages2) {
            System.out.println(message);
        }
    }
}

アプリの設定

src/main/resourcesapplication.propertieshibernate.properties を作成する。

application.properties

PostgreSQL に接続するための情報を記述。

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

hibernate.properties

hibernate.properties はこのエラー対策

hibernate.jdbc.lob.non_contextual_creation = true

動作確認

ここまで作ったらあとは gradle run するだけ。

gradle run

以下のような感じで取得とインサートが出来ていることが確認できる。

> gradle run

> Task :run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.5.RELEASE)

...(略)...

2018-10-14 14:36:00.877  INFO 25552 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
id:1, message=テスト1
id:2, message=テスト2
Message added.
id:1, message=テスト1
id:2, message=テスト2
id:3, message=インサートメッセージ

...(略)...

2018-10-14 14:36:01.047  INFO 25552 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

BUILD SUCCESSFUL in 5s
3 actionable tasks: 1 executed, 2 up-to-date

完成品はこちら

以上。

参考資料

1 件のコメント:

  1. おはようございます。

    手軽にアプリの雛形ができるのは、有り難いですね。

    返信削除