ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
| この記事の要点 |
|
基本形
@Controller
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users/{id}")
public String show(@PathVariable Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
model.addAttribute("title", "ユーザー詳細");
return "users/show"; // templates/users/show.html
}
}
<!-- templates/users/show.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1 th:text="${title}">タイトル</h1>
<p th:text="${user.name}">名前</p>
<p th:text="|${user.name} さん(${user.age}歳)|"></p>
</body>
</html>
Model は引数に書くだけで Spring が渡してくれます。自分で new する必要はありません。
値の入れ方
// キーを指定する
model.addAttribute("user", user);
// キーを省略すると型名から自動で決まる
model.addAttribute(user); // キーは "user"
model.addAttribute(new ArrayList<User>()); // キーは "userList"
// まとめて入れる
Map<String, Object> attrs = Map.of("a", 1, "b", 2);
model.addAllAttributes(attrs);
// 確認・削除
model.containsAttribute("user");
model.asMap().remove("user");
自動で決まるキー名は分かりにくいので、明示的に文字列で指定するほうが読みやすくなります。
一覧を渡す
@GetMapping("/users")
public String index(@RequestParam(defaultValue = "1") int page, Model model) {
List<User> users = userService.findAll(page);
model.addAttribute("users", users);
model.addAttribute("page", page);
model.addAttribute("hasNext", users.size() == 20);
return "users/index";
}
<table>
<tr th:each="u, stat : ${users}">
<td th:text="${stat.count}">1</td>
<td th:text="${u.name}">名前</td>
<td><a th:href="@{/users/{id}(id=${u.id})}">詳細</a></td>
</tr>
</table>
<p th:if="${#lists.isEmpty(users)}">該当がありません</p>
<a th:if="${hasNext}" th:href="@{/users(page=${page + 1})}">次へ</a>
Model / ModelMap / ModelAndView
| 受け取り方 | ビュー名の指定 | 使いどころ | |
|---|---|---|---|
Model | 引数 | 戻り値の String | 基本はこれ |
ModelMap | 引数 | 戻り値の String | Map として扱いたいとき |
ModelAndView | 自分で new | オブジェクトの中で指定 | 条件でビューを切り替えるとき |
@GetMapping("/users/{id}")
public ModelAndView show(@PathVariable Long id) {
ModelAndView mav = new ModelAndView();
User user = userService.findById(id);
if (user == null) {
mav.setViewName("error/notfound");
return mav;
}
mav.addObject("user", user);
mav.setViewName("users/show");
return mav;
}
違いの詳細は ModelとModelAndViewの違い を参照してください。
リダイレクトすると Model は消える
@PostMapping("/users")
public String create(@Validated UserForm form, BindingResult result,
Model model, RedirectAttributes redirect) {
if (result.hasErrors()) {
model.addAttribute("form", form); // 同じリクエスト内なので届く
return "users/new";
}
User saved = userService.save(form);
// これは届かない(別のリクエストになるため)
// model.addAttribute("message", "登録しました");
// フラッシュ属性なら 1 回だけ次の画面に届く
redirect.addFlashAttribute("message", "登録しました");
// URL のクエリとして渡す(画面をブックマークできる)
redirect.addAttribute("id", saved.getId());
return "redirect:/users/{id}"; // /users/1 に転送される
}
| メソッド | どう渡るか | URL に出るか |
|---|---|---|
addFlashAttribute() | セッション経由で 1 回だけ | 出ない |
addAttribute() | クエリ文字列 / パス変数 | 出る |
「登録しました」のような一度きりのメッセージはフラッシュ属性、再表示できるべき条件(検索語・ページ番号)は URL に載せる、と使い分けます。
全画面に共通の値を渡す
@ControllerAdvice
public class GlobalModelAdvice {
@ModelAttribute("appName")
public String appName() {
return "サンプルアプリ";
}
@ModelAttribute("loginUser")
public User loginUser(HttpSession session) {
return (User) session.getAttribute("user");
}
}
@ControllerAdvice に @ModelAttribute を書くと、すべてのコントローラーの Model に自動で追加されます。ヘッダーに出すログインユーザー名などに使えます。
REST API では Model を使わない
@RestController // @Controller + @ResponseBody
@RequestMapping("/api/users")
public class UserApiController {
@GetMapping("/{id}")
public User show(@PathVariable Long id) {
return userService.findById(id); // JSON になって返る
}
@GetMapping
public ResponseEntity<List<User>> index() {
return ResponseEntity.ok(userService.findAll());
}
}
@RestController では戻り値がそのまま JSON に変換されます。ビュー名としては解釈されません。
@ModelAttribute でフォームと結び付ける
public class UserForm {
@NotBlank(message = "名前は必須です")
private String name;
@Min(value = 0, message = "0 以上を入力してください")
private int age;
// ゲッター・セッターが必要(Thymeleaf と Spring がこれを使う)
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
@Controller
@RequestMapping("/users")
public class UserController {
// 入力画面: 空のフォームを Model に入れる
@GetMapping("/new")
public String newForm(Model model) {
model.addAttribute("userForm", new UserForm());
return "users/new";
}
// 登録: 引数に書くだけで Model にも自動で入る
@PostMapping
public String create(@Validated @ModelAttribute UserForm userForm,
BindingResult result,
RedirectAttributes redirect) {
if (result.hasErrors()) {
return "users/new"; // userForm は Model に入ったままなので再表示できる
}
userService.save(userForm);
redirect.addFlashAttribute("message", "登録しました");
return "redirect:/users";
}
}
@ModelAttribute を付けた引数は、自動的に Model にも追加されます。キー名は型名の先頭を小文字にしたもの(UserForm → userForm)です。エラーで入力画面に戻したとき、入力値がそのまま残るのはこの仕組みによります。
<form th:action="@{/users}" th:object="${userForm}" method="post">
<input type="text" th:field="*{name}">
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
<input type="number" th:field="*{age}">
<button type="submit">登録</button>
</form>
th:field は id name value を自動で埋めます。BindingResult は検証したい引数の直後に置く必要があります。間に別の引数を挟むと例外になります。
値の変換とフォーマット
public class SearchForm {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate from;
@NumberFormat(pattern = "#,###")
private Integer amount;
}
<!-- 表示側でも整形できる -->
<span th:text="${#temporals.format(user.createdAt, 'yyyy年M月d日')}"></span>
<span th:text="${#numbers.formatInteger(price, 1, 'COMMA')}"></span>
@DateTimeFormat が無いと、2026-09-07 のような文字列を LocalDate に変換できず Failed to convert エラーになります。
うまく表示されないとき
| 症状 | 原因 |
|---|---|
| 画面に値が出ない | キー名の綴り違い。${user.name} の user が Model のキーと一致しているか |
Whitelabel Error Page | ビューが見つからない。templates/ 配下のパスを確認する |
| ビュー名がそのまま表示される | @RestController になっている。@Controller にする |
EL1007E: Property or field 'name' cannot be found on null | Model に入れていない/値が null。th:if で囲む |
| ゲッターが無くて読めない | Thymeleaf はゲッター経由で読む。record かゲッターを用意する |
| リダイレクト後に値が消える | RedirectAttributes を使う |
関連
- Spring Framework — 親カテゴリ
- ビューから値をモデルに格納しコントローラーで受け取る方法
- ModelとModelAndViewの違い
- テンプレートエンジン
- @Controller
- @RequestMapping
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
- インストール(eclipseプラグイン)
- クイックスタート
- プロジェクトの作成
- Spring Bootプロジェクトの作成
- Spring Bootプロジェクトの実行
- Spring BootでHello World!
- アノテーション一覧
- DB接続設定からエンティティおよびリポジトリの作成、値の取得まで(JPA編)
- DB接続設定や値の取得(JdbcTemplate編)
- ビューから値をモデルに格納しコントローラーで受け取る方法
- コントローラーにてモデルに値を格納してビューに渡す方法
- テンプレートエンジン
- ModelとModelAndViewの違い
- AOPの使用方法
- classpath: 内部ファイルの読み込み
- file: 外部ファイルの読み込み
- CSVファイルアップロード方法(Ajax)
- CSVファイルダウンロード方法(Ajax)
- Spring Bootプロジェクトのビルドと本番環境へのデプロイ方法(内部tomcat使用)
- Application.propertiesの環境依存設定の分割方法
- JPAにおけるEntityManagerの取得方法
- JPAにおけるjava.sql.Connectionの取得方法
- エラー一覧
- jarの引数を受け取る方法
- Spring BootでGmailからメール送信
- 複数のDBに接続する設定(Spring Boot & JPA編)
- ポート番号の変更
- Basic認証の実装と特定のURLに限定する方法
- Spring SecurityのBasic認証の無効化
- 独自のエラーページを定義する方法
- プロパティファイルの値やjar実行時の引数を取得する方法
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- 【django】ログイン 認証機能 2026-09-08 03:22:09
- MySQL ERROR 1063 Incorrect column specifier for column|原因と直し方 2026-09-08 03:11:09
- 【PHPエラー】Object of class stdClass could not be converted to string 2026-09-07 07:46:04
- Julia Genie ローカル開発サーバ起動完全ガイド 2026-09-07 07:46:04
- Wi-Fi とは|規格と世代(Wi-Fi 4〜7)・周波数帯・CSMA/CA・WPA3 NEW 2026-09-07 07:45:13
- set コマンドでシェルオプションと位置パラメータを操作 | bash 2026-09-07 07:45:13
- JPEG(.jpg/.jpeg)画像形式の完全ガイド — 仕様・マジックナンバー・他形式との比較・EXIF 2026-09-07 07:45:13
- UE5 Get Overlapping Actorsで特定クラスだけ処理|Class Filter・Cast To 2026-09-07 07:45:13
- Linuxで特定拡張子のファイルを再帰削除|find -deleteの安全手順 2026-09-07 07:45:13
- Django CBVでテンプレートに値を渡す|get_context_data・extra_context 2026-09-07 07:45:13
- Linuxで複数ファイルの文字列を一括置換|find・sed・xargsの安全手順 2026-09-07 07:45:13
- Laravelルートグループ|prefix・middleware・nameで一括管理 2026-09-07 07:45:13
- Linux whichコマンドの使い方|絶対パス取得とtype・command -vの違い 2026-09-07 07:45:13
- Linux catコマンドの使い方|行番号表示・複数ファイル連結 2026-09-07 07:45:13
- unable to execute 'gcc': No such file or directory 2026-09-07 07:45:13
コメントを削除してもよろしいでしょうか?