【連載第6回】最終動作確認と実行
前回の投稿では、CodeAGIで生成されたコードを修正する方法について紹介しました。本記事では、生成されたプログラムを修正して、実際に動かすまでのステップを紹介していきます。
修正していくプログラムは連載第4回目で生成した「仕入情報照会画面」です。
1. 開発の前提条件
今回の説明は、以下のPC環境を前提に進めていきます。
環境 | 詳細 |
---|---|
OS | Windows 11 Home |
DB | MySQL 8.0.42 |
JDK | JDK 21 (Java SE Development Kit 21) |
IDE | STS4 (Spring Tools 4 for Eclipse) |
ライブラリ | Lombok |
STS4は「Pleiades」プラグインをインストールして、日本語化しています。
これらのインストール手順については割愛させていただきます。他サイトより手順を確認してください。
2. プロジェクトの雛形を作成
CodeAGIで生成されるプログラムは、それ単体では動かすことができません。
まずはプロジェクトのベースとなる雛形を用意する必要があります。
今回修正していく「仕入情報照会画面」はSpring Bootフレームワークのプロジェクトであるため、STS4から新しいSpringスタータープロジェクトを作成します。
STS4を開き、「ファイル(F) > 新規(N) > Spring スターター・プロジェクト(Spring Initializr)」をクリックしてください。
基本情報の設定
表示される「新規 Spring スターター・プロジェクト(Spring Initializr)」ポップアップに、今回のプロジェクト情報を入力していきます。
入力項目の簡単な説明をします。
- サービス URL(Service URL):規定値の「https://start.spring.io」で問題ありません
- 名前(Name):規定値の「demo」でも構いませんが、今回は「仕入情報照会画面」を作成しようとしているので、「purchaseinquiry」とします
- タイプ(Type):今回は「Maven」とします
- Java バージョン(Java Version):PCにインストールしているJDKのバージョンを入力するため、今回は「21」とします
- パッケージング(Packaging):規定値の「Jar」で問題ありません
- 言語(Language):規定値の「Java」で問題ありません
- グループ(Group):規定値の「com.example」で問題ありません
-
成果物(Artifact):「名前」項目と同じ値を入力してください
※「名前」項目を入力すると自動で補完されます - バージョン(Version):規定値の「0.0.1-SNAPSHOT」で問題ありません
- 説明(Description):規定値の「Demo project for Spring Boot」でも構いませんが、今回は「仕入情報照会画面」を作成しようとしているので、「Demo project for Purchase Info Inquiry Screen」とします
- パッケージ(Package):こちらは、CodeAGIで生成したプログラムのパッケージ構成と同じ形にしたいため、今回は「com.example」とします
入力を終えた後は、「次へ(N)」をクリックしてください。
依存関係の設定
「次へ(N)」をクリックすると、「新規 Spring スターター・プロジェクト依存関係」ポップアップが表示されます。
こちらのポップアップで、使用するSpring Bootフレームワークのバージョンと、プロジェクトで使用予定の依存環境を選択します。
「Spring Boot バージョン」は規定値のままで問題ありません。
今回のプロジェクトで使用する依存関係は以下の通りです。
依存関係 | 詳細 |
---|---|
Spring Web | Webアプリケーションを構築するための依存関係 |
Thymeleaf | Javaテンプレートエンジン |
Spring Data JPA | ORM |
MySQL Driver | MySQLを使用するためのDBドライバー |
Lombok | アノテーションライブラリ |
Validation(検証) | ユーザーが入力したデータ(フォーム)を検証する仕組み |
Spring Boot DevTools | 開発を効率化するためのツールセット(ホットリロードなど) |
Spring Boot Actuator | デバッグを容易にするための機能を提供 |
「Spring Boot DevTools」と「Spring Boot Actuator」の選択は任意です。
依存関係の選択が完了しましたら、「完了(F)」をクリックしてください。
作成したSpring Bootのプロジェクトが、画面左側の「パッケージ・エクスプローラー」に表示されます。
プロジェクトの雛形が作成できました!
3. 作成した雛形に生成コードを配置
先ほど作成したプロジェクトの雛形に、CodeAGIで生成したプログラムを配置します。
以下に生成したプログラム情報を再掲します。(連載4回目より)
-
Entity.java
- MstItemCategory.java
- MstItem.java
- MstPerson.java
- MstSupplier.java
- PurchaseInformation.java
-
SystemApplication.java
- SystemApplication.java
-
Repository.java
- MstItemCategoryRepository.java
- MstItemRepository.java
- MstPersonRepository.java
- MstSupplierRepository.java
- PurchaseInformationRepository.java
-
Service.java
- PurchaseInformationService.java
- MstSupplierService.java
- MstPersonService.java
- MstItemCategoryService.java
-
Form.java
- PurchaseInformationSearchForm.java
-
Controller.java
- PurchaseInformationController.java
-
action.js
- action.js
-
HTML
- purchaseInformationSearch.html
連載4回目の記事では、「仕入先参照画面」のコードも生成していますが、本記事では「仕入情報照会画面」の修正のみ行うため、「仕入先参照画面」の掲載を省いています。
CodeAGIからすべてのファイルを出力します。 出力時のフォルダ構成は、このようになっています。
#指定したルートディレクトリ#
├── プロジェクト共通定義
│ └── Spring Boot
│ └── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── entity
│ │ ├── MstItem.java
│ │ ├── MstItemCategory.java
│ │ ├── MstPerson.java
│ │ ├── MstSupplier.java
│ │ └── PurchaseInformation.java
│ └── SystemApplication.java
│
└── 仕入情報照会画面
└── Spring Boot
└── src
└── main
├── java
│ └── com
│ └── example
│ ├── repository
│ │ ├── MstItemCategoryRepository.java
│ │ ├── MstItemRepository.java
│ │ ├── MstPersonRepository.java
│ │ ├── MstSupplierRepository.java
│ │ └── PurchaseInformationRepository.java
│ ├── service
│ │ ├── MstItemCategoryService.java
│ │ ├── MstPersonService.java
│ │ ├── MstSupplierService.java
│ │ └── PurchaseInformationService.java
│ ├── form
│ │ └── PurchaseInformationSearchForm.java
│ └── controller
│ └── PurchaseInformationController.java
└── resources
├── static
│ └── js
│ └── action.js
└── templates
└── purchaseInformationSearch.html
それぞれのファイルをプロジェクトの雛形に配置すると、下の画像のようになります。
プロジェクトの雛形を作成したときについてきた「PurchaseinquiryApplication.java」は、「SystemApplication.java」と内容が同等であるため、この段階で削除してあります。
4. データベースの設定
データベースの設定を行わないとプロジェクトを起動することができません。そのため、まずはデータベースの設定から行っていきましょう。
MySQL側の設定
今回のプロジェクトではORMとして「Spring Data JPA」を使用しているため、テーブルの作成は事前に行っておく必要はありません。しかし、データベースは作成しておく必要があります。
コマンドプロンプトからMySQLにログインして、下のコードを実行してください。※データベース名は「PURCHASE_INFO_DEMO」としています。
CREATE DATABASE PURCHASE_INFO_DEMO;
これで、MySQL側での設定は完了です。
Spring Bootプロジェクト側の設定
データベースとの接続に必要な情報は、
「src/main/resources/application.properties」
ファイルに記載していきます。
プロジェクトの雛形を作る段階で自動的に作成されますが、中身は一行のコードのみとなっています。
spring.application.name=purchaseinquiry
application.propertiesの内容を以下のように書き換えます。
spring.application.name=purchaseinquiry
# --- 1. Database config properties ---
spring.datasource.url=jdbc:mysql://localhost:3306/PURCHASE_INFO_DEMO
spring.datasource.username={ユーザー名}
spring.datasource.password={パスワード}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# -------------------------------------
# --- 2. JPA config ---
spring.jpa.hibernate.ddl-auto=update
# ---------------------
コードの簡単な説明をします。
- 「1. Database config properties」には、MySQLとの接続に必要な情報を記載します
-
spring.datasource.url
:今回のプロジェクトで使用する、MySQL内のデータベースへのURLを指定します -
spring.datasource.username
:ログイン用のユーザー名を指定します -
spring.datasource.password
:ログイン用のパスワードを指定します -
spring.datasource.driver-class-name
:MySQLのDBドライバーを指定します
-
- 「2. JPA config」には、ORMの設定を記載します。
-
spring.jpa.hibernate.ddl-auto
:「update」を指定することで、プロジェクトを実行したときに、テーブルが自動で生成・更新されます。
-
これでひとまずデータベースの設定は終わりです。 あとはプロジェクトを実行することができれば、データベースにEntityクラスに対応したテーブルが自動的に作成されます。
次のステップでは、プロジェクトを実行できるようになるまでの流れを説明します。
5. プロジェクトの実行
プロジェクトを実行するには、プログラム内で構文エラーとなっている部分をすべて修正する必要があります。
STS4の「パッケージ・エクスプローラー」を確認すると、以下のパッケージ内でエラーが発生しています。
com.example.entity
com.example.form
com.example.controller
今回発生しているエラー内容は、全ファイルで共通していて、import文でエラーが出ています。jakartaパッケージからインポートするべきところが、javaxパッケージになっています。
「javax
」という記載をすべて「jakarta
」に置き換えます。
// 例) MstItem.java
- import javax.persistence.*;
+ import jakarta.persistence.*;
// 例) PurchaseInformationSearchForm.java
- import javax.validation.constraints.Size;
+ import jakarta.validation.constraints.Size;
// 例) PurchaseInformationController.java
- import javax.validation.Valid;
+ import jakarta.validation.Valid;
これでプロジェクトの実行を妨げている構文エラーはすべて消えました。
ではプロジェクトを実行してみましょう。
STS4の画面左下に位置する「Boot ダッシュボード」から、今回のプロジェクト(purchaseinquiry)を選択して実行してみます。
画面下部の「コンソール」に下の画像のような表示が出ていれば、プロジェクトの実行は成功です!
この時点で、MySQLにテーブルが作成されます。
mysql> show tables;
+------------------------------+
| Tables_in_purchase_info_demo |
+------------------------------+
| mst_item |
| mst_item_category |
| mst_person |
| mst_supplier |
| purchase_information |
+------------------------------+
6. 初期表示
先ほどのステップで、プロジェクトを実行することができました。
このステップでは、ブラウザに「仕入情報照会画面」を表示できるようになるまでの流れを説明します。
URLの確認
今回生成した「仕入情報照会画面」がどのURLで表示されるのかは、Controller.javaのコードを確認することで判断できます。
package com.example.controller;
import com.example.form.PurchaseInformationSearchForm;
// ...
/**
* 仕入情報コントローラークラス
*/
@Controller
@RequestMapping("/purchase")
public class PurchaseInformationController {
// ...
/**
* 初期表示処理
*
* @param model モデル
* @return 仕入情報照会画面
*/
@GetMapping("/search")
public String init(Model model) {
// ...
}
/**
* 検索処理
*
* @param form 検索フォーム
* @param result バインディング結果
* @param model モデル
* @return 仕入情報照会画面
*/
@PostMapping("/search")
public String search(@Valid PurchaseInformationSearchForm form, BindingResult result, Model model) {
// ...
}
}
ファイル内のアノテーション(@~)を確認すると、
- クラスに「
@RequestMapping("/purchase")
」 - 初期表示処理メソッドに「
@GetMapping("/search")
」
となっています。
したがって、今回の初期表示用のURLは、このようになります。
http://localhost:8080/purchase/search
初期表示までの修正点
どのURLで「仕入情報照会画面」を表示できるのかは確認できました。しかし、今の状態でURLにアクセスしようとすると、エラーが出てしまいます。エラーの原因を修正していきましょう。
「PurchaseInformationController.java」ファイルの内容を確認すると、クラスの各メソッドに「return "purchase/search";
」というコードがあります。
Controllerクラスのメソッドの戻り値は、HTMLテンプレートへのファイルパスにしないといけません。
生成された「仕入情報照会画面」のHTMLファイルは、templatesフォルダ直下に配置した「purchaseInformationSearch.html」だけです。そのため、このようにコードを書き換えます。
- return "purchase/search";
+ return "purchaseInformationSearch";
間違っていた6ヵ所を書き換え、「http://localhost:8080/purchase/search」にアクセスしてみます。
画面を表示することができました!
Bootstrap CSSの反映
画面を表示することはできましたが、デザインがまるっきり抜けてしまっています。
この原因は、HTMLファイルでインポートしているBootstrap CSSへのパスが間違っているためです。
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>仕入情報照会</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<script src="/js/action.js"></script>
</head>
<body>
<!-- ... -->
</body>
</html>
今回はBootstrap CSSをローカル環境にダウンロードしていないため、CDN経由でインポートする方法に変更します。
- <link rel="stylesheet" href="/css/bootstrap.min.css">
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
変更後、ブラウザをリフレッシュすると、デザインが反映された状態の「仕入情報照会画面」が表示されます。
7. 設計書通りに修正
ここからは、「仕入情報照会画面」が設計書通りに動くように、プログラムを修正していきます。
action.jsの削除
今回のプロジェクトでは、JavaScriptのコードは不要です。そのため、「purchaseInformationSearch.html」から「action.js」へのリンクを削除します。
<head>
タグ内から<script>
タグの記載を消してください。
- <script src="/js/action.js"></script>
Formオブジェクトのリンク
HTMLとController間でFormオブジェクトの受け渡しができるように、「PurchaseInformationController.java」の検索処理メソッドを修正します。
メソッド定義を以下のように書き換えます。
- public String search(@Valid PurchaseInformationSearchForm form, BindingResult result, Model model) {
+ public String search(@Valid @ModelAttribute("searchForm") PurchaseInformationSearchForm form, BindingResult result, Model model) {
メソッドの引数でFormクラスを指定しているところに、「@ModelAttribute("searchForm")
」を追加しました。この修正により、画面から検索処理を実行したときに、フォームの入力値をController側に渡すことができます。
※このアノテーションの引数には、HTMLとController間で受け渡しするFormオブジェクトの識別名を定義する必要があります。そのため、ここで定義した名前をHTML側でも使用してください。
セッションスコープに保存
「PurchaseInformationController.java」の初期表示処理メソッドでは、マスタデータとFormオブジェクトをリクエストスコープに保存しています。このままでは、アプリで画面遷移があったときに、保存していた値が消えてしまいます。
したがって、セッションスコープに保存するようにコードを修正します。
// ...
+ import org.springframework.web.bind.annotation.SessionAttributes;
+ import java.time.LocalDate;
+ import java.time.temporal.TemporalAdjusters;
// ...
/**
* 仕入情報コントローラークラス
*/
@Controller
+ @SessionAttributes({"suppliers", "persons", "itemCategories", "searchForm"})
@RequestMapping("/purchase")
public class PurchaseInformationController {
// ...
+ @ModelAttribute("suppliers")
+ public List<MstSupplier> suppliers() {
+ // 仕入先リストを取得
+ return mstSupplierService.getAllSuppliers();
+ }
+ @ModelAttribute("persons")
+ public List<MstPerson> persons() {
+ // 担当者リストを取得
+ return mstPersonService.getAllPersons();
+ }
+ @ModelAttribute("itemCategories")
+ public List<MstItemCategory> itemCategories() {
+ // 商品種別リストを取得
+ return mstItemCategoryService.getAllItemCategories();
+ }
+ @ModelAttribute("searchForm")
+ public PurchaseInformationSearchForm searchForm() {
+ PurchaseInformationSearchForm searchForm = new PurchaseInformationSearchForm();
+ // フォームの仕入開始日と仕入終了日に値を自動で入力
+ LocalDate today = LocalDate.now();
+ searchForm.setStartDate(today.with(TemporalAdjusters.firstDayOfMonth()));
+ searchForm.setEndDate(today.with(TemporalAdjusters.lastDayOfMonth()));
+ return searchForm;
+ }
/**
* 初期表示処理
*
* @param model モデル
* @return 仕入情報照会画面
*/
@GetMapping("/search")
public String init(Model model) {
- // 仕入先リストを取得
- model.addAttribute("suppliers", mstSupplierService.getAllSuppliers());
- // 担当者リストを取得
- model.addAttribute("persons", mstPersonService.getAllPersons());
- // 商品種別リストを取得
- model.addAttribute("itemCategories", mstItemCategoryService.getAllItemCategories());
- // フォームの初期化
- PurchaseInformationSearchForm form = new PurchaseInformationSearchForm();
- model.addAttribute("searchForm", form);
return "purchaseInformationSearch";
}
この変更に伴って、「purchaseInformationSearch.html」のコードもセッションスコープから値を読み取るように修正します。
<!-- 仕入先 -->
- ${suppliers}"
+ ${session.suppliers}"
<!-- 仕入担当者 -->
- ${persons}"
+ ${session.persons}"
<!-- 商品種別 -->
- ${itemCategories}"
+ ${session.itemCategories}"
日付型の変換
アプリ全体で使用している日付型が、今では非推奨の「java.util.Date
」になっています。これらをすべて「java.time.LocalDate
」に変更していきます。
修正箇所は、以下のファイルです。
-
Entity.java
修正ファイル:「MstItemCategory.java」、「MstItem.java」、「MstPerson.java」、「MstSupplier.java」、「PurchaseInformation.java」※「// 例)PurchaseInformation.java - import java.util.Date; + import java.time.LocalDate; // ... /** * 更新日時 */ @Column(name = "EDIT_DATETIME") - @Temporal(TemporalType.TIMESTAMP) - private Date editDatetime; + private LocalDate editDatetime; // 他にも2フィールドを修正
java.util.Date
」の廃止に伴って、「@Temporal
」アノテーションの削除も行っています。
-
Repository.java
修正ファイル:「PurchaseInformationRepository.java」- import java.util.Date; + import java.time.LocalDate; // ... List<PurchaseInformation> findByCriteria( @Param("supplierCode") String supplierCode, @Param("purchasePic") String purchasePic, - @Param("startDate") Date startDate, + @Param("startDate") LocalDate startDate, - @Param("endDate") Date endDate, + @Param("endDate") LocalDate endDate, @Param("itemCategory") String itemCategory, @Param("itemName") String itemName);
-
Service.java
修正ファイル:「PurchaseInformationService.java」- import java.util.Date; + import java.time.LocalDate; // ... - public List<PurchaseInformation> searchPurchaseInformation(String supplierCode, String purchasePic, Date startDate, Date endDate, String itemCategory, String itemName) { + public List<PurchaseInformation> searchPurchaseInformation(String supplierCode, String purchasePic, LocalDate startDate, LocalDate endDate, String itemCategory, String itemName) { return purchaseInformationRepository.findByCriteria(supplierCode, purchasePic, startDate, endDate, itemCategory, itemName); }
-
Form.java
修正ファイル:「PurchaseInformationSearchForm.java」※「- import java.util.Date; + import java.time.LocalDate; // ... /** * 仕入開始日 */ - @DateTimeFormat(pattern = "yyyy/MM/dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") - private Date startDate; + private LocalDate startDate; // 仕入終了日のフィールドも同じように修正する
@DateTimeFormat
」アノテーションのコードも修正しました。(HTMLのフォームに入力する日付の形と、Controller側で受け取る日付の形が異なっていたため)
最後に、HTMLのコードも修正する必要があります。
「purchaseInformationSearch.html」ファイルの「#dates.~
」というコードを「#temporals.~
」に変更します。
<!-- 仕入日 -->
<div class="form-group">
<label for="startDate">仕入開始日:</label>
- <input type="date" id="startDate" name="startDate" class="form-control" th:value="${#dates.format(searchForm.startDate, 'yyyy-MM-dd')}">
+ <input type="date" id="startDate" name="startDate" class="form-control" th:value="${#temporals.format(searchForm.startDate, 'yyyy-MM-dd')}">
</div>
<!-- 他の2ヵ所も修正する -->
※「#dates
」は「java.util.Date
」などを扱うユーティリティで、「#temporals
」は「java.time.LocalDate
」などを扱うユーティリティとなっています。
HTML側に渡す検索結果(DTO対応)
画面側で表示するデータと、データベースから取得されるデータが異なっています。
HTML側 | DB取得結果 |
---|---|
商品コード 商品名 商品種別 仕入先 仕入日 仕入単価 仕入数量 仕入金額 仕入担当者名 |
仕入番号 仕入日 仕入先コード 商品コード 仕入単価 仕入担当者 登録者 登録日時 更新者 更新日時 |
この違いをなくすために、今回はDTOクラスを新しく定義します。
具体的には、「com.example.dto
」パッケージを作成して、そのパッケージに「PurchaseInformationDto.java」ファイルを作成します。
「パッケージ・エクスプローラー」からプロジェクトを右クリックし、「新規(W) > パッケージ」をクリックして、新しいパッケージを作成します。
「名前(M)」欄に「com.example.dto」と入力し、「完了(F)」をクリックしてください。
次に、新しいDTOクラスを作成します。
「パッケージ・エクスプローラー」から「com.example.dto
」パッケージを右クリックし、「新規(W) > クラス」をクリックします。
「名前(M)」欄に「PurchaseInformationDto」と入力し、「完了(F)」をクリックしてください。
作成された「PurchaseInformationDto.java」ファイルの内容はこのようにします。
package com.example.dto;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PurchaseInformationDto {
/**
* 商品コード
*/
private String itemCode;
/**
* 商品名
*/
private String itemName;
/**
* 商品種別
*/
private String itemCategory;
/**
* 仕入先
*/
private String supplierName;
/**
* 仕入日
*/
private LocalDate purchaseDate;
/**
* 仕入単価
*/
private Integer purchaseUnitPrice;
/**
* 仕入数量
*/
private Integer purchaseQuantity;
/**
* 仕入担当者名
*/
private String purchasePic;
}
※HTML側で表示するデータ名と、DTO側で定義するフィールド名が一致するようにしてください。
それでは、新しいDTOクラスを使用するように他のファイルも修正します。
- Repository.java
修正ファイル:「PurchaseInformationRepository.java」※SQL文のSELECT内もDTO対応に伴って修正しています。// ... + import com.example.dto.PurchaseInformationDto; // ... - @Query("SELECT pi FROM PurchaseInformation pi " + + @Query("SELECT new com.example.dto.PurchaseInformationDto(" + + "mi.itemCode, mi.itemName, mic.itemCategoryName, ms.supplierName, pi.purchaseDate, pi.purchaseUnitPrice, pi.purchaseQuantity, mp.personName) " + + "FROM PurchaseInformation pi " + "JOIN MstSupplier ms ON pi.supplierCode = ms.supplierCode " + "JOIN MstPerson mp ON pi.purchasePic = mp.personCode " + "JOIN MstItem mi ON pi.itemCode = mi.itemCode " + "JOIN MstItemCategory mic ON mi.itemCategory = mic.itemCategory " + "WHERE (:supplierCode IS NULL OR pi.supplierCode = :supplierCode) " + "AND (:purchasePic IS NULL OR pi.purchasePic = :purchasePic) " + "AND (:startDate IS NULL OR pi.purchaseDate >= :startDate) " + "AND (:endDate IS NULL OR pi.purchaseDate <= :endDate) " + "AND (:itemCategory IS NULL OR mi.itemCategory = :itemCategory) " + "AND (:itemName IS NULL OR mi.itemName LIKE %:itemName%) " + "ORDER BY pi.supplierCode, pi.purchaseDate") - List<PurchaseInformation> findByCriteria( + List<PurchaseInformationDto> findByCriteria( // ...
- Service.java
修正ファイル:「PurchaseInformationService.java」// ... - import com.example.entity.PurchaseInformation; + import com.example.dto.PurchaseInformationDto; // ... - public List<PurchaseInformation> searchPurchaseInformation(String supplierCode, String purchasePic, LocalDate startDate, LocalDate endDate, String itemCategory, String itemName) { + public List<PurchaseInformationDto> searchPurchaseInformation(String supplierCode, String purchasePic, LocalDate startDate, LocalDate endDate, String itemCategory, String itemName) { return purchaseInformationRepository.findByCriteria(supplierCode, purchasePic, startDate, endDate, itemCategory, itemName); }
- Controller.java
修正ファイル:「PurchaseInformationController.java」// ... - import com.example.entity.PurchaseInformation; + import com.example.dto.PurchaseInformationDto; // ... // 検索処理 - List<PurchaseInformation> purchaseInformationList = purchaseInformationService.searchPurchaseInformation( + List<PurchaseInformationDto> purchaseInformationList = purchaseInformationService.searchPurchaseInformation( form.getSupplierCode(), form.getPurchasePic(), form.getStartDate(), form.getEndDate(), form.getItemCategory(), form.getItemName() ); // ...
DB検索用SQLの最終調整
画面側から送られてくる「仕入先」、「仕入担当者」、「商品種別」、「商品名」の値が未選択(空文字)の場合は、データベースの抽出条件から外さないと、空文字のデータを検索するSQL文が発行されてしまいます。
そのため、「PurchaseInformationRepository.java」のSQL文を修正します。
// ...
/**
* 検索条件に基づいて仕入情報を取得するクエリ
*
* @param supplierCode 仕入先コード
* @param purchasePic 仕入担当者
* @param startDate 仕入開始日
* @param endDate 仕入終了日
* @param itemCategory 商品種別
* @param itemName 商品名
* @return 仕入情報のリスト
*/
@Query("SELECT new com.example.dto.PurchaseInformationDto(" +
"mi.itemCode, mi.itemName, mic.itemCategoryName, ms.supplierName, pi.purchaseDate, pi.purchaseUnitPrice, pi.purchaseQuantity, mp.personName) " +
"FROM PurchaseInformation pi " +
"JOIN MstSupplier ms ON pi.supplierCode = ms.supplierCode " +
"JOIN MstPerson mp ON pi.purchasePic = mp.personCode " +
"JOIN MstItem mi ON pi.itemCode = mi.itemCode " +
"JOIN MstItemCategory mic ON mi.itemCategory = mic.itemCategory " +
- "WHERE (:supplierCode IS NULL OR pi.supplierCode = :supplierCode) " +
+ "WHERE (:supplierCode IS NULL OR :supplierCode = '' OR pi.supplierCode = :supplierCode) " +
- "AND (:purchasePic IS NULL OR pi.purchasePic = :purchasePic) " +
+ "AND (:purchasePic IS NULL OR :purchasePic = '' OR pi.purchasePic = :purchasePic) " +
"AND (:startDate IS NULL OR pi.purchaseDate >= :startDate) " +
"AND (:endDate IS NULL OR pi.purchaseDate <= :endDate) " +
- "AND (:itemCategory IS NULL OR mi.itemCategory = :itemCategory) " +
+ "AND (:itemCategory IS NULL OR :itemCategory = '' OR mi.itemCategory = :itemCategory) " +
- "AND (:itemName IS NULL OR mi.itemName LIKE %:itemName%) " +
+ "AND (:itemName IS NULL OR :itemName = '' OR mi.itemName LIKE %:itemName%) " +
"ORDER BY pi.supplierCode, pi.purchaseDate")
List<PurchaseInformation> findByCriteria(
// ...
HTMLの最終調整
フォーム内のドロップダウン要素から、「th:field="項目への参照"
」というコードが抜けているため、画面遷移が発生した場合に入力内容が保持されません。
「purchaseInformationSearch.html」のコードを修正します。
<!-- ... -->
- <select id="supplierCode" name="supplierCode" class="form-control">
+ <select id="supplierCode" name="supplierCode" th:field="*{supplierCode}" class="form-control">
<!-- ... -->
- <select id="purchasePic" name="purchasePic" class="form-control">
+ <select id="purchasePic" name="purchasePic" th:field="*{purchasePic}" class="form-control">
<!-- ... -->
- <select id="itemCategory" name="itemCategory" class="form-control">
+ <select id="itemCategory" name="itemCategory" th:field="*{itemCategory}" class="form-control">
<!-- ... -->
また、検索結果表示テーブルのフォーマット変換処理コードにも誤りがあるため、そちらも修正します。
<!-- ... -->
- <td th:text="${#numbers.formatInteger(info.purchaseUnitPrice, '###,###,##0')}">仕入単価</td>
+ <td th:text="${#numbers.formatInteger(info.purchaseUnitPrice, 3, 'COMMA')}">仕入単価</td>
- <td th:text="${#numbers.formatInteger(info.purchaseQuantity, '###,###,##0')}">仕入数量</td>
+ <td th:text="${#numbers.formatInteger(info.purchaseQuantity, 3, 'COMMA')}">仕入数量</td>
- <td th:text="${#numbers.formatInteger(info.purchaseUnitPrice * info.purchaseQuantity, '###,###,##0')}">仕入金額</td>
+ <td th:text="${#numbers.formatInteger(info.purchaseUnitPrice * info.purchaseQuantity, 3, 'COMMA')}">仕入金額</td>
<!-- ... -->
8. 最後に
これですべての修正が終わりました。あとはデータベースの各テーブルにテスト用データを追加すれば、「仕入情報照会画面」が動作します。
CodeAGIを使用すれば、テストデータの生成も簡単にできます。
テストデータ生成についての説明は、本記事では割愛させていただきますが、未来の投稿では詳細に説明させていただきます!
各ファイルの修正後の内容はこちらです。
MstItem.java
// MstItem.java
package com.example.entity;
import lombok.Data;
import jakarta.persistence.*;
import java.time.LocalDate;
/**
* 商品マスタ
*/
@Entity
@Table(name = "MST_ITEM")
@Data
public class MstItem {
/**
* 商品コード
*/
@Id
@Column(name = "ITEM_CODE", length = 13, nullable = false)
private String itemCode;
/**
* 商品名
*/
@Column(name = "ITEM_NAME", length = 100)
private String itemName;
/**
* 商品略称
*/
@Column(name = "ITEM_SHORT_NAME", length = 50)
private String itemShortName;
/**
* 商品種別
*/
@Column(name = "ITEM_CATEGORY", length = 3)
private String itemCategory;
/**
* 登録者
*/
@Column(name = "REGIST_PERSON", length = 5)
private String registPerson;
/**
* 登録日時
*/
@Column(name = "REGIST_DATETIME")
private LocalDate registDatetime;
/**
* 更新者
*/
@Column(name = "EDIT_PERSON", length = 5)
private String editPerson;
/**
* 更新日時
*/
@Column(name = "EDIT_DATETIME")
private LocalDate editDatetime;
}
MstItemCategory.java
// MstItemCategory.java
package com.example.entity;
import lombok.Data;
import jakarta.persistence.*;
import java.time.LocalDate;
// 商品種別マスタ
@Entity
@Table(name = "MST_ITEM_CATEGORY")
@Data
public class MstItemCategory {
// 商品種別
@Id
@Column(name = "ITEM_CATEGORY", length = 3, nullable = false)
private String itemCategory;
// 商品種別名
@Column(name = "ITEM_CATEGORY_NAME", length = 50)
private String itemCategoryName;
// 登録者
@Column(name = "REGIST_PERSON", length = 5)
private String registPerson;
// 登録日時
@Column(name = "REGIST_DATETIME")
private LocalDate registDatetime;
// 更新者
@Column(name = "EDIT_PERSON", length = 5)
private String editPerson;
// 更新日時
@Column(name = "EDIT_DATETIME")
private LocalDate editDatetime;
}
MstPerson.java
// MstPerson.java
package com.example.entity;
import lombok.Data;
import jakarta.persistence.*;
import java.time.LocalDate;
/**
* 担当者マスタ
*/
@Entity
@Table(name = "MST_PERSON")
@Data
public class MstPerson {
/**
* 担当者コード
*/
@Id
@Column(name = "PERSON_CODE", length = 5, nullable = false)
private String personCode;
/**
* 担当者名
*/
@Column(name = "PERSON_NAME", length = 100)
private String personName;
/**
* 担当者部署
*/
@Column(name = "PERSON_DIVISION", length = 50)
private String personDivision;
/**
* 担当者連絡先
*/
@Column(name = "PERSON_PHON_NO", length = 15)
private String personPhonNo;
/**
* 登録者
*/
@Column(name = "REGIST_PERSON", length = 5)
private String registPerson;
/**
* 登録日時
*/
@Column(name = "REGIST_DATETIME")
private LocalDate registDatetime;
/**
* 更新者
*/
@Column(name = "EDIT_PERSON", length = 5)
private String editPerson;
/**
* 更新日時
*/
@Column(name = "EDIT_DATETIME")
private LocalDate editDatetime;
}
MstSupplier.java
// MstSupplier.java
package com.example.entity;
import lombok.Data;
import jakarta.persistence.*;
import java.time.LocalDate;
/**
* 仕入先マスタ
*/
@Entity
@Table(name = "MST_SUPPLIER")
@Data
public class MstSupplier {
/**
* 仕入先コード
*/
@Id
@Column(name = "SUPPLIER_CODE", length = 4, nullable = false)
private String supplierCode;
/**
* 仕入先名
*/
@Column(name = "SUPPLIER_NAME", length = 100)
private String supplierName;
/**
* 仕入先略称
*/
@Column(name = "SUPPLIER_SHORT_NAME", length = 50)
private String supplierShortName;
/**
* 仕入先連絡先
*/
@Column(name = "SUPPLIER_PHONE_NO", length = 15)
private String supplierPhoneNo;
/**
* 仕入先担当者
*/
@Column(name = "SUPPLIER_PIC", length = 100)
private String supplierPic;
/**
* 登録者
*/
@Column(name = "REGIST_PERSON", length = 5)
private String registPerson;
/**
* 登録日時
*/
@Column(name = "REGIST_DATETIME")
private LocalDate registDatetime;
/**
* 更新者
*/
@Column(name = "EDIT_PERSON", length = 5)
private String editPerson;
/**
* 更新日時
*/
@Column(name = "EDIT_DATETIME")
private LocalDate editDatetime;
}
PurchaseInformation.java
// PurchaseInformation.java
package com.example.entity;
import lombok.Data;
import jakarta.persistence.*;
import java.time.LocalDate;
/**
* 仕入情報エンティティクラス
* テーブル名: 仕入情報
*/
@Entity
@Table(name = "PURCHASE_INFORMATION")
@Data
public class PurchaseInformation {
/**
* 仕入番号
*/
@Id
@Column(name = "PURCHASE_NO", nullable = false, length = 10)
private Long purchaseNo;
/**
* 仕入日
*/
@Column(name = "PURCHASE_DATE", nullable = false)
private LocalDate purchaseDate;
/**
* 仕入先コード
*/
@Column(name = "SUPPLIER_CODE", nullable = false, length = 4)
private String supplierCode;
/**
* 商品コード
*/
@Column(name = "ITEM_CODE", nullable = false, length = 13)
private String itemCode;
/**
* 仕入単価
*/
@Column(name = "PURCHASE_UNIT_PRICE", nullable = false, length = 7)
private Integer purchaseUnitPrice;
/**
* 仕入数量
*/
@Column(name = "PURCHASE_QUANTITY", nullable = false, length = 5)
private Integer purchaseQuantity;
/**
* 仕入担当者
*/
@Column(name = "PURCHASE_PIC", nullable = false, length = 5)
private String purchasePic;
/**
* 登録者
*/
@Column(name = "REGIST_PERSON", length = 5)
private String registPerson;
/**
* 登録日時
*/
@Column(name = "REGIST_DATETIME")
private LocalDate registDatetime;
/**
* 更新者
*/
@Column(name = "EDIT_PERSON", length = 5)
private String editPerson;
/**
* 更新日時
*/
@Column(name = "EDIT_DATETIME")
private LocalDate editDatetime;
}
PurchaseInformationRepository.java
package com.example.repository;
import com.example.dto.PurchaseInformationDto;
import com.example.entity.PurchaseInformation;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.time.LocalDate;
import java.util.List;
/**
* 仕入情報のリポジトリインターフェース
*/
@Repository
public interface PurchaseInformationRepository extends JpaRepository<PurchaseInformation, Long> {
/**
* 検索条件に基づいて仕入情報を取得するクエリ
*
* @param supplierCode 仕入先コード
* @param purchasePic 仕入担当者
* @param startDate 仕入開始日
* @param endDate 仕入終了日
* @param itemCategory 商品種別
* @param itemName 商品名
* @return 仕入情報のリスト
*/
@Query("SELECT new com.example.dto.PurchaseInformationDto(" +
"mi.itemCode, mi.itemName, mic.itemCategoryName, ms.supplierName, pi.purchaseDate, pi.purchaseUnitPrice, pi.purchaseQuantity, mp.personName) " +
"FROM PurchaseInformation pi " +
"JOIN MstSupplier ms ON pi.supplierCode = ms.supplierCode " +
"JOIN MstPerson mp ON pi.purchasePic = mp.personCode " +
"JOIN MstItem mi ON pi.itemCode = mi.itemCode " +
"JOIN MstItemCategory mic ON mi.itemCategory = mic.itemCategory " +
"WHERE (:supplierCode IS NULL OR :supplierCode = '' OR pi.supplierCode = :supplierCode) " +
"AND (:purchasePic IS NULL OR :purchasePic = '' OR pi.purchasePic = :purchasePic) " +
"AND (:startDate IS NULL OR pi.purchaseDate >= :startDate) " +
"AND (:endDate IS NULL OR pi.purchaseDate <= :endDate) " +
"AND (:itemCategory IS NULL OR :itemCategory = '' OR mi.itemCategory = :itemCategory) " +
"AND (:itemName IS NULL OR :itemName = '' OR mi.itemName LIKE %:itemName%) " +
"ORDER BY pi.supplierCode, pi.purchaseDate")
List<PurchaseInformationDto> findByCriteria(
@Param("supplierCode") String supplierCode,
@Param("purchasePic") String purchasePic,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate,
@Param("itemCategory") String itemCategory,
@Param("itemName") String itemName);
}
PurchaseInformationService.java
package com.example.service;
import com.example.dto.PurchaseInformationDto;
import com.example.repository.PurchaseInformationRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
/**
* 仕入情報サービスクラス
*/
@Service
public class PurchaseInformationService {
@Autowired
private PurchaseInformationRepository purchaseInformationRepository;
/**
* 検索条件に基づいて仕入情報を取得する
*
* @param supplierCode 仕入先コード
* @param purchasePic 仕入担当者
* @param startDate 仕入開始日
* @param endDate 仕入終了日
* @param itemCategory 商品種別
* @param itemName 商品名
* @return 仕入情報のリスト
*/
public List<PurchaseInformationDto> searchPurchaseInformation(String supplierCode, String purchasePic, LocalDate startDate, LocalDate endDate, String itemCategory, String itemName) {
return purchaseInformationRepository.findByCriteria(supplierCode, purchasePic, startDate, endDate, itemCategory, itemName);
}
}
PurchaseInformationSearchForm.java
package com.example.form;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.validation.constraints.Size;
import java.time.LocalDate;
/**
* 仕入情報検索フォームクラス
*/
@Data
public class PurchaseInformationSearchForm {
/**
* 仕入先コード
*/
private String supplierCode;
/**
* 仕入担当者
*/
private String purchasePic;
/**
* 仕入開始日
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;
/**
* 仕入終了日
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;
/**
* 商品種別
*/
private String itemCategory;
/**
* 商品名
*/
@Size(max = 50, message = "商品名は最大50文字までです。")
private String itemName;
}
PurchaseInformationDto.java
package com.example.dto;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PurchaseInformationDto {
/**
* 商品コード
*/
private String itemCode;
/**
* 商品名
*/
private String itemName;
/**
* 商品種別
*/
private String itemCategory;
/**
* 仕入先
*/
private String supplierName;
/**
* 仕入日
*/
private LocalDate purchaseDate;
/**
* 仕入単価
*/
private Integer purchaseUnitPrice;
/**
* 仕入数量
*/
private Integer purchaseQuantity;
/**
* 仕入担当者名
*/
private String purchasePic;
}
PurchaseInformationController.java
package com.example.controller;
import com.example.form.PurchaseInformationSearchForm;
import com.example.service.MstItemCategoryService;
import com.example.service.MstPersonService;
import com.example.service.MstSupplierService;
import com.example.service.PurchaseInformationService;
import com.example.dto.PurchaseInformationDto;
import com.example.entity.MstItemCategory;
import com.example.entity.MstPerson;
import com.example.entity.MstSupplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import jakarta.validation.Valid;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.List;
/**
* 仕入情報コントローラークラス
*/
@Controller
@SessionAttributes({"suppliers", "persons", "itemCategories", "searchForm"})
@RequestMapping("/purchase")
public class PurchaseInformationController {
@Autowired
private PurchaseInformationService purchaseInformationService;
@Autowired
private MstSupplierService mstSupplierService;
@Autowired
private MstPersonService mstPersonService;
@Autowired
private MstItemCategoryService mstItemCategoryService;
@ModelAttribute("suppliers")
public List<MstSupplier> suppliers() {
// 仕入先リストを取得
return mstSupplierService.getAllSuppliers();
}
@ModelAttribute("persons")
public List<MstPerson> persons() {
// 担当者リストを取得
return mstPersonService.getAllPersons();
}
@ModelAttribute("itemCategories")
public List<MstItemCategory> itemCategories() {
// 商品種別リストを取得
return mstItemCategoryService.getAllItemCategories();
}
@ModelAttribute("searchForm")
public PurchaseInformationSearchForm searchForm() {
PurchaseInformationSearchForm searchForm = new PurchaseInformationSearchForm();
// フォームの仕入開始日と仕入終了日に値を自動で入力
LocalDate today = LocalDate.now();
searchForm.setStartDate(today.with(TemporalAdjusters.firstDayOfMonth()));
searchForm.setEndDate(today.with(TemporalAdjusters.lastDayOfMonth()));
return searchForm;
}
/**
* 初期表示処理
*
* @param model モデル
* @return 仕入情報照会画面
*/
@GetMapping("/search")
public String init(Model model) {
return "purchaseInformationSearch";
}
/**
* 検索処理
*
* @param form 検索フォーム
* @param result バインディング結果
* @param model モデル
* @return 仕入情報照会画面
*/
@PostMapping("/search")
public String search(@Valid @ModelAttribute("searchForm") PurchaseInformationSearchForm form, BindingResult result, Model model) {
if (result.hasErrors()) {
return "purchaseInformationSearch";
}
// 検索条件のバリデーション
if (form.getStartDate() == null && form.getEndDate() == null) {
result.reject("error.search", "検索条件を指定してください。");
return "purchaseInformationSearch";
}
if (form.getStartDate() != null && form.getEndDate() != null && form.getStartDate().isAfter(form.getEndDate())) {
result.reject("error.search", "仕入日の検索条件の指定に誤りがあります。");
return "purchaseInformationSearch";
}
// 検索処理
List<PurchaseInformationDto> purchaseInformationList = purchaseInformationService.searchPurchaseInformation(
form.getSupplierCode(),
form.getPurchasePic(),
form.getStartDate(),
form.getEndDate(),
form.getItemCategory(),
form.getItemName()
);
if (purchaseInformationList.isEmpty()) {
result.reject("error.search", "検索結果が存在しませんでした。");
return "purchaseInformationSearch";
}
// 検索結果をモデルに追加
model.addAttribute("purchaseInformationList", purchaseInformationList);
return "purchaseInformationSearch";
}
}
purchaseInformationSearch.html
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>仕入情報照会</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h1>仕入情報照会</h1>
<form th:action="@{/purchase/search}" th:object="${searchForm}" method="post">
<!-- エラーメッセージ表示 -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger" role="alert">
<p th:each="err : ${#fields.errors('*')}" th:text="${err}">エラーメッセージ</p>
</div>
<!-- 仕入先 -->
<div class="form-group">
<label for="supplierCode">仕入先:</label>
<select id="supplierCode" name="supplierCode" th:field="*{supplierCode}" class="form-control">
<option value="">選択してください</option>
<option th:each="supplier : ${session.suppliers}" th:value="${supplier.supplierCode}"
th:text="${supplier.supplierShortName}"></option>
</select>
</div>
<!-- 仕入担当者 -->
<div class="form-group">
<label for="purchasePic">仕入担当者:</label>
<select id="purchasePic" name="purchasePic" th:field="*{purchasePic}" class="form-control">
<option value="">選択してください</option>
<option th:each="person : ${session.persons}" th:value="${person.personCode}"
th:text="${person.personName}"></option>
</select>
</div>
<!-- 仕入日 -->
<div class="form-group">
<label for="startDate">仕入開始日:</label>
<input type="date" id="startDate" name="startDate" class="form-control"
th:value="${#temporals.format(searchForm.startDate, 'yyyy-MM-dd')}">
</div>
<div class="form-group">
<label for="endDate">仕入終了日:</label>
<input type="date" id="endDate" name="endDate" class="form-control"
th:value="${#temporals.format(searchForm.endDate, 'yyyy-MM-dd')}">
</div>
<!-- 商品種別 -->
<div class="form-group">
<label for="itemCategory">商品種別:</label>
<select id="itemCategory" name="itemCategory" th:field="*{itemCategory}" class="form-control">
<option value="">選択してください</option>
<option th:each="category : ${session.itemCategories}" th:value="${category.itemCategory}"
th:text="${category.itemCategoryName}"></option>
</select>
</div>
<!-- 商品名 -->
<div class="form-group">
<label for="itemName">商品名:</label>
<input type="text" id="itemName" name="itemName" class="form-control" th:value="${searchForm.itemName}"
maxlength="50">
</div>
<!-- 検索ボタン -->
<button type="submit" id="searchButton" class="btn btn-primary">検索</button>
</form>
<!-- 検索結果表示 -->
<div th:if="${purchaseInformationList != null}">
<h2>検索結果</h2>
<table class="table table-bordered">
<thead>
<tr>
<th>№</th>
<th>商品コード</th>
<th>商品名</th>
<th>商品種別</th>
<th>仕入先</th>
<th>仕入日</th>
<th>仕入単価</th>
<th>仕入数量</th>
<th>仕入金額</th>
<th>仕入担当者名</th>
</tr>
</thead>
<tbody>
<tr th:each="info, iterStat : ${purchaseInformationList}">
<td th:text="${iterStat.index + 1}">1</td>
<td th:text="${info.itemCode}">商品コード</td>
<td th:text="${info.itemName}">商品名</td>
<td th:text="${info.itemCategory}">商品種別</td>
<td th:text="${info.supplierName}">仕入先</td>
<td th:text="${#temporals.format(info.purchaseDate, 'yyyy/MM/dd')}">仕入日</td>
<td th:text="${#numbers.formatInteger(info.purchaseUnitPrice, 3, 'COMMA')}">仕入単価</td>
<td th:text="${#numbers.formatInteger(info.purchaseQuantity, 3, 'COMMA')}">仕入数量</td>
<td th:text="${#numbers.formatInteger(info.purchaseUnitPrice * info.purchaseQuantity, 3, 'COMMA')}">仕入金額</td>
<td th:text="${info.purchasePic}">仕入担当者名</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
application.properties
spring.application.name=purchaseinquiry
# --- 1. Database config properties ---
spring.datasource.url=jdbc:mysql://localhost:3306/PURCHASE_INFO_DEMO
spring.datasource.username={ユーザー名}
spring.datasource.password={パスワード}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# -------------------------------------
# --- 2. JPA config ---
spring.jpa.hibernate.ddl-auto=update
# ---------------------
本連載「CodeAGIを使って開発を行っていきます。」は、今回が最終回となります。
最後までお読みいただき、ありがとうございました!
「CodeAGI」の詳細については、以下のリンクをご覧ください:
👉 https://codeagi.ai/