LoginSignup
2
4
お題は不問!Qiita Engineer Festa 2023で記事投稿!

Spring Boot × PostgreSQLによるデータ登録Tips.(解説付)

Last updated at Posted at 2023-06-14

はじめに

お疲れ様です! @Keichan_15 です!

今回は Spring BootPostgreSQL を使用して、フォームに入力した値をDBに登録した後に、登録データを表示する機能を作成していきたいと思います。

過去にRailsを触れていたこともあって、比較的開発体験の良いSpring Boot。かなり気に入ってます。

それでは早速行っていきましょう!:muscle:

環境

  • Windows11(Windows10でもほぼ同じ)
  • Eclipse 2022-12 (4.26.0)
  • Spring Boot
  • PostgreSQL v15.3

プロジェクト作成

最初にSpring Bootのプロジェクト作成から行います。導入方法は様々あるのですが、今回はSpring公式が チュートリアル として公開している Spring Initializr を使用していきます。

ファイル > 新規 > Springスターター・プロジェクト(Spring Initializr) を選択します。

image.png

特段デフォルトで問題ありません。次へを選択します。

image.png

依存関係を追加します。デフォルトで追加されているものに加えて、以下3つの依存関係を追加します。

  • Thymeleaf
  • PostgreSQL Driver
  • Spring Data JPA

特にPostgreSQL DriverSpring 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で確認します。

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 からダウンロードしましょう。

image.png

今回は 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です。

image.png

このように、視覚的にDBを操作できる & UIが綺麗なので、個人的にはおススメですね。
特段CLIにこだわりがあったり、勉強の一環でコマンド打ちたかったりって方を除き、使うべきなのでは無いかなと。

次にデータベースを作成します。今回は testdb という名前のデータベースを作成します。

左のObject ExplorerからDatabasesをクリックし、 右クリック > Create > Database... と進みます。

image.png

image.png

Databaseの項目に testdb と入力し、右下の Save をクリックすればデータベースが作成されます。
image.png

問題無く保存できるとObject Explorerのdatabases内にtestdbが追加されていることが確認できます。

image.png

これでDB作成は完了です!お疲れ様でした。次はテーブルとカラムを作成していきましょう!

テーブル&カラム作成

まず、登録したデータを保持するための forums テーブルと、各データを保持するためのカラムを作成します。
Object Explorertestdbを右クリックし、Query Toolを選択します。

image.png

以下のSQL文を実行してください。

PostgreSQL
create TABLE forums (
  id serial primary key,
  title varchar(20),
  body varchar(100)
);

image.png

このような表記が出ればテーブルが作成できています。

画面に反映させるためにRefresh...を選択します。これもtestdbを右クリックすると出てきます。

image.png

以下のように追加したテーブルが表示されます。

image.png

ここで今回登録した各カラムについて簡単にまとめておきます。

  • id : AUTO INCREMENTによる自動採番
  • title : 投稿の「タイトル」を保持するカラム。文字数上限は20文字。
  • body : 投稿の「本文」を保持するカラム。文字数上限は100文字。

次にカラムが追加されているかも確認してみましょう。

listsを右クリックすると View/Edit Data > All Rows を選択すると、テーブル内の全てのデータを表示することができます。

image.png

image.png

現在はテーブルとカラムを定義しただけなので、写真のようにまっさらな状態になっています。
イメージはこのテーブルの各カラムに対して、Spring Boot側で作成したフォームより入力された値が格納されるようになります。

これでテーブルとカラムの作成は完了です。

最後にSpring Bootのapplication.propertiesを編集します。

src/resources/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="インストール時に決めたパスワード"

パスワードはご自身で決めたものに変更してくださいね。:relaxed:

実装

いよいよSpring Bootの実装に入っていきます。
Spring Bootの詳しい内容については今後別記事でまとめる予定ですが、初めて触る方に向けて適宜解説を挟んでいきますので、分かりにくい等あればコメント頂けると幸いです。

今回は同期通信によるデータ登録のTipsを説明します。非同期通信(Ajax × jQuery)によるデータ登録については今後、別記事でまとめる予定です。

:writing_hand:(寄り道)MVCモデルって??

まず、Spring Bootを扱う上で大切な概念にMVCモデルがあります。Ruby on RailsなどのWebフレームワークを使用したことがある方はお馴染みですね。

