はじめに
お疲れ様です! @Keichan_15 です!
今回は Spring Boot と PostgreSQL を使用して、フォームに入力した値をDBに登録した後に、登録データを表示する機能を作成していきたいと思います。
過去にRailsを触れていたこともあって、比較的開発体験の良いSpring Boot。かなり気に入ってます。
それでは早速行っていきましょう!
環境
- Windows11(Windows10でもほぼ同じ)
- Eclipse 2022-12 (4.26.0)
- Spring Boot
- PostgreSQL v15.3
プロジェクト作成
最初にSpring Bootのプロジェクト作成から行います。導入方法は様々あるのですが、今回はSpring公式が チュートリアル として公開している Spring Initializr を使用していきます。
ファイル > 新規 > Springスターター・プロジェクト(Spring Initializr)
を選択します。
特段デフォルトで問題ありません。次へ
を選択します。
依存関係を追加します。デフォルトで追加されているものに加えて、以下3つの依存関係を追加します。
Thymeleaf
PostgreSQL Driver
Spring Data JPA
特にPostgreSQL Driver
とSpring Data JPA
はPostgreSQLの接続に必要なので、選択漏れが無いようにしましょう。
※ちなみに余談ですが、ビルド・ツールはGradle or Mavenか問題、実際の所どちらが良いんでしょう。まだ私はGradleしか使用したことが無いので何とも言えないですが…。
少し脱線してしまいましたね…(-_-;) 気を取り直して。
プロジェクト作成後のディレクトリ構成は以下になります。
demo
│ .classpath
│ .gitignore
│ .project
│ build.gradle
│ gradlew
│ gradlew.bat
│ HELP.md
│ settings.gradle
│
├─.gradle
│ ├─7.6.1
│ │ │ gc.properties
│ │ │
│ │ ├─checksums
│ │ │ checksums.lock
│ │ │
│ │ ├─dependencies-accessors
│ │ │ dependencies-accessors.lock
│ │ │ gc.properties
│ │ │
│ │ ├─executionHistory
│ │ │ executionHistory.lock
│ │ │
│ │ ├─fileChanges
│ │ │ last-build.bin
│ │ │
│ │ ├─fileHashes
│ │ │ fileHashes.lock
│ │ │
│ │ └─vcsMetadata
│ ├─buildOutputCleanup
│ │ buildOutputCleanup.lock
│ │ cache.properties
│ │
│ └─vcs-1
│ gc.properties
│
├─.settings
│ org.eclipse.buildship.core.prefs
│
├─bin
│ ├─main
│ │ │ application.properties
│ │ │
│ │ ├─com
│ │ │ └─example
│ │ │ └─demo
│ │ │ DemoApplication.class
│ │ │
│ │ └─templates
│ └─test
│ └─com
│ └─example
│ └─demo
│ DemoApplicationTests.class
│
├─gradle
│ └─wrapper
│ gradle-wrapper.jar
│ gradle-wrapper.properties
│
└─src
├─main
│ ├─java
│ │ └─com
│ │ └─example
│ │ └─demo
│ │ DemoApplication.java
│ │
│ └─resources
│ │ application.properties
│ │
│ ├─static
│ └─templates
└─test
└─java
└─com
└─example
└─demo
DemoApplicationTests.java
また、依存関係が問題無く追記されているかをbuild.gradle
で確認します。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
上記のようになっていればSpring Bootの導入は完了です。お疲れ様でした。
次にPostgreSQLの準備を行います。
PostgreSQLの導入
PostgreSQLをまだダウンロードしていない方は、EDB からダウンロードしましょう。
今回は Windows x86-64
最新版の v15.3
をダウンロードしました。OSによってはサポート外の場合や、ダウンロードリンクが別ページに記載されている場合があるため、注意して確認してください。
PostgreSQLのダウンロード方法については本記事の主題から逸れてしまうため、割愛させて頂きます。
日本PostgreSQLユーザー会が紹介している インストールガイド や、下記の記事などが参考になるかと思います。
PostgreSQLのダウンロードが完了したら、早速CRUDのCであるデータ登録用のDBを準備していきましょう!
DB準備
まずは本記事用のDBを構築します。
個人的に、最初はCLIで黒い画面と睨めっこしながら進めていく方が勉強になると思います。以下の記事などがそうです。
ただ今回は手っ取り早く構築したいので、便利なGUIであるpgAdmin4
を使用します。
後にpgAdmin4
を使用する理由が分かります。(笑)
PGAdmin4
はpostgreSQLをデフォルトでダウンロードした場合、 C:\Program Files\PostgreSQL\15\pgAdmin4\runtime
にexeが存在しています。
exeを実行し、pgAdmin4
を開くとパスワードの入力が求められますので、インストール時に設定したパスワードをここで入力してください。
以下のような画面が表示されていればOKです。
このように、視覚的にDBを操作できる & UIが綺麗なので、個人的にはおススメですね。
特段CLIにこだわりがあったり、勉強の一環でコマンド打ちたかったりって方を除き、使うべきなのでは無いかなと。
次にデータベースを作成します。今回は testdb
という名前のデータベースを作成します。
左のObject Explorer
からDatabasesをクリックし、 右クリック > Create > Database...
と進みます。
Databaseの項目に testdb
と入力し、右下の Save
をクリックすればデータベースが作成されます。
問題無く保存できるとObject Explorer
のdatabases内にtestdb
が追加されていることが確認できます。
これでDB作成は完了です!お疲れ様でした。次はテーブルとカラムを作成していきましょう!
テーブル&カラム作成
まず、登録したデータを保持するための forums
テーブルと、各データを保持するためのカラムを作成します。
Object Explorer
のtestdb
を右クリックし、Query Tool
を選択します。
以下のSQL文を実行してください。
create TABLE forums (
id serial primary key,
title varchar(20),
body varchar(100)
);
このような表記が出ればテーブルが作成できています。
画面に反映させるためにRefresh...
を選択します。これもtestdb
を右クリックすると出てきます。
以下のように追加したテーブルが表示されます。
ここで今回登録した各カラムについて簡単にまとめておきます。
-
id
: AUTO INCREMENTによる自動採番 -
title
: 投稿の「タイトル」を保持するカラム。文字数上限は20文字。 -
body
: 投稿の「本文」を保持するカラム。文字数上限は100文字。
次にカラムが追加されているかも確認してみましょう。
lists
を右クリックすると View/Edit Data > All Rows
を選択すると、テーブル内の全てのデータを表示することができます。
現在はテーブルとカラムを定義しただけなので、写真のようにまっさらな状態になっています。
イメージはこのテーブルの各カラムに対して、Spring Boot側で作成したフォームより入力された値が格納されるようになります。
これでテーブルとカラムの作成は完了です。
最後にSpring Bootのapplication.properties
を編集します。
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=postgres
spring.datasource.password="インストール時に決めたパスワード"
spring.jpa.open-in-view=True
この設定を行わないとSpring BootとpostgreSQLが接続できないので記入漏れが無いようにしましょう。
spring.datasource.password="インストール時に決めたパスワード"
パスワードはご自身で決めたものに変更してくださいね。
実装
いよいよSpring Bootの実装に入っていきます。
Spring Bootの詳しい内容については今後別記事でまとめる予定ですが、初めて触る方に向けて適宜解説を挟んでいきますので、分かりにくい等あればコメント頂けると幸いです。
今回は同期通信によるデータ登録のTipsを説明します。非同期通信(Ajax × jQuery)によるデータ登録については今後、別記事でまとめる予定です。
(寄り道)MVCモデルって??
まず、Spring Bootを扱う上で大切な概念にMVCモデルがあります。Ruby on RailsなどのWebフレームワークを使用したことがある方はお馴染みですね。
MVCモデルは各頭文字である M(Model) V(View) C(Controller) に分けてコードを管理するものです。
- Model : データベースとのやりとりやシステム内のデータ処理を担当
- View : 画面表示を担当
- Controller : ViewとModelの動作制御を担当(いわば仲介役)
MVCモデルを採用することにより、役割ごとにファイルが独立しているため、保守性や開発効率向上に繋がることがメリットとして挙げられています。
◎Model作成
最初にシステムの根幹ともいえるモデルから作成していきましょう。
今回作成するモデルはForum.java
になります。
package com.example.demo.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@Entity
@Table(name = "forums")
public class Forum {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "title")
private String title;
@Column(name = "body")
private String body;
}
解説
まずJavaを使用して実装したことが無い方は、おそらく以下の表記に混乱されていると思います。
@Data
@Entity
@Table
@Id
@GeneratedValue
@Column
これはアノテーションと呼ばれています。アノテーションとは直訳すると注釈という意味で、ソースコードにアノテーションを加えることによってプログラムの動作を変更したり、Spring Bootを始めとするframeworkに対して処理を指示したりすることができます。
例えば@Data
ですが、このアノテーションはコンパイル時に以下のメソッドを自動生成します。
- setter()
- getter()
- toString()
- equals()
- hashCode()
特に上2つのgetter
,setter
の表記を行わなくて良い点は、
- 開発者側も実装負担が減る
- ソースコードの可読性向上
にも繋がるので、アノテーションを使わない手はないといえるでしょう。
1つ1つのアノテーションを詳しく解説するのは本記事の主題から逸れるため省略します。
以下のサイトでアノテーションの一覧がまとめられています。非常に便利…。
以下、各アノテーションや実装内容をコメントで付記しましたので、学習にお役立てください。
package com.example.demo.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
// getter(), setter()を自動生成するアノテーション
@Data
// JPAのエンティティであることを示すアノテーション
@Entity
// エンティティに対応するテーブル名を指定。 "forums"部分はPostgreSQLで作成したテーブル名
@Table(name = "forums")
public class Forum {
// エンティティの主キーを指定。今回であれば "id" カラムになる
@Id
// オートインクリメント。主キー "id" の値を自動採番する
@GeneratedValue(strategy = GenerationType.IDENTITY)
// カラムに名前を付与
@Column(name = "id")
private Integer id;
@Column(name = "title")
private String title;
@Column(name = "body")
private String body;
}
これでModelの実装は完了です!
◎Controller作成
次にControllerを作成していきます。
まず ForumController
の作成を行います。
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.example.demo.model.Forum;
import com.example.demo.service.ForumService;
@Controller
public class ForumController {
// 特定のアノテーションを付与したクラスのインスタンスを使用可能にする(依存性注入・DI)
@Autowired
ForumService service;
// トップページ(フォームがある画面)
// HTTPリクエストの"GET"を受け付けるメソッドに付与するアノテーション
@GetMapping("/")
public String top(Model model) {
model.addAttribute("forum", new Forum());
return "top";
}
// データベースへの登録
// HTTPリクエストの"POST"を受け付けるメソッドに付与するアノテーション
@PostMapping("/create")
public String saveForum(@ModelAttribute Forum forum, Model model) {
// serviceクラスのinsertメソッドを呼び出し、DBに入力値を登録
service.insert(forum);
return "result";
}
}
解説
今回登場したアノテーションは @Autowired
@GetMapping
@PostMapping
になります。
中でも@Autowired
についてですが、これは DI(依存性注入) を使用しています。特定のアノテーション(今回であれば@Serviceや後述の@Repository)を付与したクラスのインスタンスは、@Autowired
を付与したフィールド変数などによって取得することができます。
次にトップページを表示するためのtop
メソッドについて見ていきましょう。
@GetMapping("/")
public String top(Model model) {
model.addAttribute("forum", new Forum());
return "top";
}
@GetMapping
によって、引数に指定しているURLにGETのHTTPリクエストが送られると、このメソッドが処理される仕組みになっています。ex)localhost:8080/
にアクセス
model.addAttribute("forum", new Forum());
はViewに値を渡すために使用します。
addAttribute
の引数の設定方法ですが、第1引数にViewで使用する変数名、第2引数にViewに渡したい値を設定します。
今回であればforum
という変数にはForum
クラスの空インスタンスが格納されており、これがViewのオブジェクト部分(後述のViewで紹介)に設定されることで、フォームの値を格納することができるようになります。
そして戻り値に top
と指定していますが、この戻り値の名前がViewのファイル名になります。ポイントです。
流れのイメージをまとめると、
1. ルートパスにHTTPリクエスト(GET)が送られる
2. model.addAttributeでViewにフォームの空インスタンスを渡す
3. top.htmlを表示する
となります。こうすることでtop.html
でフォームを作成することができるんですね。
次にDB登録に必要なメソッドであるsaveForum
を見ていきます。
@PostMapping("/create")
public String saveForum(@ModelAttribute Forum forum, Model model) {
service.insert(forum);
return "result";
}
ほとんどは先に挙げたtop
メソッドと変わりはありません。新たに登場した内容だけ紹介します。
-
@ModelAttribute
saveForum
メソッドの引数に@ModelAttribute
のアノテーションが付与されています。
このアノテーションが付与されることにより、フォームで入力した値を受け取ることができるようになります。
引数のforum
は、後述のtop.html
のView
で作成したフォームのオブジェクト部分に入っているデータになります。
以下のform
タグ内にあるth:object
のforum
になります。
Viewの説明は後ほど詳しく行いますので、フォームで入力された値を受け取っているということを認識して頂ければ問題ありません!
<form th:action="@{/create}" th:object="${forum}" method="post">
-
service.insert(forum);
ここではServiceクラスのinsertメソッドを呼び出しています。前述のDI(依存性注入)によって呼び出すことができる仕組みです。
insert()
の引数にフォームに入力された値が格納されているオブジェクトを設定することで、DBへ登録処理を行うことができます。
insert()
メソッドの詳しい内容についても、後ほどServiceクラスの章で説明します。
DBへの登録処理が完了したら、result.html
に遷移するように設定しています。
コントローラーの説明は以上です。難しいですね…
◎Viewの作成
続いてViewを作成します。
Spring BootではViewファイルを src/resources/templates
配下に置くことで、画面を表示することができます。
まずフォーム画面を表示するためのトップページを作成します。
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Index</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h2>【New Post】</h2>
<form th:action="@{/create}" th:object="${forum}" method="post">
<label>Title</label>
<p><input type="text" th:field="*{title}" /></p>
<label>Body</label>
<p><textarea id="form" th:field="*{body}"></textarea></p>
<p><input type="submit" value="送信" id="button" /></p>
</form>
</body>
</html>
また、投稿内容を表示する画面も実装します。
<!DOCTYPE HTML>
<html>
<head>
<title>Result View</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div>
<p>[[${forum.title}]]</p>
<p>[[${forum.body}]]</p>
<p><a href="http://localhost:8080/">一覧画面へ</a></p>
</div>
</body>
</html>
解説
まずトップページから見ていきます。
今回はテンプレート・エンジンにthymeleaf
を使用しています。
<html xmlns:th="http://www.thymeleaf.org">
ここでthymeleaf
の属性をth
とし、名前空間を宣言して利用しています。
フォーム関連もthymeleaf
を使用することで簡潔に実装が可能です。
<form th:action="@{/create}" th:object="${forum}" method="post">
th:action | th:object | method |
---|---|---|
フォームのアクションを設定(Controller側で使用) | ${}内のオブジェクトにフォーム入力値が格納 | HTTPメソッドを指定 |
また、フォーム入力部分はth:field
を使用することで、オブジェクトのフィールドに値を格納します。
<p><input type="text" th:field="*{title}" /></p>
<p><textarea id="form" th:field="*{body}"></textarea></p>
ここで言うフィールドは、モデルであるForum.java
内のフィールドを指しています。
// フィールド
@Column(name = "id")
private Integer id;
@Column(name = "title")
private String title;
@Column(name = "body")
private String body;
以上がトップページの実装になります。
続いて、結果表示画面について見ていきます。
<p>[[${forum.title}]]</p>
<p>[[${forum.body}]]</p>
薄々お気付きかとは思いますが、上記の部分で入力した内容をそれぞれ表示しています。
以上がViewの実装になります。意外とシンプルでしたね。
◎Repository作成
次にRepositoryを作成します。
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.demo.model.Forum;
@Repository
public interface ForumRepository extends JpaRepository<Forum, Integer> {
}
JpaRepository
を継承したインターフェースを作成し、データベースを操作します。
JpaRepository
を継承することにより、データ操作に関するCRUD(作成・取得・更新・削除)のメソッドを自動で生成してくれます。便利ですこと。
次にServiceを作成します。
◎Service作成
package com.example.demo.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.model.Forum;
import com.example.demo.repository.ForumRepository;
import jakarta.transaction.Transactional;
@Service
@Transactional
public class ForumService {
@Autowired
ForumRepository repository;
// データベースよりフォーラム(掲示板)の一覧を取得
public List<Forum> findAll() {
return repository.findAll();
}
// データベースに値を登録
public void insert(Forum forum) {
repository.save(forum);
}
}
解説
今回新たに登場したアノテーションから見ていきましょう。
@Service
アノテーションはこのクラスがサービスクラスであることを示しています。
また、@Transactional
を付与することでDBのトランザクション制御を行います(今回のような小規模なアプリには必要性皆無)
次にデータベース操作系のメソッドについてです。
repository.findAll();
では、@Autowired
で作成したForumRepositoryのインスタンスを使用して、レコードの全件取得を行っています。登録しているデータを一覧表示したい場合などに使用します。今回は使いません。
repository.save(forum);
では、フォーム入力値が格納されたオブジェクトを引数に設定して、DBへ登録します。
Serviceの実装&解説は以上です。これで実装全てが完了しました。
最終的なツリーを下記に記載しておきます。
demo
│ .classpath
│ .gitignore
│ .project
│ build.gradle
│ gradlew
│ gradlew.bat
│ HELP.md
│ settings.gradle
│
├─.gradle
│ ├─7.6.1
│ │ │ gc.properties
│ │ │
│ │ ├─checksums
│ │ │ checksums.lock
│ │ │ md5-checksums.bin
│ │ │ sha1-checksums.bin
│ │ │
│ │ ├─dependencies-accessors
│ │ │ dependencies-accessors.lock
│ │ │ gc.properties
│ │ │
│ │ ├─executionHistory
│ │ │ executionHistory.lock
│ │ │
│ │ ├─fileChanges
│ │ │ last-build.bin
│ │ │
│ │ ├─fileHashes
│ │ │ fileHashes.lock
│ │ │
│ │ └─vcsMetadata
│ ├─buildOutputCleanup
│ │ buildOutputCleanup.lock
│ │ cache.properties
│ │
│ └─vcs-1
│ gc.properties
│
├─.settings
│ org.eclipse.buildship.core.prefs
│
├─bin
│ ├─main
│ │ │ application.properties
│ │ │
│ │ ├─com
│ │ │ └─example
│ │ │ └─demo
│ │ │ │ DemoApplication.class
│ │ │ │
│ │ │ ├─controller
│ │ │ │ ForumController.class
│ │ │ │
│ │ │ ├─model
│ │ │ │ Forum.class
│ │ │ │
│ │ │ ├─repository
│ │ │ │ ForumRepository.class
│ │ │ │
│ │ │ └─service
│ │ │ ForumService.class
│ │ │
│ │ └─templates
│ │ result.html
│ │ top.html
│ │
│ └─test
│ └─com
│ └─example
│ └─demo
│ DemoApplicationTests.class
│
├─gradle
│ └─wrapper
│ gradle-wrapper.jar
│ gradle-wrapper.properties
│
└─src
├─main
│ ├─java
│ │ └─com
│ │ └─example
│ │ └─demo
│ │ │ DemoApplication.java
│ │ │
│ │ ├─controller
│ │ │ ForumController.java
│ │ │
│ │ ├─model
│ │ │ Forum.java
│ │ │
│ │ ├─repository
│ │ │ ForumRepository.java
│ │ │
│ │ └─service
│ │ ForumService.java
│ │
│ └─resources
│ │ application.properties
│ │
│ ├─static
│ └─templates
│ result.html
│ top.html
│
└─test
└─java
└─com
└─example
└─demo
DemoApplicationTests.java
最後に動作に問題が無いか確認していきましょう!
動作確認
まずサーバーを起動します。サーバー起動はBootダッシュボードから実行できます。
コンソールに以下のような表記が出れば起動完了です。
localhost:8080
にアクセスします。以下のようなページが表示されると思います。
フォームに値を入力して送信
ボタンを押下します。
以下の画面が表示されていればOKです!
DBにも問題無くデータが登録されているか確認しましょう。
問題無さそうです!
以上でSpring Boot × PostgreSQLによる登録機能が完成です。お疲れ様でした!
参考
おわりに
いかがでしたでしょうか。
今回はSpring BootとPostgreSQLを使用して、最低限のデータ登録機能を実装しました。
実際初めてSpring Bootに触れたのですが、冒頭に述べたようにRailsなどのWebフレームワークと似通っていることもあって、比較的すんなり理解することができました。
ただ最近はもっぱらNext.jsとGoとDockerを弄ってるので当分触らなくても良いかなあ
是非皆さんもSpring Boot使っちゃいましょ~~!