docker-compose 下で Java + Spring Boot + PostgreSQL (Spring Data JPA編)
そろそろタイトルの文字数がキツくなってきました……
TL;DR
- 下記記事に書いてあるようなこと(+ちょい設定まわり変更)をします
- サンプルコードも同じにさせてもらいました
- SpringBootとPostgreSQLの開発環境をdocker-composeで用意する!上 | Qiita
- SpringBootとPostgreSQLの開発環境をdocker-composeだけで用意する!下 | Qiita
docker-compose.yml を用意する
cd your_project
mkdir server
touch docker-compose.yml
version: '3.6'
services:
app:
image: openjdk:15
container_name: app
ports:
- 8080:8080
tty: true
volumes:
- ./server:/srv:cached
working_dir: /srv
depends_on:
- db
adminer:
image: adminer:4.7.8
container_name: adminer
ports:
- "9000:8080"
depends_on:
- db
db:
image: postgres:13.1
container_name: db
environment:
POSTGRES_USER: "root"
POSTGRES_PASSWORD: "root"
POSTGRES_DB: "dev"
ports:
- "5432:5432"
volumes:
- dbvol:/var/lib/postgresql/data
- ./forDocker/db/initdb:/docker-entrypoint-initdb.d
volumes:
dbvol:
- OpenJDK | DockerHub
- JDK Project Releases | OpenJDK
- 指定できるバージョン情報はここ
- http://openjdk.java.net/projects/jdk/
- postgres | DockerHub
-
/docker-entrypoint-initdb.d
にマウントしている./forDocker/db/initdb
については後述します -
/docker-entrypoint-initdb.d
については Initialization scripts の頁を参照してください - https://hub.docker.com/_/postgres
-
- Adminer | DockerHub
- PostgreSQL にも対応したPHP製の GUI SQL クライアント
- https://hub.docker.com/_/adminer
DB初期投入クエリの準備
postgres の Docker イメージは前述の DockerHub ページに書いてあるように、
/docker-entrypoint-initdb.d
に用意したファイルを
サービス開始前に実行してくれます。(MySQL の Docker イメージにも同様の機能がありますね)
このクエリは /var/lib/postgresql/data
を生成するときに実行されるため、
再度実行させたい時は
今回の docker-compose.yml
であれば docker volume を割り当てているので
dbvol
を docker volume rm ...
で削除してやれば再度実行させることができます。
ではその前提で、DB初期投入クエリの準備をしていきましょう。
mkdir -p forDocker/db/initdb
touch forDocker/db/initdb/1_create_users.sql
touch forDocker/db/initdb/2_insert_users.sql
touch forDocker/db/initdb/3_create_role_appuser.sql
CREATE TABLE users (
id SERIAL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO users (
name
) values (
'test'
);
CREATE ROLE appuser WITH LOGIN PASSWORD 'apppass';
GRANT SELECT,UPDATE,INSERT,DELETE ON ALL TABLES IN SCHEMA public TO appuser;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO appuser;
ファイル名順に実行してくれるのでファイル名にシーケンスを振っています。
3_create_role_appuser.sql でJavaアプリケーションから利用する用のユーザを作成しています。
気持ちとしてはテーブル作成や初期投入データINSERTの前にユーザを作成したいところですが、
GRANT...ON ALL TABLES IN SCHEMA public TO...
では
既に作成済みのテーブルにしか権限が付与されないようだったので
このようなクエリ順になっています。
DBに対して権限を付与してやれば最初でも大丈夫ですが、今回はちょっとうまくいかなかったので割愛します。
Gradleプロジェクト作成
今回も Spring Initializr というサイトで作ってしまいます。
- Spring Initializr
PostgreSQL Driver と Spring Data JPA を Dependencies に追加するのを忘れないようにしてください。
Spring Boot で扱える ORM にはいくつか種類があるんですが、
今回は参考にした記事でも使っている Spring Data JPA を使用します。
- Java ORマッパー選定のポイント | SlideShare
閑話休題。
入力が終わったら GENERATE してダウンロードします。
ダウンロードしたら展開したものを
your_project/server 配下に配置します。
server/src/main/java/com/example/api/ApiApplication.java はダウンロードしてきたままでOK.
package com.example.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
server/src/main/resources/application.properties を以下のように編集します。
spring.datasource.url=jdbc:postgresql://db:5432/dev
spring.datasource.username=appuser
spring.datasource.password=apppass
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none
-
spring.datasource.url=jdbc:postgresql://db:5432/dev
- Docker としてアクセスする場合は
jdbc:postgresql://{DBコンテナ名}:{ポート}/{DB名}
とするようです - DB名 は docker-compose.yml で dev としましたね
- Docker としてアクセスする場合は
spring.datasource.username=appuser
-
spring.datasource.password=apppass
- forDocker/db/initdb/3_create_role_appuser.sql で用意したユーザです
- その他のオプションについては以下を参照
- Spring Boot アプリケーションプロパティ設定一覧 - Spring リファレンス
Spring Data JPA で必要な Entity と Repository を作成
Spring Data JPA は ORM なので
Entity(テーブルのマッピング先) と Repository(DBアクセスリポジトリ) を作成するだけで
DB Access が可能です。
# server/src/main/java/com/example/app までは Spring Initializr からDLしたものを展開した時点で存在するはずです
mkdir server/src/main/java/com/example/app/entity
touch server/src/main/java/com/example/app/entity/User.java
mkdir server/src/main/java/com/example/app/repository
touch server/src/main/java/com/example/app/repository/UserRepository.java
package com.example.app.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
protected User() {}
public User(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return String.format("{id:%d,name:%s}", id, name);
}
}
@GeneratedValue
は DBの設定と連携して IDの自動採番をする設定です。
詳しくは以下が分かりやすかったです。
forDocker/db/initdb/1_create_users.sql にて
users.id
は SERIAL で定義しましたね。
- @GeneratedValueを使って主キーを生成する方法 | Qiita
package com.example.app.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.app.entity.User;
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
UserRepository
には今回特に新しい定義は追加しないので
CrudRepository
のインターフェースをそのまま利用します。
DI を利用して User にアクセスする Controller を作成
Spring の強力なDIを使って Controller を用意します。
mkdir server/src/main/java/com/example/app/controller
touch server/src/main/java/com/example/app/controller/UserController.java
package com.example.app.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.app.repository.UserRepository;
@RestController
public class UserController {
private final UserRepository repository;
@Autowired
public UserController(UserRepository repository) {
this.repository = repository;
}
@RequestMapping("/")
public String user() {
return String.valueOf(repository.findAll());
}
}
@Autowired
をつけることで Spring の DI 機能により new して突っ込んでくれます。
以下などが分かりやすかったです。
- 【Spring】@Autowired と @Component を使用した DI の基本 | 山崎屋の技術メモ
@RestController
と @RequestMapping("/")
は以下記事でも扱いましたね。
今回、Response は String にしています。
本来であれば Repository を扱う Domain Model(Service) を作った方がいいんですが今回は割愛しました。
- docker-compose 下で Java + Spring Boot + 簡単なWeb API を作ってみる | 北山淳也 | zenn
Docker コンテナを起動してGradleビルド, アプリケーション起動, リクエストを投げてみる
docker-compose up -d
# DB が立ち上がって初期化されるまでちょっとかかるのでちょっと待つ
docker-compose exec app bash
bash-4.4# sh gradlew build
...
BUILD SUCCESSFUL in 8m 41s
5 actionable tasks: 5 executed
# できてるのを確認
bash-4.4# ls build/libs/
app-0.0.1-SNAPSHOT.jar
bash-4.4# java -jar build/libs/app-0.0.1-SNAPSHOT.jar
起動したらリクエストを投げてみましょう。
$ curl http://localhost:8080 -X GET
[{id:1,name:test}]
OK ですね!
アプリケーションの終了は例によって Ctrl+c です。
コンテナから出るのは exit
, コンテナの停止は docker-compose down
で。
Adminer からも PostgreSQL に接続してみる。
docker-compose.yml で書いたように
http:localhost:9000
で Adminer にアクセスできます。
こんな感じでログインできるので、
ログインしたら左側の「SQLコマンド」でSQLコマンドが発行できるので
こんな感じでクエリ結果をみることが出来ます。便利!
今回のリポジトリはこちらです。
参考
- SpringBootとPostgreSQLの開発環境をdocker-composeで用意する!上 | Qiita
- SpringBootとPostgreSQLの開発環境をdocker-composeだけで用意する!下 | Qiita
- VSCodeとDockerでSpring Boot + PostgreSQL開発環境を作る(1) | Sales8開発者日記
- VSCodeとDockerでSpring Boot + PostgreSQL開発環境を作る(2) | Sales8開発者日記
- SpringBootに入門する為の助走本(随時更新) | Qiita
- 今更ながらSpring Data JPAに入門してみた | Qiita
- Spring Boot with Docker な開発環境を考える | tiqwablog
- 【Spring Boot入門(5)】RestAPI(POST)を作ってみる | 釣りキチエンジニアのブログ
- Spring MVCのコントローラでの戻り値いろいろ | Qiita
Discussion