1. 前提条件
DBおよびWebプロジェクトの構築が完了していることが前提です。構築がまだ完了していない場合は、以下の記事をご参照ください。
DBの構築手順:
WEBプロジェクトの構築手順:
完全版はこちらです。
また、この記事のソースコードはGitHubにアップロードしましたので、ぜひダウンロードして確認してください。
2. データモデルの設計
ユーザーテーブルは以下のように設計する。
| 項目ID | 項目名 | データ型 | 説明 |
|---|---|---|---|
| user_id | ユーザーID | 主キー(VARCHAR2) | ユーザーの一意識別子 |
| user_name | ユーザー名 | 文字列 (VARCHAR2) | ユーザー名またはログインID |
| password | パスワード | 文字列 (ハッシュ化) | セキュリティ対策のためのパスワードハッシュ化をお勧めですが、今回は明文のままで保存する |
| メールアドレス | 文字列 (VARCHAR2) | ユーザーの連絡先情報 | |
| role | 権限 | 文字列 (VARCHAR2) | ユーザーのアクセス権限を示すロール |
| last_login_date | 最終ログイン日時 | 日付時刻 (TIMESTAMP) | ユーザーの最後のログイン日時 |
| del_flg | 削除フラグ | 文字列 (VARCHAR2) | 物理削除ではなく、削除するユーザーはFLGを1 |
| create_date | 登録日時 | 日付時刻 (TIMESTAMP) | ユーザーアカウント作成日時 |
| create_user | 登録ユーザー | 文字列 (VARCHAR2) | ユーザーアカウント作成ユーザー |
| update_date | 登録日時 | 日付時刻 (TIMESTAMP) | ユーザーアカウント変更日時 |
| update_user | 登録ユーザー | 文字列 (VARCHAR2) | ユーザーアカウント変更ユーザー |
ユーザーテーブルのDDL
CREATE TABLE "USERS"
("USER_ID" VARCHAR2(10 BYTE) NOT NULL ENABLE,
"USER_NAME" VARCHAR2(50 BYTE) NOT NULL ENABLE,
"PASSWORD" VARCHAR2(255 BYTE) NOT NULL ENABLE,
"EMAIL" VARCHAR2(100 BYTE),
"ROLE" VARCHAR2(20 BYTE),
"LAST_LOGIN_DATE" TIMESTAMP (0),
"DEL_FLG" VARCHAR2(1 BYTE),
"CREATE_DATE" TIMESTAMP (0) NOT NULL ENABLE,
"CREATE_USER" VARCHAR2(20 BYTE) NOT NULL ENABLE,
"UPDATE_DATE" TIMESTAMP (0) NOT NULL ENABLE,
"UPDATE_USER" VARCHAR2(20 BYTE) NOT NULL ENABLE,
CONSTRAINT "USERS_PK" PRIMARY KEY ("USER_ID")
)
Oracle SQL DeveloperでDDLを実行する。
作成したuserテーブルです。

3. フロントエンド用ライブラリを追加
pom.xmlにフロントエンド用ライブラリを追加する。
Mavenプロジェクトのpom.xmlにフロントエンド用ライブラリを追加するには、依存関係(dependencies)を設定する必要があります。以下は、jQuery、jQuery UI、およびBootstrapの依存関係をpom.xmlに追加するための例です。
<!-- jquery -->
<dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
<!-- jquery-ui -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery-ui</artifactId>
<version>1.12.1</version>
</dependency>
<!-- bootstrap -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>5.2.3</version>
</dependency>
全体のソースコードはGitHubにご確認ください。
4. 画面設計
登録画面
ユーザーID、ユーザー名、メールアドレスの3つの項目だけを持つ登録フォームを作成する。

テーブルの項目は多くありますが、パスワードの初期値をユーザーIDと同じに設定したいと考えています。ロールについては、別のメンテナンス画面にするか、ドロップダウンリストにするかを検討中です。現時点ではロールの入力欄を空欄にしておきます。
編集画面
登録画面と同じです、但し、ユーザーIDは編集不可になります。

