Thymeleafでネストしたオブジェクトを扱う方法

こんにちは。ゆうせいです。

Javaを用いたWebアプリケーション開発において、オブジェクトの中に別のオブジェクトを持たせる構造は多用されます。例えば、ユーザーという情報のなかに、住所という独立した情報のまとまりが含まれるようなケースです。このような階層構造を持つデータを画面に表示する際、Thymeleafではドット(.)を用いて目的のデータへアクセスします。

データの階層構造を定義するモデルの作成

まず、プログラムで扱うデータの形を定義します。ここでは、住所を管理するAddressクラスと、そのAddressクラスを属性として持つUserクラスの2つを用意します。

オブジェクトの階層構造は、高校の部活動に例えると理解しやすくなります。学校という大きな枠組みの中に、野球部や吹奏楽部という部活動のオブジェクトが存在し、その部活動の中に部長や部員というデータが存在する状態と同じです。

Java

// Address.java
public class Address {
    private String zipCode;
    private String city;

    public Address(String zipCode, String city) {
        this.zipCode = zipCode;
        this.city = city;
    }
    public String getZipCode() { return zipCode; }
    public String getCity() { return city; }
}

// User.java
public class User {
    private String name;
    private Address address;

    public User(String name, Address address) {
        this.name = name;
        this.address = address;
    }
    public String getName() { return name; }
    public Address getAddress() { return address; }
}

データを画面へ渡すコントローラーの役割

作成したモデルに具体的な数値を代入し、画面を表示するためのテンプレートエンジンに渡します。コントローラーは、データベースやプログラム内部で生成したデータを、適切な画面(View)へと橋渡しする役割を担います。

以下のコードでは、3人のユーザーを作成しています。3人目のユーザーについては、住所情報を持たない(nullである)状態を想定して作成しています。

Java

@Controller
public class UserController {

    @GetMapping("/users")
    public String getUserList(Model model) {
        List<User> userList = new ArrayList<>();
        
        userList.add(new User("田中さん", new Address("123-4567", "東京都")));
        userList.add(new User("佐藤さん", new Address("987-6543", "大阪府")));
        userList.add(new User("鈴木さん", null));

        model.addAttribute("users", userList);
        return "user-list";
    }
}

Thymeleafによる階層データの表示と安全なアクセス

HTML側では、Thymeleafの変数式を用いてデータを取り出します。UserオブジェクトからAddressオブジェクトを経由して、最終的な都市名を取得するには、ドットを2つ繋げて記述します。

ここで重要になるのが、セーフナビゲーション演算子(?.)という記法です。これは、途中のデータが空(null)であっても、プログラムが停止してエラーになることを防ぐ仕組みです。

HTML

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>ユーザー一覧</title>
</head>
<body>
    <h1>ユーザー名と都市名の一覧</h1>
    <table border="1">
        <tr>
            <th>名前</th>
            <th>郵便番号</th>
            <th>都市</th>
        </tr>
        <tr th:each="user : ${users}">
            <td th:text="${user.name}">名前</td>
            <td th:text="${user.address?.zipCode}">000-0000</td>
            <td th:text="${user.address?.city}">未登録</td>
        </tr>
    </table>
</body>
</html>

階層構造を利用するメリットと注意点

オブジェクトをネストさせる手法には、事実として以下の特徴があります。

メリット

  • データの関連性が明確になる:ユーザーと住所を切り離して管理することで、他のプログラムでも住所クラスを再利用できます。
  • コードの可読性が向上する:一つのクラスに大量の変数を定義するよりも、役割ごとに分割されている方が構造を把握しやすくなります。

デメリット

  • NullPointerExceptionの発生リスク:データの階層が深くなるほど、途中のオブジェクトが空だった場合にエラーが発生しやすくなります。
  • テンプレートの記述が複雑になる:ドットを用いた参照が長くなると、HTML側の視認性が低下する傾向にあります。

まとめ

この記事では、ネストされたオブジェクトのデータをThymeleafで表示する方法を解説しました。学習を深めるためには、以下のステップを順に進めてください。

  1. クラスの中に別のクラスを定義する「関連」の概念を理解する。
  2. ドットを用いたメンバアクセスと、Thymeleafでの変数式の対応関係を確認する。
  3. セーフナビゲーション演算子を使用し、データが存在しない場合の例外処理を実装する。
  4. 3層以上の深い階層構造を作成し、同様の手順でデータを取得できるか試行する。

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。