1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Spring Boot入門】#2 登録機能を作成する

Last updated at Posted at 2024-10-31

はじめに

自身の知識のアウトプットも兼ねて、新人研修用に作成した記事となります。Spring Bootを学び始めた方を対象とした内容になっています。

新規登録画面の作成と表示については、前回の記事を参照ください。

概要

企業情報の登録機能を作成していきます。
クライアントから渡ってきた企業情報をDBに追加し、最後に登録完了画面を返す機能を作成します。

完成イメージ

image.png

image.png

image.png

パッケージ構成

赤枠で囲ったパッケージ、クラス、HTMLファイルを今回作成していきます。また、青枠で囲ったクラスを編集していきます。
image.png

Entityクラスの作成

このクラスは、データベースのテーブルをそのままミラーリングしたオブジェクトです。要は、データベースのテーブルのカラムと連携させる変数(フィールド)を持つクラスです。

ここでは、Formクラスがクライアントとのデータの受け渡しを担当するのに対し、Entityクラスはデータベースとのデータの受け渡しを担当します。
配置先:src > main > java > com > example > demo > entity > Company.java

Company.java
package com.example.demo.entity;

import java.util.Date;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
	
	@Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long companyId;	// 企業ID

	@NotBlank
    private String name;	// 会社名
    
	@NotNull
    private int employees;	// 従業員数

    @Temporal(TemporalType.DATE)
    private Date establishmentDate;	// 設立日
    
}

@￰Entity

このクラスがJPAで利用するEntityクラスであることを宣言します。
データベースの1行と対になっていることを示します。

@￰Id

主キーを指定します。

@￰GeneratedValue(strategy = GenerationType.IDENTITY)

この記述により自動採番してくれます。MySQLの場合はAuto Increment(AI)として設定されます。

@￰NotBlank

必須チェックのためのアノテーションです。Null、空文字、半角スペースが許可されないことを示します。

@￰NotNull

必須チェックのためのアノテーションです。NotNullであることを示します。

@￰Temporal

日付型のオブジェクトの型を指定します。ここでは日付の部分のみ指定しています。(TemporalType.DATE)

Entityクラスを作成することで、下記のように接続情報がデータベースの自動更新を行う設定になっていれば、アプリケーション起動時に自動でEntityの定義に基づいてテーブルを作成してくれます。

application.properties
# Spring-JPA: DBのテーブルを自動作成してくれる機能
spring.jpa.hibernate.ddl-auto=update

試しにアプリケーションを起動してみると...

image.png

sampleデータベースにEntityクラスで指定した定義に基づいてcompanyというテーブルが自動で作成されました。

補足:

今回はEntityクラスの定義からテーブルを自動で作成しましたが、@Tableアノテーションや@Columnアノテーションを使うことで、テーブル名やカラム名、Null制約などを明示的に指定することもできます。

Example
@Entity
@Data
@Table(name="tablename")
public class Company {
	
	@Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id")
    private Long companyId;	// 企業ID

	@NotEmpty
    @Column(name = "company_name", length = 50, nullable = false)
    private String name;	// 会社名
    
	@NotNull
    @Column(name = "employees", length = 10, nullable = false)
    private int employees;	// 従業員数

    @Temporal(TemporalType.DATE)
    @Column(nullable = true)
    private Date establishmentDate;	// 設立日
    
}

@Tableを省略した場合はクラス名を大文字にした名前のテーブルにマッピングされされ、@Columnを省略した場合はプロパティ名を大文字にした名前のカラムへマッピングされます。(上述の通り)

@Table@Column の name属性はキャメルケース(camelCase)ではなく、スネークケース(snake_case)で書きましょう。
もしスネークケースで書く場合は、application.propertiesに以下の1行を追加する必要があります。

application.properties
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

この設定がないと、キャメルケースでカラム名やテーブル名を明示しても、裏で勝手にスネークケースに変換されるてしまう為、エラーの原因となります。

Repositoryクラスの作成

JpaRepositoryというインターフェイスを作成します。
これまでのクラス作成と違い、拡張インターフェイスの指定があるので注意しましょう。
配置先:src > main > java > com > example > demo > repository > CompanyRepository.java

CompanyRepository
package com.example.demo.repository;

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

import com.example.demo.entity.Company;

public interface CompanyRepository extends JpaRepository<Company, Long> {

}

JpaRepositoryは型とID型を指定する必要があります。
T は型なのでCompanyクラスを、ID はLongを指定します。

Serviceクラスの作成

ビジネスロジックを実装します。このクラスは、コントローラーから呼び出されデータを処理します。

ここでは、コントローラーから渡ってきたFormのデータ(ブラウザからユーザーが入力した企業情報)をデータベースへ登録する為に、FormとEntity間のデータの変換を行いRepositoryクラスを呼び出します。