一覧画面
- No.: 行No.
- ユーザーID: ボタンにする。クリックすると編集画面に遷移可能。
- ユーザー名、メールアドレス、削除フラグ: 表示のみ
- 操作列: 物理削除と論理削除ボタンを設ける。
- 物理削除: DBから物理的に削除される。
- 論理削除: 削除フラグを設定する。実際のプロジェクトではユーザーの削除は通常論理削除が行われます。
5.ソースコードの説明
全体ソースはgithubをご参照ください。
ここでは一部を抜粋してポイントだけを説明する。
ビュー「user.html」の説明
画面は3つありますが、すべてを「user.html」に統合しています。使用しているフレームワークはThymeleafで、画面のレイアウトとスタイルはBootstrapを使用しています。イベント処理にはjQueryを利用しています。
今回は入力項目のバリデーションを行っていないため、実際にテストを行う際には、文字数制限を超えないように入力してください。文字数制限を超えると、DBエラーが発生するため、項目の許容範囲内に入力してください。
ユーザー登録とユーザー編集画面のソースコード(一覧画面の説明は省略する):
<div class="container" th:if="${objUser}" th:object="${objUser}" >
<h2 th:if="${__function} eq 'new' ">ユーザー登録</h2>
<h2 th:if="${__function} eq 'edit' ">ユーザー編集</h2>
<div class="row form-row">
<div class="col-md-6">
<label for="userId">ユーザーID:</label>
<input type="text" id="userId" th:field="*{userID}" class="form-control" th:if="${__function} eq 'new' "/>
<input type="text" id="userId" th:field="*{userID}" class="form-control" readonly th:if="${__function} eq 'edit' " />
</div>
<div class="col-md-6">
<label for="username">ユーザー名:</label>
<input type="text" id="userName" th:field="*{userName}" class="form-control" />
</div>
</div>
<div class="row form-row">
<div class="col-md-6">
<label for="email">メールアドレス:</label>
<input type="text" id="email" th:field="*{email}" class="form-control" />
</div>
</div>
<div class="row form-row">
<div class="col-md-4">
</div>
<div class="col-md-4">
<button type="button" class="btn btn-primary registerUser-button" th:if="${__function} eq 'new' ">登録</button>
<button type="button" class="btn btn-primary updateUser-button" th:if="${__function} eq 'edit' ">編集</button>
<button type="button" class="btn btn-secondary selectUser-button">検索</button>
<button type="button" class="btn btn-secondary resetUser-button">リセット</button>
</div>
<div class="col-md-4">
</div>
</div>
</div>
th:if="${objUser}"
Thymeleafの構文であり、Controller側で objUser オブジェクトが設定されている場合、このエリアが表示されます。 Thymeleafは、条件が true の場合に要素を表示するための非常に便利な方法です。 Thymeleafを使用することで、動的なコンテンツを表示するHTMLテンプレートを簡単に作成できます。
th:if="${__function} eq 'new' "
th:if="${__function} eq 'edit' "
新規登録か編集かは __function の値で判断されます。
新規登録する際に、Controller側で __function を 'new' に設定します。
編集する際に、Controller側で __function を 'edit' に設定します。
設定された __function の値に応じて、新規登録または編集の動作が決まります。
<div class="col-md-6">
上記のコードは、Bootstrapのレイアウト用CSSです。col-md はブラウザの表示サイズが中(992ピクセル以上)の場合、全てのブラウザで同じ動作が期待されることを意味します。数字の 6 は、画面を12等分しているうちの6分(半分)を占有することを指します。つまり、画面の半分を使用するという意味です。
この設定により、大画面デバイスでは2つのコンテンツを並べて表示し、小さな画面デバイスでは一つのコンテンツが画面全体を占めるなど、レスポンシブなデザインを実現できます。
class="btn btn-primary registerUser-button"
btn btn-primary は Bootstrap のボタン用のCSSクラスです。一方、registerUser-button は jQuery のイベント処理用のクラスです。 Bootstrapの btn btn-primary クラスは、ボタンを強調表示するためのスタイルを提供し、registerUser-button クラスは特定のイベント処理に関連する要素を識別するために使用されます。
登録画面のイベント処理ソースコード(javascript):
// ......略......
$(document).ready(function() {
// ユーザー登録ボタンクリック時の処理
$('div').on('click', '.registerUser-button', function(event) {
// 登録処理を実行
var userId = $('#userId').val();
var username = $('#username').val();
var email = $('#email').val();
if (userId == '' || username == '' || email == '') {
alert('全てのフィールドを入力してください');
event.preventDefault(); // フォーム送信を中止
return;
} else {
registerUser();
}
});
// ......略......
});
function registerUser(){
$('#__function').val('insert');
$('form').submit();
}
// ......略......
$('div').on('click', '.registerUser-button', function(event)
このコードは、div 要素の下にある class="registerUser-button" の要素にクリックイベント処理を追加します。具体的には、【登録】ボタンにクリックイベント処理が追加されます。
$('form').submit();
【登録】ボタンをクリックすると、フォームデータが送信されるようになります。
コントローラー「UserController.java」の説明
登録処理の部分だけ説明します。
String __function = request.getParameter("__function");
// 略
if ("insert".equals(__function)) {
String userID = request.getParameter("userID");
String userName = request.getParameter("userName");
String email = request.getParameter("email");
User user = new User();
user.setUserID(userID);
user.setUserName(userName);
user.setEmail(email);
user.setRole("");
user.setPassword(userID);
user.setCreateUser("ADMIN");
user.setUpdateUser("ADMIN");
List<User> userList = userService.createAndGetUserList(user);
if(userList != null) {
model.addObject("listUser", userList);
} else {
model.addObject("errorMessage", "ユーザーの登録が失敗しました!");
}
}
// 略
String __function = request.getParameter("__function");
フォームから__functionの値を取得する。
List userList = userService.createAndGetUserList(user);
ユーザー登録サービスを呼び出す。
「UserService.java」の登録部分だけの説明
登録処理が行われます。具体的には、UserService.java は UserRepository の insert メソッドと selectByCondition メソッドを呼び出して、ユーザーの登録およびユーザー一覧の取得を実行します。
@Autowired
UserRepository userRepo;
/**
* ユーザーをInsert後、全てのユーザーを抽出する
* @param user Insertユーザー
* @return ユーザー一覧
*/
public List<User> createAndGetUserList(User user) {
if(userRepo.insert(user) == 1) {
user = new User();
return userRepo.selectByCondition(user);
}
return null;
}
モデルの「UserRepository.java」、「UserRepository.xml」、「User.java」の説明
「UserRepository.java」(インタフェース)の登録処理部分:
// ユーザー登録処理
int insert(@Param("user") User user);
// ユーザー一覧取得処理
List<User> selectByCondition(@Param("user") User user);
「UserRepository.xml」(DBとのやり取り)の登録処理部分:
注意すべきポイント:
- UserRepository.xml は Resources フォルダに配置し、UserRepository.java は java フォルダに配置してください。
- UserRepository.xml と UserRepository.java のフォルダ構造が同じであることを確認してください。
ユーザー登録処理(insert)部分
<insert id="insert" parameterType="com.example.demo.bean.User">
INSERT INTO USERS (USER_ID,USER_NAME,PASSWORD,EMAIL,ROLE,LAST_LOGIN_DATE,DEL_FLG,CREATE_DATE,CREATE_USER,UPDATE_DATE,UPDATE_USER)
VALUES (#{user.userID}, #{user.userName}, #{user.password}, #{user.email}, #{user.role}, null, '0', sysdate, #{user.createUser}, sysdate, #{user.updateUser})
</insert>
普通のinsert文を書けます、insert値にはBeanの項目を入れ替えて完成です。
ユーザー一覧取得処理(selectByCondition)部分
<resultMap id="ResultUserMap" type="com.example.demo.bean.User">
<id column="USER_ID" jdbcType="VARCHAR" property="userID" />
<result column="USER_NAME" jdbcType="VARCHAR" property="userName" />
<result column="PASSWORD" jdbcType="VARCHAR" property="password" />
<result column="EMAIL" jdbcType="VARCHAR" property="email" />
<result column="ROLE" jdbcType="VARCHAR" property="role" />
<result column="LAST_LOGIN_DATE" jdbcType="VARCHAR" property="lastLoginDate" />
<result column="DEL_FLG" jdbcType="VARCHAR" property="delFlg" />
<result column="DEL_FLG_TEXT" jdbcType="VARCHAR" property="delFlgText" />
<result column="CREATE_DATE" jdbcType="VARCHAR" property="createDate" />
<result column="CREATE_USER" jdbcType="VARCHAR" property="createUser" />
<result column="UPDATE_DATE" jdbcType="VARCHAR" property="updateDate" />
<result column="UPDATE_USER" jdbcType="VARCHAR" property="updateUser" />
</resultMap>
<select id="selectByCondition" parameterType="com.example.demo.bean.User" resultMap="ResultUserMap">
SELECT
USER_ID,
USER_NAME,
PASSWORD,
EMAIL,
ROLE,
TO_CHAR(LAST_LOGIN_DATE, 'YYYY/MM/DD HH24:MI:SS') LAST_LOGIN_DATE,
DEL_FLG,
DECODE(DEL_FLG, '1', 'Deleted', 'Active') DEL_FLG_TEXT,
TO_CHAR(CREATE_DATE, 'YYYY/MM/DD HH24:MI:SS') CREATE_DATE,
CREATE_USER,
TO_CHAR(UPDATE_DATE, 'YYYY/MM/DD HH24:MI:SS') UPDATE_DATE,
UPDATE_USER
FROM
USERS
WHERE 1 = 1
<if test="user.userName != null">
AND USER_NAME LIKE #{user.userName}
</if>
</select>
リターン値がListの場合、resultMapにリターンオブジェクトを定義する。
<id column="USER_ID" jdbcType="VARCHAR" property="userID" />
「column」はデータベースのカラムを指します。
「property」はJavaBeanの属性を指します。
ここではデータベースのカラムとJavaBeanの属性をマッピングすることができ、非常に便利ですね。
「User.java」、ユーザーBean
これだけです。
import lombok.Data;
@Data
public class User {
private String userID;
private String userName;
private String password;
private String email;
private String role;
private String lastLoginDate;
private String delFlg;
private String delFlgText;
private String createDate;
private String createUser;
private String updateDate;
private String updateUser;
}
実行&確認
ツールバーからアプリケーションを実行する。
Run -> demo - DemoApplicationをクリックする(一回実行したことがあればツールバーからDemoApplicationがでるが、実行したことなければ、メニューから実行するかDemoApplication.javaを右クリックして実行するか)

おわりに
全体のソースコードはGitHubにアップロードしましたので、ぜひダウンロードして確認してください。
また、この記事では一部のソースコードしか説明していませんが、説明を希望する特定の部分があればリクエストしていただければと思います。詳細や特定の質問についてお知らせいただければ、それに基づいて訂正や説明を提供できます。
次回はメニューを実装していきたいと思います、お楽しみして待ってください。
それではまた。


