Webアプリケーション入門
目次
4.3 (手順3)初期プロジェクトをEclipseにインポート
5.2 MVC(Model View Controller)
7.3.2 GitHubでリポジトリ mainbank を作成(任意:必要な方のみ)
13.2.1 usernameとpassword の確認方法と設定方法
13.3 application.propertiesの設定
13.5 spring.jpa.hibernate.ddl-autoの設定
14.1 サーバサイドレンダリング(Server-Side Rendering, SSR)
14.2 クライアントサイドレンダリング(Client-Side Rendering, CSR)
14.3 静的サイト生成(Static Site Generation, SSG)
Webアプリケーション(Web Application、Web App)とは、インターネットを介して、Webブラウザ上で動作するアプリケーションのことです。
例 マイナポータル(行政手続のオンライン窓口)
マイナポータルなど、さまざまなWebアプリケーションが存在しますが、それらがゼロから全て開発されているわけではありません。通常、フレームワークと呼ばれるソフトウェアを基盤として開発が行われます。Webアプリケーション用フレームワークとは、多くのWebアプリケーションで共通して利用できる構造や基本的な機能を提供する枠組みです。
フレームワークには、PHP用、Ruby用、そしてJava用のものがあり、Javaでは特にSpringが広く使われています。なお、同じプログラミング言語でも複数のフレームワークが存在します。多くのフレームワークは、オープンソースソフトウェアとして無料で利用することが可能です。
フレームワークを使うことで、開発者は基礎的な部分を一から作る手間を省き、アプリケーションの本質的な機能の開発に集中することができます。
フレームワークを使用すると次のような利点があります。
フレームワークには、よく使われる機能やパターン(ユーザー認証、データベース接続、ページ遷移など)が組み込まれています。それを利用することで、毎回ゼロからコードを書く必要がありません。
フレームワークは、アプリケーションの構造や作り方にある程度のルールを提供します。このため、どの開発者も一貫した方法でコードを書くことができ、チームでの開発が容易になります。
フレームワークには、セキュリティやパフォーマンスに関する考慮がされており、これを利用することでセキュアで効率的なWebアプリケーションを作ることができます。
Webアプリケーションを作成する際には、何らかのフレームワークを使用することが一般的です。フレームワークは、使用するプログラミング言語によって選択肢が決まってきます。
Java言語用としては、本書で紹介する Spring の他にも Apache Struts などがあります。
Python 用の場合は、Django や Flask、PHP 用の場合は、LaravelやCakePHP、Ruby 用の場合は、Ruby on Rails、C#の場合は、ASP.NET Core Blazor などがあります。
Spring Boot は Spring フレームワークの一部分(サブセット)です。Spring Bootについて説明する前に、Springについて説明します。
Spring framework(以降 Spring と略称します)は、Javaでエンタープライズアプリケーションを開発するためのフレームワークです。主な特徴は、依存性注入(DI)やアスペクト指向プログラミング(AOP)などを使って、柔軟でスケーラブルなアプリケーションを構築しやすくする点です。Springを使うと、ビジネスロジックに集中しやすくなります。
一方で、Springは多くの機能を有するフレームワークであるため、どの機能を選択し使用すればよいのか戸惑うこともありました。そこでこの問題を解消するために登場したのが Spring Boot です。
Spring Bootは、Springをより簡単に使えるようにしたフレームワークです。自動設定機能や組み込みWebサーバー(Tomcatなど)が提供されているため、設定が少なく、すぐにアプリケーションを起動できるのが特徴です。スムーズにSpringのプロジェクトを始められる点がSpring Bootの大きなメリットです。
Spring フレームワークは、主に Java をサポートしていますが、他の複数のプログラミング言語も利用可能です。
以下の言語が Spring フレームワークおよび Spring Boot でサポートされています:
Spring は元々 Java のために設計されたフレームワークであり、Java が主な言語です。
Spring フレームワークおよび Spring Boot では、Kotlin も公式にサポートされています。
Groovy は、動的言語でありながら JVM 上で動作するスクリプト言語です。
これらの言語はすべて Spring Boot でも同様にサポートされており、Java プロジェクトと同じように、Kotlin や Groovy で Spring Boot アプリケーションを構築することができます。
例えば、Spring Initializr(後述)を使って、Kotlin や Groovy ベースの Spring Boot プロジェクトを作成することも可能です。
本書では、統合開発環境(IDE)として「Pleiades All in One」(以降Eclipseと記載)を使用します。
(参考)
もし、Java言語やIDEに関する情報が必要でしたら
「Javaプログラミング入門」の「1. 学習環境を準備します」を参考ください。
http://www.fk-nextdesign.sakura.ne.jp/learn/Roadmap.html
本書ではSpringの初期プロジェクト(IDEにインポート可能な空のSpringアプリケーションプロジェクト)を作成するために、「Spring Initialzr」を利用します。
Spring Bootは、Springを使いやすくしましたが、それでも、最初に Spring Boot用のJavaプロジェクトを構成するときはボタンひとつでは済みません。そこでWeb画面から構成できるようにしたのが、Spring Initialzrです。この初期プロジェクトをEclipseにインポートして、独自のプログラムを追加・編集することで、自前のWebアプリケーションとして作成します。具体的な操作は後述します。
Eclipseを使ってJava Spring Webアプリケーションを作成するためには、まずEclipse上に新しいプロジェクトを作成します。方法はいくつかありますが、本書では次の手順で作成します。
本サンプルでは Maven を選択しますが、他でも問題ありません。
Dependencies(依存性)は次の3つを選択します。
右上の「ADD …」ボタンを押下してDependencyを選択し追加します。
上の構成は、Thyemleafを使う場合の最小構成です。
もし、永続化(データベース)を使用するような場合は、次の依存性が必要です。
この依存性の構成は一例です。例えば、MySQLを使用したい場合は別の構成になります。
適切なフォルダに保存し、展開します。
このフォルダはEclipseプロジェクトの保管場所になります。
ダウンロードし展開後の内容
C:.
│ .gitignore
│ HELP.md
│ mvnw
│ mvnw.cmd
│ pom.xml
│
├─.mvn
│ └─wrapper
│ maven-wrapper.properties
│
└─src
├─main
│ ├─java
│ │ └─com
│ │ └─example
│ │ └─hello
│ │ HelloApplication.java
│ │
│ └─resources
│ │ application.properties
│ │
│ ├─static
│ └─templates
└─test
└─java
└─com
└─example
└─hello
HelloApplicationTests.java
Eclipseで、ファイル → インポート → 既存 Maven プロジェクト →
完了
初期プロジェクトのままではHelloWorldページは表示されませんので、次の4つのファイルを追加します。
HelloControllerjava
HelloWorld.html
error.html
index.html
追加する場所は次図の通りです。
package com.example.hello;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController {
@GetMapping("hello")
public ModelAndView hello(ModelAndView mav) {
mav.setViewName("HelloWorld");
return mav;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>HelloWorld !!</h1>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>エラーページ</h1>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>静的index.htmlページ</h1>
</body>
</html>
Eclipseのパッケージエクスプローラまたはプロジェクトエクスプローラで hello を右クリックします。
次図のように
Eclipseのコンソールに次のようなログが表示され、Webアプリケーションが起動したことを確認できます。
ブラウザを開いて次のURLにアクセスします。
http://localhost:8080/hello999
いくつか方法があります。一つは次図の赤いボタン押下で停止します。
前章の「簡単なWebアプリケーション」(Spring + Thymeleaf) では,最小限のリクエストとレスポンスを確認できました。しかし、ここから先を、自己流だけで機能を追加していっても、実用的な品質のアプリケーションを作成することは困難でしょう。通常は、すでに多くの開発者によって使用されている設計手法や構造パターンを参考にして設計や構造を決めていきます。
次の一文は、エリックエヴァンスのドメイン駆動設計 日本語版第4章から引用したものです。
~引用ここから~
輸送アプリケーションで、貨物の荷出し地を都市の一覧から選択するという、ユーザの簡単な操作をサポートしたい。そのためには、次の処理を実行するプログラムコードがなければならない。①スクリーンにウィジェットを描き、②選択できる都市をすべてデータベースに問い合わせて、③ユーザーの入力を解釈して検証し、④選択された都市を貨物と結び付け、⑤データベースへの変更をコミットする。このコードすべてが同じプログラムの一部になるが、輸送業というビジネスに関係するのは、そのごく一部にすぎない。
ソフトウェアプログラムには、さまざまな種類のタスクを実行する設計とコードがある。
ユーザ入力を受つけ、ビジネスロジックを実行し、データベースにアクセスして、ネットワークを通じて通信し、ユーザに情報を表示するといった具合である。したがって、各プログラム機能に含まれるコードは、かなりの量になる可能性がある。
~引用ここまで~
単純なプログラムですが、十分な分析や設計をしないで、HelloWorldの延長のようなつもりで作成すると複雑で混沌としたWebアプリケーションになるでしょう。当然、拡張性や保守性の低いシステムとなります。
そのためにはアプリケーションアーキテクチャというものを理解し適切に使用していく必要があります。
以下の用語は、すべてWebアプリケーションの設計や構造に関するものであり、互いに関連しています。それぞれが異なる視点からアプリケーションの構造を示していますが、目的は一貫して、コードの可読性や保守性、拡張性を高めることです。
アプリケーションアーキテクチャとは、ソフトウェア全体の構造を定義し、どのように機能が分割されているか、各コンポーネントがどのように相互作用するかを示します。Webアプリケーションでは、複雑な要件を効率よく満たすために、設計時に適切なアーキテクチャが選ばれます。以下に説明するMVC、レイヤードアーキテクチャ、クリーンアーキテクチャはすべて、異なる種類のアプリケーションアーキテクチャです。
MVCは、Webアプリケーションの構造を「Model(データの管理)」「View(ユーザーインターフェース)」「Controller(ロジックと指令)」の3つに分ける設計パターンです。
レイヤードアーキテクチャは、アプリケーションを層(レイヤー)ごとに分割する設計です。一般的には、以下のような層に分けられます:
このように層を分けることで、責任の分離が明確になり、保守や拡張がしやすくなります。
クリーンアーキテクチャは、アプリケーションを柔軟かつ保守しやすくすることを目指した設計思想です。ソフトウェアの依存関係が一方向(内側から外側)になるように設計します。基本的な構造は以下の通りです:
クリーンアーキテクチャの特徴は、外部依存(データベース、UI、フレームワークなど)からビジネスロジックを分離することにより、変更に強い設計を実現する点です。
重要な関連概念
これらの概念は、Webアプリケーション開発におけるコードの可読性、テストのしやすさ、保守性を向上させるために非常に役立ちます。
本章の内容は例です。
層毎に役割を明確に分けます。トランザクション・スクリプトと呼ばれるアンチパターンとならないように、そして、低品質で保守や拡張が困難な混沌としたソフトウェアとならないように、適切に役割を分けます。
各層の名前や構成はチームで決めていきます。必要な場合はリファクタリングします。
名前 |
役割 |
プレゼンテーション層 |
アクター(人間や別のコンピュータ)と情報を相互通信します。 |
アプリケーション層 |
ユースケ―ス、シナリオを実行します。 |
ドメイン層 |
ドメイン駆動設計などでモデリングしたクラス群によってドメインロジックを提供します。 |
インフラストラクチャ層 |
上位のレイヤを支える一般的な技術機能(永続化機能やデータ送受信機能など)を提供します。 |
├─java
│ └─com
│ └─example
│ └─mainbank
│ ├─presen (プレゼンテーション層(コントローラー、ビュー))
│ ├─appl (アプリケーション層(ユースケース、サービス))
│ ├─config
│ ├─domain
│ └─infra (infrastructure)
└─resources
├─static
│ └─css
└─templates
クリーンアーキテクチャでは、依存関係の方向を意識し、ビジネスロジックを中心に据えるような構成が推奨されます。
├─java
│ └─com
│ └─example
│ └─mainbank
│ ├─config
│ ├─domain
│ │ ├─model
│ │ └─repository (repository interface)
│ ├─application
│ │ └─usecase (business logic)
│ ├─infra (implementation of repository, external services, etc.)
│ ├─adapter (controllers, gateways, and presenters)
│ └─shared (common utilities)
└─resources
├─static
└─templates
自社が取引する銀行の情報を管理する。
【銀行の種類】
・都市銀行
・地方銀行
・ネット銀行
【取引の種類】
・融資 (Loan)
・預金 (Deposit)
・送金
1 |
ユースケース名 |
取引銀行を登録する。 |
|
アクター |
経理部員。 |
|
事前条件 |
登録する取引銀行は未登録であること。 |
|
事後条件 |
エラーなしの場合は、登録済みとなること。 エラーありの場合は、未登録のままであること。 |
|
主シナリオ |
1. ブラウザから所定のURLにアクセスし、取引銀行一覧画面を開く。 2. 新規登録ボタンを押下して新規登録画面を開く。 3. 取引銀行名、銀行種類、取引種類を入力する。 4. 保存ボタンを押下する。 |
|
副シナリオ |
保存ボタン押下でエラーメッセージが表示された場合 |
|
|
エラーを修正し、再度保存ボタンを押下する。 または、キャンセルボタン押下で終了する。 |
サンプルアプリ作成時のInitializrの設定内容とGitHubで管理する手順について説明します。
これはGitHubでソースコードを管理したい場合のみ必要です。後からGitHub管理もできます。
Codeボタンから「Open with GitHub Desktop」を選択すると自分のPCにインストール済みのGitHub Desktopが起動するので、GitHub Desktop を操作して、リポジトリをクローンします。
GitHub Desktop画面が開きます
Clone すると Local Path のフォルダが自動生成されます。(そのつもりで指定してください)
次のような結果になります。
カレントブランチは、デフォルトのmainブランチになっています。
この例では、main ブランチから develop ブランチを作成し、develop をカレントブランチにしました。
ローカルフォルダに初期プロジェクトを解凍した後の mainbank を移動(コピー&貼付け)します。
この辺の手順は色々なやり方あると思います。
GitHub Desktopの状態
Commitコメントを入力 → Commit to develop → Push origin → Create Pull Request
→ ブラウザでGithubページが開く
下の方にスクロールして Create pull request を押下します
少し下にスクロール
Merge pull requestを押下
Confirm mergeを押下
Mergedが表示されたらマージ成功です。
Eclipseにインポートします。
Eclipse ファイルメニュー → インポート → 既存Mavenプロジェクト → ルートディレクトリとして mainbank フォルダを選択 → 完了。
このインポートは mainbank を解凍し配置した後でも問題ありません。
本書ではSpring Boot JPA + MySQLを使用します。MySQLについては後述します。また、取引銀行管理に登場する一部のドメインモデル名を使いますが、本セクションの目的は、継承関連を持つエンティティ(クラス)の永続化戦略です。そのため、エンティティのプロパティや振舞いはほぼ省略しています。
(クラス図)
(Javaコード)
package com.example.mainbank.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
/**
* エンティの基底クラス
*/
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity {
@Id
//@GeneratedValue(strategy=GenerationType.IDENTITY)
@GeneratedValue(strategy=GenerationType.TABLE)
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
package com.example.mainbank.domain;
import jakarta.persistence.Entity;
/**
* 取引銀行
*/
@Entity
public class MainBank extends BaseEntity {
private String name;
//コンストラクタ
public MainBank() {
this.name = "";
}
}
(MySQLデータベーステーブルの状態)
(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>mainbank</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mainbank</name>
<description>Main Bank Management</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ここまで、クラス毎にテーブルを作成する戦略ですです。
ただし、@GeneratedValue(strategy = GenerationType.IDENTITY)と指定するとエラーになります。
原因は、InheritanceType.TABLE_PER_CLASS と GenerationType.IDENTITY の組み合わせにあります。
正しい指定方法は、@GeneratedValue(strategy = GenerationType.TABLE)です。
このように指定することで、Hibernate がテーブル (hibernate_sequences) を作成し、そこから ID を生成するので、TABLE_PER_CLASS と互換性があり、3つのテーブルが正しく作成されます。
この戦略は適切な解決策と言えます。ただし、ID テーブルが追加で作成されることに注意が必要です。
この他の戦略としては、
InheritanceType.JOINED や InheritanceType.SINGLE_TABLE があります。この戦略も検討可能です。
PostgreSQL の場合は、
PostgreSQL は IDENTITY 戦略と TABLE_PER_CLASS の組み合わせが MySQL と同様に問題を引き起こす可能性があります。
しかし、PostgreSQL のシーケンスは MySQL より柔軟なので、次の戦略を推奨します。
GenerationType.SEQUENCE:PostgreSQL はシーケンスに適しており、Hibernate
がシーケンスを自動で管理します。この場合、データベース側での追加のシーケンス作成が不要となり、効率的です。
例:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_sequence")
@SequenceGenerator(name = "your_sequence", sequenceName = "your_sequence_name", allocationSize = 1)
まとめ
MySQL では、GenerationType.TABLE と TABLE_PER_CLASS の組み合わせが適切です。
PostgreSQL では、GenerationType.SEQUENCE の使用をお勧めします。
依存性注入 (DI : Dependency Injection)は、Springフレームワークの中心的な機能で、オブジェクト同士の依存関係を効率的に管理する方法です。オブジェクトが他のオブジェクトに依存する際、その依存関係を外部から「注入」することで、コードを柔軟にします。
通常、オブジェクトが別のオブジェクトを必要とする場合、そのオブジェクトを自分で作成します。しかし、これでは依存関係が強くなり、コードの再利用やテストが難しくなります。DIを使うと、必要な依存オブジェクトを外部(コンテナ)から提供されるため、オブジェクト間の結びつきが緩やかになります。
例えば、以下のようなクラスがあるとします。
public class Service {
private Repository repository;
public Service(Repository repository) {
this.repository = repository;
}
public void performTask() {
repository.save();
}
}
ServiceクラスはRepositoryクラスに依存しています。通常はRepositoryのインスタンスをServiceクラス内で生成しますが、SpringではServiceのコンストラクタにRepositoryを渡すことで依存性を外部から注入します。
例
this.repository = new ConRepository();
とすると、Serviceクラスは、Repositoryインタフェースを実装した具象クラスConRepositoryと強く結びついてしまいます。
Springでは、DIを使ってオブジェクトの依存関係を設定できます。主な注入方法は3つあります。
コンストラクタを通じて依存オブジェクトを注入する方法です。上記の例がこれに該当します。
セッターメソッドを使って依存オブジェクトを注入します。
public class Service {
private Repository repository;
public void setRepository(Repository repository) {
this.repository = repository;
}
}
フィールドに直接注入する方法で、@Autowiredアノテーションを使います。
public class Service {
@Autowired
private Repository repository;
}
依存性注入は、より柔軟で保守性の高いアプリケーションを作るための非常に重要な概念です。Springフレームワークはこの仕組みを自動的に管理してくれます。
「依存性」とは、何かが他の何かに頼って動く関係のことです。例えば、車がガソリンに「依存」しているとしましょう。車はガソリンがないと動かないので、ガソリンに頼っている、つまり依存しているというわけです。
ソフトウェアでも、あるプログラム(車)が他のプログラム(ガソリン)を必要として動作する場合、その2つの間に「依存性」があるといいます。
「注入」とは、必要なものを外から与えることです。車の例でいうと、車がガソリンスタンドで給油されるように、外からガソリンを「注入」されることです。つまり、車は自分でガソリンを作るのではなく、誰かがガソリンを入れてあげている状態です。
ソフトウェアの場合も同様で、必要なプログラム(例えば車でいうガソリン)を別のプログラムに「注入」して、うまく動くようにします。
依存性は、「あるものが他のものに頼っている関係」です。例えば、車がガソリンに頼って動いている状態。
注入は、「必要なものを外から与えること」です。車が自分でガソリンを用意するのではなく、外からガソリンを入れてもらうイメージです。
このように考えると、依存性と注入の関係は、何かを動かすために必要なものを外から提供してもらう仕組みだと言えます。
短い説明:Springによる依存性注入を行うアノテーション。
記述場所:フィールド、コンストラクタ、またはメソッド。
目的: Springが管理する任意のBean(オブジェクト)を注入するために使われます。
利用範囲: SpringのDIコンテナで管理されている全てのオブジェクトに適用できます。例えば、サービスクラスやリポジトリクラスなどに依存するオブジェクトを注入する際に使います。
例
@Autowired
private MyService myService;
@Autowiredを使うことで、MyServiceクラスのインスタンスが自動的に注入され、myServiceというフィールドに格納されます。
補足: @Autowiredは、デフォルトでby type(型に基づいて)自動的に適切なBeanを見つけて注入します。必要に応じて@Qualifierを使い、特定のBeanを選択することも可能です。
短い説明:Springコンテナに管理されるBeanを定義するアノテーション。
記述場所:メソッド。
短い説明:Springコンテナに管理される一般的なBeanを定義するアノテーション。
記述場所:クラス。
短い説明:Springの設定クラスを示すアノテーション。
記述場所:クラス。
短い説明:Webリクエストを処理するコントローラを定義するアノテーション。
記述場所:クラス。
短い説明:REST APIのエンドポイントを定義するコントローラ用のアノテーション
(@Controller + @ResponseBody)。
記述場所:クラス。
短い説明:メソッドの戻り値をHTTPレスポンスのボディに直接変換するアノテーション。
記述場所:メソッド。
短い説明:URLパスとリクエストハンドラをマッピングするアノテーション。
記述場所:クラスまたはメソッド。
短い説明:HTTP GETリクエストを処理するメソッドをマッピングするアノテーション。
記述場所:メソッド。
短い説明:HTTP POSTリクエストを処理するメソッドをマッピングするアノテーション。
記述場所:メソッド。
短い説明:HTTPリクエストのパラメータをメソッドの引数にバインドするアノテーション。
記述場所:メソッド引数。
短い説明:モデルデータをリクエストと共にバインドするアノテーション。
記述場所:メソッド、またはメソッド引数。
短い説明:全体的な例外処理やモデルデータの共通処理を提供するアノテーション。
記述場所:クラス。
短い説明:データアクセス層(DAO)のBeanを定義するアノテーション。
記述場所:クラス。
短い説明:JPAエンティティクラスを示すアノテーション。
記述場所:クラス。
短い説明:エンティティがマッピングされるデータベーステーブルを指定するアノテーション。
記述場所:クラス。
短い説明:エンティティの主キーを指定するアノテーション。
記述場所:フィールドまたはメソッド。
短い説明:Lombokによる自動的なゲッター、セッター、toString等の生成アノテーション。
記述場所:クラス。
短い説明:ビジネスロジックを持つサービスクラスを定義するアノテーション。
記述場所:クラス。
短い説明:メソッドやクラスに対してトランザクションの境界を定義するアノテーション。
記述場所:クラスまたはメソッド。
短い説明:Spring Bootアプリケーションのエントリポイントを定義するアノテーション。
記述場所:クラス。
短い説明:スケジューリング機能を有効にするアノテーション。
記述場所:クラス。
@Autowiredと@PersistenceContextはどちらも依存性注入を行うためのアノテーションですが、注入する対象や役割が異なります。
目的: 特定のEntityManagerを注入するために使われます。EntityManagerは、データベース操作を行う際に必要なJPA(Java Persistence API)のインターフェースです。
利用範囲: 主にデータベースにアクセスするためのJPA(Java Persistence API)関連クラスで使用されます。@PersistenceContextを使うことで、Springが管理するEntityManagerを注入し、データベースとのやり取りを実現します。
例:
@PersistenceContext
private EntityManager entityManager;
@PersistenceContextを使うと、EntityManagerのインスタンスが自動的に注入され、entityManagerというフィールドで利用できるようになります。
補足: EntityManagerは、エンティティ(データベーステーブルの行に対応するオブジェクト)を操作するために使用されます。例えば、エンティティの保存、更新、削除、クエリ実行などを行うために重要です。
注入対象:
@PersistenceContextはEntityManagerの注入に特化しています。
用途:
@PersistenceContextは、JPAを使ったデータベース操作でのみ使います。
補足:
@PersistenceContextの代わりに@Autowiredを使うと、次のような問題が発生する可能性があります。
@PersistenceContextは、JPAのEntityManagerを注入する際、トランザクションスコープで管理されるため、トランザクション開始や終了のタイミングに応じて自動的に正しいEntityManagerが提供されます。これにより、エンティティの状態が一貫性を保ち、トランザクションの境界内で適切に動作します。
一方、@AutowiredはSpringの通常のBeanを注入するためのものです。もし@AutowiredでEntityManagerを注入しようとすると、Springがトランザクションの境界を適切に管理できず、複数のトランザクション間で問題が発生する可能性があります。例えば、トランザクション内でEntityManagerが正しく解放されない、または使い回されるといった不具合が起きます。
@PersistenceContextを使うと、トランザクションスコープでEntityManagerが注入されるため、マルチスレッド環境でもスレッドごとに適切に分離されたEntityManagerが使用されます。
@Autowiredを使った場合、EntityManagerがシングルトンとして扱われる可能性があり、スレッド間で共有されるとスレッドセーフでなくなることがあります。これにより、同時実行中の複数のトランザクションで不整合が発生する可能性があります。
そのため、EntityManagerの注入には@PersistenceContextを使うべきで、@Autowiredではトランザクション管理やスレッドセーフ性の問題が起きる可能性があります。
目的: Javaの依存性注入を行うための標準アノテーションで、主に名前による依存性注入に使われます。
特徴: Springの@Autowiredが型に基づいて注入するのに対し、@Resourceは名前に基づいて依存性を解決します。
使用例:
@Resource(name="myBean")
private MyBean myBean;
目的: Javaの標準依存性注入アノテーション(JSR-330)。Springの@Autowiredに非常に似ていますが、標準のDIアノテーションです。
特徴: @Autowiredとほぼ同じ動作をしますが、Springに特化していないため、他のDIフレームワークでも使えることが特徴です。
使用例:
@Inject
private MyService myService;
目的: Java EE(Jakarta EE)の環境で使われるアノテーションで、**Enterprise Java Beans(EJB)**を注入するために使用します。
特徴: EJBを使用する際、@Autowiredではなく@EJBを使ってEJBコンポーネントを注入します。
使用例:
@EJB
private MyEJBService ejbService;
目的: プロパティファイルなどから値を注入する際に使用します。
特徴: 値を直接コード内に埋め込むのではなく、設定ファイルから動的に読み込む際に使います。
使用例:
@Value("${app.name}")
private String appName;
目的: メソッドやクラスに対してトランザクション境界を定義するためのアノテーションです。
特徴: トランザクション管理を明示的に行う必要がある場合、@Transactionalを使用してトランザクションの開始・終了を自動的に管理します。
使用例:
@Transactional
public void saveData() {
// データ保存処理
}
次のアノテーションを付けたクラスがインスタンス化の対象です。
@Component
@Controller
@Service
@Repository
@RestController
@ControllAdvice
ManagedBean
@Named
これらのアノテーションがついたクラスはコードのなかでnew()でインスタンス化することはなく、DIの仕組みによって呼び出し元クラスへ注入される
それが@Autowiredアノテーションを目印にして行われる。全般的な範囲の広いアノテーション
Bean Validationは対象クラスにチェックに応じたアノテーションを付与して使用します。
以下は入力値やロジックによるチェック時によく使用されるバリデーション用アノテーションです。
@Validated
@AssertTrue
@AssertFalse
@Null
@NotNull
@NotBlank
@Max(value)
@Min(value)
@DecimalMax(value, inclusive)
@DecimalMin(value, inclusive)
@Positive
@PositiveOrZero
@Negative
@NegativeOrZero
@Digits(integer, fraction)
@DateTimeFormat(Date format)
@Size(max or min=value)
@Pattern(regex, flags)
このアノテーションは、リクエストパラメータを引数として受け取る際に使用します。
主にフォームの入力やURLクエリパラメータを取得するのに使われます。
例えば、/greet?name=John のようなリクエストで「name」というパラメータを受け取る場合、@RequestParam を使います。
@GetMapping("/greet")
public String greet(@RequestParam("name") String name, Model model) {
model.addAttribute("name", name);
return "greet";
}
このアノテーションは、フォームデータやリクエストボディのデータをオブジェクトにバインドするために使用します。主にフォームのデータを複数のフィールドにまとめて受け取る場合に使われます。例えば、ユーザーオブジェクト全体をフォームから取得する場合に便利です。
@PostMapping("/register")
public String register(@ModelAttribute User user, Model model) {
model.addAttribute("user", user);
return "register";
}
@RequestParamは個別のパラメータを、@ModelAttributeはオブジェクト全体を受け取るといった役割で使用されます。
他の方法
このアノテーションは、リクエストボディ全体を直接Javaオブジェクトに変換して受け取るのに使用します。
特に、JSON形式のデータを受け取る際に役立ちます。REST APIでよく使います。
@PostMapping("/api/data")
public ResponseEntity<String> postData(@RequestBody Data data) {
// JSONをオブジェクトにバインド
return ResponseEntity.ok("Success");
}
パスの一部を引数として受け取る際に使います。
@GetMapping("/user/{id}")
public String getUser(@PathVariable("id") Long id, Model model) {
model.addAttribute("userId", id);
return "user";
}
Spring Bootでは、フォームクラスに対応しない入力フィールドの値を受け取るためにさまざまな方法を提供しています。
たとえば、@RequestParamアノテーションを使用して直接リクエストパラメータを取得できます。
また、@ModelAttributeアノテーションを使用してフォームクラス以外のオブジェクトにデータをバインドできます。
要するに、Spring Bootではフォームクラスを使用してフォームデータを受け取ることが一般的ですが、他の方法を使ってフォームクラスに定義されていない入力フィールドの値を受け取ることも可能です。
ControllerからViewにデータを渡す際に、Modelを使ってデータを追加します。
これは最も一般的な方法です。
@GetMapping("/show")
public String show(Model model) {
model.addAttribute("message", "Hello World");
return "show";
}
ModelAndViewは、モデルとビューを同時に返すことができるクラスです。
ビュー名とモデルを一緒に扱う場合に便利です。
@GetMapping("/show")
public ModelAndView show() {
ModelAndView mav = new ModelAndView("show");
mav.addObject("message", "Hello World");
return mav;
}
リダイレクト時に一時的にデータを渡すために使用します。
RedirectAttributesは一度だけ使用される一時的なデータを渡すために便利です。
@PostMapping("/submit")
public String submit(RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", "Data submitted successfully");
return "redirect:/result";
}
これらの方法を組み合わせて、用途に応じたデータの受け渡しが可能です。
Spring AOP (Aspect-Oriented Programming) は、横断的な関心事(cross-cutting concerns)をモジュール化するためのプログラミング手法です。横断的な関心事とは、アプリケーション全体にまたがる共通の機能のことを指し、ログ出力やトランザクション管理、セキュリティなどが典型例です。
AOP の基本的な考え方は、これらの横断的な機能をアプリケーションの本来のビジネスロジックとは分離し、個別に管理できるようにすることです。これにより、コードの重複を減らし、保守性や可読性を向上させます。
Spring AOP では、以下の5つの要素が基本的な構成要素です:
横断的な機能そのものです。たとえば、ログ出力や例外処理をひとつのアスペクトとして実装します。
アスペクトがいつ実行されるかを定義します。メソッドの実行前、実行後、例外発生時など、どのタイミングで横断的な処理を行うかを指定します。
アスペクトが適用される具体的なポイントです。Spring では、主にメソッド呼び出しがジョインポイントとなります。
ジョインポイントをフィルタリングし、どのメソッドにアドバイスを適用するかを定義します。正規表現やアノテーションを使って、対象となるメソッドを柔軟に指定できます。
アスペクトを適用されるオブジェクト、つまりビジネスロジックを持つクラスです。
たとえば、特定のメソッドが実行される前にログを出力するアスペクトを作成する場合、以下の流れとなります:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Executing method: " + joinPoint.getSignature());
}
}
このコードでは、com.example.service パッケージ内のすべてのメソッドが実行される前に、メソッド名をログに出力します。
Spring AOP を使用すると、ビジネスロジックに直接関与しないコードを分離し、関心ごとに応じた処理を柔軟に追加できるようになります。
状態名 |
概要 |
NEW状態 |
エンティティオブジェクトを生成(new)しただけでの状態です。永続化コンテキストとは関連付けられていません。 |
MANAGED状態 |
エンティティオブジェクトは永続化コンテキストと関連付けられた状態です。EntityManager#findで検索可能です。 |
DETACHED状態 |
エンティティオブジェクトは永続化コンテキストと関連付けられていません。 MANAGED状態からDETACHED状態への遷移は EntityManager#clear または EntityManager#close で操作できます。 もう一つ、トランザクションに関係した挙動でも遷移します(後述:merge 参照) |
REMOVED状態 |
削除された状態です。EntityManager#persist で再びMANAGED状態に遷移できます。 |
<T> T merge(T entity)
Merge the state of the given entity into the current persistence context.
Parameters:
entity - entity instance
Returns:
the managed instance that the state was merged to
Throws:
IllegalArgumentException - if instance is not an entity or is a removed entity
TransactionRequiredException - if there is no transaction when invoked on a container-managed entity manager of that is of type PersistenceContextType.TRANSACTION
MySQL80 とMySQLWorkbench8.0 を前提に説明します。
MySQLでは「データベース」と「スキーマ」を区別しません。本書では同意語として使います。
(作成例)
(作成例)
MySQL Workbench を開き、MySQL インスタンスに接続します。
メインメニューから「Server(サーバー)」 -> 「User and Privileges(ユーザーと権限)」をクリックします。
左側のユーザーリストから確認したいユーザー名(例: mainbank)を選択します。
右側に選択したユーザーの詳細(認証方式、パスワード、権限など)が表示されます。
上記の「User and Privileges」画面で、パスワードを変更したい場合は、「Login(ログイン)」タブで password フィールドを変更し、適用します。
新しいユーザーを追加したい場合は、同じ画面の「Add Account(アカウントを追加)」ボタンを押し、新しいユーザー名とパスワードを設定し、適切な権限を割り当てます。
(設定例)
spring.application.name=mainbank
spring.thymeleaf.mode=HTML
spring.datasource.url=jdbc:mysql://localhost:3306/db_mainbank?serverTimezone=UTC
spring.datasource.username=mainbank
spring.datasource.password=mainbank
# spring.datasource.driver-class-name =com.mysql.jdbc.Driver => 'com.mysql.jdbc.Driver'. This is deprecated.
spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
spring.jpa.database=MYSQL
# create-drop cause Error executing DDL "alter table
#none / validate / update / create / create-drop / drop
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.show-sql: true
spring.security.user.name=test
spring.security.user.password=test
#platform line seems unnecessary
#spring.datasource.platform=mysql
MySQLの場合、データベースとスキーマはほぼ同じ意味で使われます。
Spring Boot アプリケーションにおける Hibernate のデータベーススキーマの自動生成や変更に関する設定です。これには複数のオプションがあり、それぞれ異なる挙動を持っています。
挙動: Hibernate がスキーマ管理を一切行わず、データベースの構造はそのままです。
用途: スキーマの自動生成や変更を避けたい場合。既存のスキーマがあり、手動で管理したいときに使用します。
挙動: エンティティの定義とデータベースのスキーマが一致しているかを検証しますが、データベースに対して何も変更を加えません。
用途: スキーマが正しく設定されているか確認したい場合や、変更を加えずにスキーマの整合性をチェックしたい場合に適しています。
挙動: エンティティの定義に基づいて、必要なテーブルやカラムを作成・更新します。ただし、既存のデータや構造を削除することはありません。
用途: スキーマを自動的に変更したい場合や、既存のデータを維持したまま、変更を反映したい場合に適しています(現状これを使用中ですね)。
挙動: アプリケーション起動時に既存のスキーマをすべて削除し、新しくスキーマを作成します。アプリケーション終了時にはスキーマは削除されません。
用途: 開発環境で新しいスキーマをすぐに作りたい場合に使用されますが、既存データは消えるため注意が必要です。
挙動: アプリケーションの起動時にスキーマを作成し、終了時にスキーマを削除します。
用途: テストや一時的な環境で、スキーマを簡単に作成し、使用後にクリーンアップしたい場合に使用します。
挙動: アプリケーション起動時にスキーマを削除し、その後何もしません。
用途: 一般的にはあまり使用されませんが、起動時に明示的にスキーマを削除したい場合に使用します。
none: スキーマ操作をしない(手動管理)。
validate: スキーマの検証のみ(変更なし)。
update: スキーマの更新(データは保持)。
create: スキーマを再作成(データは削除)。
create-drop: 起動時にスキーマを作成し、終了時に削除。
drop: スキーマを削除。
update は開発時に便利ですが、本番環境では validate や none を使って、スキーマを手動で管理するのが一般的です。
レンダリングとは、Webアプリ開発の文脈では、クライアントサイド・レンダリングやサーバサイド・レンダリングのようにHTMLデータを生成する処理を指します。
また、最終的に、ブラウザがデータ(HTMLなど)を視覚化する処理を指す場合もあります。
JSP(JavaServer Pages)はサーバーサイドレンダリングの一種です。
サーバーサイドレンダリングとは、サーバー側でHTMLを生成してクライアント(ブラウザ)に送信する方法です。
JSPはJavaでサーバーサイドのコードを実行し、その結果をHTMLとして生成します。具体的には、JSPページはJavaコードやJSPタグを含んでおり、サーバー上で実行されると、その出力がHTMLとして生成され、クライアントに送信されます。
JSPのレンダリングプロセスは次のようになります:
このように、JSPはサーバーサイドで実行されるため、サーバーサイドレンダリングの一部とされています。
React, Angular,Vue.js などのJavaScriptフレームワークはクライアントサイドレンダリングを前提として設計されています。これらのフレームワークは、コンポーネントベースのアーキテクチャを使用して、クライアント側で効率的にUIを構築・更新します。
クライアントサイドレンダリングは、一般的にSPAアプリケーションで使用されます。SPAでは、最初に必要なHTML、CSS、JavaScriptをロードし、その後のページ遷移はJavaScriptによって行われ、サーバーから新しいHTMLを取得するのではなく、必要なデータだけをフェッチして、ページを動的に更新します。
ウェブサイトのページを事前に生成し、それらを静的ファイル(通常はHTML、CSS、JavaScriptなど)としてサーバーに配置する手法です。SSGは、あらかじめ定義されたテンプレートとデータを基に、ビルド時に全てのページを生成するので、リクエスト時にサーバー側で動的に生成する必要がなく、非常に高速で効率的です。
<select name="example">
<option value="サンプル1">サンプル1</option>
<option value="サンプル2">サンプル2</option>
<option value="サンプル3">サンプル3</option>
</select>
<c:forEach items="${itemAry}" var="item" varStatus="status">
<option value="${status.count}" <c:if test="${status.count==itemIndexStr}">selected</c:if>> ${item}</option>
</c:forEach>
<!--職種 -->
<div class="mr-2">
<form:select path="jobTypeId" onchange="showHome()">
<form:options items="${jobTypeList}" itemLabel="label" itemValue="id" /
</form:select>
</div>
<select class="dropdown form-control">
<option th:value="0"></option>
<option th:each="item : ${mDepartmentMap}" th:value="${item.value}" th:text="${item.key}"></option>
</select>