FormとEntity間のデータ変換の方法はいくつかありますが、今回は手動でマッピングします。明示的な変換ロジックをサービス層で記述します。

配置先:src > main > java > com > example > demo > service > CompanyService.java

CompanyService
package com.example.demo.service;

import java.text.ParseException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.controller.form.CompanyForm;
import com.example.demo.entity.Company;
import com.example.demo.repository.CompanyRepository;

import jakarta.transaction.Transactional;

@Service
public class CompanyService {
	
	@Autowired
	CompanyRepository repository;
	@Autowired
	FormatService format;
	
	// 企業情報の新規登録
    @Transactional
	public Company createCompany(CompanyForm form) {
		Company entity = new Company();
		entity.setName(form.getCompanyName());
		entity.setEmployees(Integer.parseInt(form.getEmployees()));			
		try {
			entity.setEstablishmentDate(format.strToDate(form.getEst()));
		} catch (ParseException e) {
			System.err.println("Failed to convert format to date:" + e.getMessage());
			e.printStackTrace();
		}		
		return repository.save(entity);
	}

}

@￰AutoWired

これを付与することで、DIコンテナから対象のオブジェクトを取り出します。

DIコンテナには、@￰Component や @￰Controller、@￰Service、@￰Repositoryなどが付与されたクラスのインスタンスが登録され、コンテナ内で自動生成されます。簡単に言うと、使いたいクラスをインスタンス化をしてくれるアノテーションです。

ここではCompanyRepositoryクラスをrepositoryというインスタンス名で、FormatServiceクラスをformatというインスタンス名で使えるように指定しています。

@￰Transactional

@Transactionalを使用すると、メソッドが呼び出されると同時にトランザクションが開始され、メソッドの実行が成功した場合にはコミットされ、例外が発生した場合にはロールバックされます。これにより、データの整合性が保たれます。

repository.save(entity);

Form から Entity へデータの変換が出来たら、repositoryがデフォルトで持つsave()メソッドを利用して、EntityのデータをデータベースにINSERTします。save()はentityオブジェクトを引数にとり,それをDBに永続化します。

FormatService

FormとEntity間でデータを変換する際、今回で言うと設立日のようなFormとEntityで型が違うデータの場合には少し工夫が必要です。

ここでは FormatService というデータ型を変換する為のサービスクラスを作成し、文字列型 → 日付型 へ変換するロジックを定義します。

配置先:src > main > java > com > example > demo > service > FormatService.java

FormatService.java
package com.example.demo.service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.stereotype.Service;

@Service
public class FormatService {

	// String → Date
	public Date strToDate(String strDate) throws ParseException {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date date = dateFormat.parse(strDate);
		return date;
	}
}

Viewの作成

thanks.htmlを作成します。
配置先:src > main > resources > templates > thanks.html

thanks.html
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>企業情報登録完了画面</title>
</head>
<body>
	<main>
		<h1>登録完了</h1>
		<p>企業情報のご登録が完了いたしました</p>
	</main>
</body>
</html>

Controllerクラスの修正

CompanyController.javaに登録処理のメソッドを追加します。
配置先:src > main > java > com > example > demo > controller > CompanyController.java

CompanyController.java
@Controller
public class CompanyController {
	
	@Autowired
	CompanyService companyService;

	// 登録フォームの表示
	
	// 企業情報の新規登録
	@PostMapping("company/register")
	public String addCompany(@ModelAttribute CompanyForm companyForm, Model model) {
		companyService.createCompany(companyForm);
		return "thanks";
	}

}

@￰PostMapping(path)

POSTリクエストのみをマッピングします。
@￰PostMappingアノテーションをつけることで、"/company/register"というURLにマッピングしています。

@￰ModelAttribute

Spring MVCにおいて、HTTPリクエストから受け取ったデータをモデルオブジェクトにバインドするためのアノテーションです。このアノテーションを使用することで、コントローラーのメソッドに引数として渡されたオブジェクトに、リクエストパラメータを自動的にマッピングできます。

今回は、クライアントからPOST送信されたデータをまとめてCompanyFormオブジェクトとして受け取っています。

companyService.createCompany(フォームオブジェクト);

CompanyServiceクラスのcreateCompanyメソッドを呼び出しています。引数にはクライアントから渡ってきたcompanyFormオブジェクトを渡しています。

データの流れ

登録処理のデータの流れは以下の通りです。

  1. ユーザーが入力フォームに企業情報を入力して送信
  2. CompanyControllerがフォームデータを受け取る
  3. CompanyServiceに登録処理を依頼
  4. CompanyServiceが必要な業務処理を行い(FormとEntity間のデータ変換)、CompanyRepositoryに保存を指示
  5. CompanyRepositoryがデータベースに保存
  6. 結果を順次上の層に返し、最終的にユーザーに結果を表示

次回はバリデーションチェックを実装していきます。

参考サイト

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?