MVCモデルは各頭文字である M(Model) V(View) C(Controller) に分けてコードを管理するものです。

  • Model : データベースとのやりとりやシステム内のデータ処理を担当
  • View : 画面表示を担当
  • Controller : ViewとModelの動作制御を担当(いわば仲介役)

MVCモデルを採用することにより、役割ごとにファイルが独立しているため、保守性や開発効率向上に繋がることがメリットとして挙げられています。

◎Model作成

最初にシステムの根幹ともいえるモデルから作成していきましょう。

今回作成するモデルはForum.javaになります。

src/main/java/com/example/demo/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;
}

:speaking_head: 解説

まずJavaを使用して実装したことが無い方は、おそらく以下の表記に混乱されていると思います。

@Data
@Entity
@Table
@Id
@GeneratedValue
@Column

これはアノテーションと呼ばれています。アノテーションとは直訳すると注釈という意味で、ソースコードにアノテーションを加えることによってプログラムの動作を変更したり、Spring Bootを始めとするframeworkに対して処理を指示したりすることができます。

例えば@Dataですが、このアノテーションはコンパイル時に以下のメソッドを自動生成します。

  • setter()
  • getter()
  • toString()
  • equals()
  • hashCode()

特に上2つのgetter,setterの表記を行わなくて良い点は、

  • 開発者側も実装負担が減る
  • ソースコードの可読性向上

にも繋がるので、アノテーションを使わない手はないといえるでしょう。

1つ1つのアノテーションを詳しく解説するのは本記事の主題から逸れるため省略します。
以下のサイトでアノテーションの一覧がまとめられています。非常に便利…。:relaxed:

以下、各アノテーションや実装内容をコメントで付記しましたので、学習にお役立てください。

src/main/java/com/example/demo/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;

// 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の作成を行います。

src/main/java/com/example/demo/controller/ForumController.java
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";
	}

}

:speaking_head: 解説

今回登場したアノテーションは @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.htmlViewで作成したフォームのオブジェクト部分に入っているデータになります。

以下のformタグ内にあるth:objectforumになります。
Viewの説明は後ほど詳しく行いますので、フォームで入力された値を受け取っているということを認識して頂ければ問題ありません!

<form th:action="@{/create}" th:object="${forum}" method="post">
  • service.insert(forum);
    ここではServiceクラスのinsertメソッドを呼び出しています。前述のDI(依存性注入)によって呼び出すことができる仕組みです。
    insert()の引数にフォームに入力された値が格納されているオブジェクトを設定することで、DBへ登録処理を行うことができます。
    insert()メソッドの詳しい内容についても、後ほどServiceクラスの章で説明します。

DBへの登録処理が完了したら、result.htmlに遷移するように設定しています。

コントローラーの説明は以上です。難しいですね…:cry:

◎Viewの作成

続いてViewを作成します。

Spring BootではViewファイルを src/resources/templates配下に置くことで、画面を表示することができます。

まずフォーム画面を表示するためのトップページを作成します。

src/resources/templates/top.html
<!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>

また、投稿内容を表示する画面も実装します。

src/resources/templates/result.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>

:speaking_head: 解説

まずトップページから見ていきます。

今回はテンプレート・エンジンに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内のフィールドを指しています。

src/main/java/com/example/demo/model/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を作成します。

src/main/java/com/example/demo/repository/ForumRepository.java
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);
	}
}

:speaking_head: 解説

今回新たに登場したアノテーションから見ていきましょう。

@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ダッシュボードから実行できます。

image.png

コンソールに以下のような表記が出れば起動完了です。

image.png

localhost:8080にアクセスします。以下のようなページが表示されると思います。

image.png

フォームに値を入力して送信ボタンを押下します。

image.png

以下の画面が表示されていればOKです!

image.png

DBにも問題無くデータが登録されているか確認しましょう。

image.png

問題無さそうです!:muscle:

以上でSpring Boot × PostgreSQLによる登録機能が完成です。お疲れ様でした!

参考

おわりに

いかがでしたでしょうか。
今回はSpring BootとPostgreSQLを使用して、最低限のデータ登録機能を実装しました。

実際初めてSpring Bootに触れたのですが、冒頭に述べたようにRailsなどのWebフレームワークと似通っていることもあって、比較的すんなり理解することができました。
ただ最近はもっぱらNext.jsとGoとDockerを弄ってるので当分触らなくても良いかなあ

是非皆さんもSpring Boot使っちゃいましょ~~!:v:

2
4
0

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
2
4