こんな感じのことをします。(察して...)
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-api
と javassist
を明示的に追加する必要が出てきたらしい。
- 参考情報
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 を作成する。
- 今回は、
id
とmessage
を取得したいので、それらをフィールドに持つ 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)
をした時点で、よしなに実装が作られるみたい
repo
は JpaRepository (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/resources
に application.properties
と hibernate.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
完成品はこちら。
以上。
参考資料
- 依存関係
- JpaRepository
おはようございます。
返信削除手軽にアプリの雛形ができるのは、有り難いですね。