4.9. コードリスト


4.9.1. Overview

コードリストとは、「コード値(value)とその表示名(label)」の集合である。

画面のセレクトボックスなどコード値を画面で表示する際のラベルへのマッピング表として利用される。

共通ライブラリでは、

  • xmlファイルやDBに定義されたコードリストをアプリケーション起動時に読み込みキャッシュする機能

  • JSPやThymeleafのテンプレートHTML、Javaクラスからコードリストを参照する機能

  • コードリストを用いて入力チェックする機能

を提供している。

また、応用的な使い方として、

  • コードリストの国際化対応

  • キャッシュされたコードリストのリロード

もサポートしている。

Note

標準でリロードが可能なのは、DBに定義されたコードリストを使用する場合のみである。


共通ライブラリでは、以下のコードリスト実装クラスを提供している。

コードリスト種類一覧

種類

内容

Reloadable

org.terasoluna.gfw.common.codelist.SimpleMapCodeList

xmlファイルに直接記述した内容を使用する。

NO

org.terasoluna.gfw.common.codelist.NumberRangeCodeList

数値の範囲のリストを作成する際に使用する。

NO

org.terasoluna.gfw.common.codelist.JdbcCodeList

DBから対象のコードをSQLで取得して使用する。

YES

org.terasoluna.gfw.common.codelist.EnumCodeList

Enumクラスに定義した定数からコードリストを作成する際に使用する。

NO

org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeList

国際化に対応し、java.util.Localeに応じたコードリストを使用する。

NO

org.terasoluna.gfw.common.codelist.i18n.SimpleReloadableI18nCodeList

国際化に対応し、java.util.Localeに応じた更新可能なコードリストを使用する。(5.4.2から追加)

YES

上記コードリストのインタフェースについて、共通ライブラリにorg.terasoluna.gfw.common.codelist.CodeListを提供している。
また国際化に対応しているコードリストのインタフェースについて、org.terasoluna.gfw.common.codelist.i18n.I18nCodeListを提供している。

共通ライブラリで提供しているコードリストのクラス図構成を以下に示す。

codelist class diagram

Picture - Image of codelist class diagram


4.9.2. How to use

本項では、各種コードリストを使用する上での設定や実装方法を記述する。


4.9.2.1. SimpleMapCodeListの使用方法

org.terasoluna.gfw.common.codelist.SimpleMapCodeListとは、 xmlファイルに定義したコード値をアプリケーション起動時に読み込み、そのまま使用するコードリストである。

SimpleMapCodeListのイメージ

codelist simple

4.9.2.1.1. コードリスト設定例

bean定義ファイルの定義

bean定義ファイルは、コードリスト用に作成することを推奨する。

  • XxxCodelistConfig.java

    @Bean("CL_ORDERSTATUS") // (1)
    public SimpleMapCodeList clOrderstatus() {
        Map<String, String> codeMap = new LinkedHashMap<>();
        codeMap.put("1", "Received"); // (2)
        codeMap.put("2", "Sent"); // (2)
        codeMap.put("3", "Cancelled"); // (2)
        SimpleMapCodeList bean = new SimpleMapCodeList();
        bean.setMap(codeMap);
        return bean;
    }
    

    項番

    説明

    (1)
    SimpleMapCodeListクラスをbean定義する。
    beanIDは、後述するorg.terasoluna.gfw.web.codelist.CodeListInterceptorのIDパターンに合致する名称にすること。
    (2)
    Mapの Key、Valueを定義する。
    上記例ではjava.util.LinkedHashMapで登録しているため、「名前と値」が登録順にMapへ保持される。

コードリスト用bean定義ファイルを作成後、既存bean定義ファイルにimportを行う必要がある。

  • XxxDomainConfig.java

    @Configuration
    @ComponentScan(basePackages = { "com.example.domain" })
    @Import({ XxxCodelistConfig.class }) // (3)
    public class XxxDomainConfig {
    

    項番

    説明

    (3)
    コードリスト用bean定義ファイルをimportする。

4.9.2.1.2. 共通ライブラリから提供しているインタセプターの使用

共通ライブラリから提供しているインタセプターを用いることで、リクエストスコープに自動的に設定し、JSP/テンプレートHTMLからコードリストを容易に参照できる。

bean定義ファイルの定義

  • SpringMvc.java

    @EnableAspectJAutoProxy
    @EnableWebMvc
    @Configuration
    public class SpringMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(codeListInterceptor())
                    .addPathPatterns("/**"); // (1)
        }
    
        // (2)
        @Bean
        public CodeListInterceptor codeListInterceptor() {
            CodeListInterceptor codeListInterceptor = new CodeListInterceptor();
            codeListInterceptor.setCodeListIdPattern(Pattern.compile("CL_.+")); // (3)
            return codeListInterceptor;
        }
    

    項番

    説明

    (1)
    適用対象のパスを設定する。
    (2)
    CodeListInterceptor クラスをbean定義する。
    (3)
    自動でリクエストスコープに設定する、コードリストのbeanIDのパターンを設定する。
    パターンにはjava.util.regex.Patternで使用する正規表現を設定すること。
    上記例では、idが”CL_XXX”形式で定義されているデータのみを対象とする。その場合、idが”CL_”で始まらないbean定義は取り込まれない。
    “CL_”で定義したbeanIDは、リクエストスコープに設定されるため、JSPで容易に参照できる。

    codeListIdPatternプロパティは省略可能である。
    codeListIdPatternを省略した場合は、すべてのコードリスト(org.terasoluna.gfw.common.codelist.CodeListインタフェースを実装しているbean)がリクエストスコープに設定される。

Warning

例外発生時のコードリスト利用について

terasoluna-gfw-common 5.4.2.RELEASEより、Controllerのハンドラメソッドで例外が発生し @ExceptionHandlerSystemExceptionResolverで例外ハンドリングを行なった場合は、コードリストがリクエストスコープに登録されなくなった。

これは、 CodeListInterceptorHandlerInterceptor#postHandleメソッドでコードリストの登録を行うように変更されたためである。

例外時に遷移する画面でコードリストを利用したい場合は、ハンドラメソッドで例外を捕捉(try-catch)するか、JSP/テンプレートHTMLから直接コードリストBeanを参照するを利用してコードリストを取得することを検討されたい。

例外ハンドリングの方法については、例外のハンドリング方法を参照されたい。


jspの実装例

<form:select path="orderStatus">
    <form:option value="" label="--Select--" /> <!-- (4) -->
    <form:options items="${CL_ORDERSTATUS}" /> <!-- (5) -->
</form:select>

項番

説明

(4)
セレクトボックスの先頭にダミーの値を設定する場合、valueに空文字を指定すること。
(5)
コードリストを定義したbeanIDを指定する。

出力HTML

<select id="orderStatus" name="orderStatus">
    <option value="">--Select--</option>
    <option value="1">Received</option>
    <option value="2">Sent</option>
    <option value="3">Cancelled</option>
</select>

出力画面

codelist selectbox

4.9.2.1.3. Javaクラスでのコードリスト使用

Javaクラスでコードリストを利用する場合、jakarta.inject.Injectアノテーションと、jakarta.inject.Namedアノテーションを設定してコードリストをインジェクションする。
@Namedにコードリスト名を指定する。
import org.springframework.stereotype.Service;
import org.terasoluna.gfw.common.codelist.CodeList;

import jakarta.inject.Inject;
import jakarta.inject.Named;

@Service
public class OrderServiceImpl implements OrderService {

    @Inject
    @Named("CL_ORDERSTATUS")
    CodeList orderStatusCodeList; // (1)

    public boolean existOrderStatus(String target) {
        return orderStatusCodeList.asMap().containsKey(target); // (2)
    }
}

項番

説明

(1)
beanIDが、”CL_ORDERSTATUS”であるコードリストをインジェクションする。
(2)
CodeList#asMapメソッドでコードリストをjava.util.Map形式で取得する。

4.9.2.2. NumberRangeCodeListの使用方法

org.terasoluna.gfw.common.codelist.NumberRangeCodeListとは、アプリケーション起動時に、指定した数値の範囲をリストにするコードリストである。
主に数だけのセレクトボックス、月や日付などのセレクトボックスに使用することを想定している。

NumberRangeCodeListのイメージ

codelist number

Tip

NumberRangeCodeListはアラビア数字のみ対応しており、漢数字やローマ数字には対応していない。

漢数字やローマ数字を表示したい場合はJdbcCodeList、SimpleMapCodeListに定義することで対応可能である。


NumberRangeCodeListには、以下の特徴がある。

  1. Fromの値をToの値より小さくする場合、昇順にinterval分増加した値をFrom~Toの範囲分リストにする。

  2. Toの値をFromの値より小さくする場合、降順にinterval分減少した値をTo~Fromの範囲分リストにする。

  3. 増加分(減少分)はintervalを設定することで変更できる。


ここでは、昇順のNumberRangeCodeListについて説明をする。 降順のNumberRangeCodeListとインターバルの変更方法については、「NumberRangeCodeListのバリエーション」を参照されたい。


4.9.2.2.1. コードリスト設定例

Fromの値をToの値より小さくする(From < To)場合の実装例を、以下に示す。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_MONTH")
    public NumberRangeCodeList clMonthAsc() {
        NumberRangeCodeList bean = new NumberRangeCodeList(); // (1)
        bean.setFrom(1); // (2)
        bean.setTo(12); // (3)
        bean.setValueFormat("%d"); // (4)
        bean.setLabelFormat("%02d"); // (5)
        bean.setInterval(1); // (6)
        return bean;
    }
    

    項番

    説明

    (1)
    NumberRangeCodeListをbean定義する。
    (2)
    範囲開始の値を指定する。省略した場合、”0”が設定される。
    (3)
    範囲終了の値を設定する。指定必須。
    (4)
    コード値のフォーマット形式を設定する。フォーマット形式はjava.lang.String.formatの形式が使用される。
    省略した場合、”%s”が設定される。
    (5)
    コード名のフォーマット形式を設定する。フォーマット形式はjava.lang.String.formatの形式が使用される。
    省略した場合、”%s”が設定される。
    (6)
    増加する値を設定する。省略した場合、”1”が設定される。

4.9.2.2.2. コードリストの使用

設定例の詳細は、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。

jspの実装例

<form:select path="depMonth" items="${CL_MONTH}" />

出力HTML

<select id="depMonth" name="depMonth">
    <option value="1">01</option>
    <option value="2">02</option>
    <option value="3">03</option>
    <option value="4">04</option>
    <option value="5">05</option>
    <option value="6">06</option>
    <option value="7">07</option>
    <option value="8">08</option>
    <option value="9">09</option>
    <option value="10">10</option>
    <option value="11">11</option>
    <option value="12">12</option>
</select>

出力画面

codelist numberrenge

4.9.2.2.3. Javaクラスでのコードリスト使用

設定例の詳細は、前述したJavaクラスでのコードリスト使用を参照されたい。


4.9.2.3. JdbcCodeListの使用方法

org.terasoluna.gfw.common.codelist.JdbcCodeListとは、アプリケーション起動時にDBから値を取得し、コードリストを作成するクラスである。
JdbcCodeListはアプリケーション起動時にキャッシュを作るので、リスト表示時はDBアクセスによる遅延がない。
起動時の読み込み時間を抑えたいならば、取得数の上限を設定するとよい。
JdbcCodeListにはorg.springframework.jdbc.core.JdbcTemplateを設定するフィールドがある。
JdbcTemplatefetchSizeに上限を設定すれば、その分だけのレコードが起動時に読み込まれる。
なお、取得する値はリロードにより動的に変更できる。詳細はコードリストをリロードする場合参照されたい。

JdbcCodeListのイメージ

codelist simple

4.9.2.3.1. コードリスト設定例

テーブル(authority)の定義

authority_id

authority_name

01
STAFF_MANAGEMENT
02
MASTER_MANAGEMENT
03
STOCK_MANAGEMENT
04
ORDER_MANAGEMENT
05
SHOW_SHOPPING_CENTER

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Value("${codelist.jdbc.fetchSize:1000}")
    private Integer fetchSize;
    
    @Bean("jdbcTemplateForCodeList")
    public JdbcTemplate jdbcTemplateForCodeList() {
        JdbcTemplate bean = new JdbcTemplate(); // (1)
        bean.setDataSource(dataSource);
        bean.setFetchSize(fetchSize); // (2)
        return bean;
    }
    
    // (3)
    private JdbcCodeList abstractJdbcCodeList() {
        JdbcCodeList bean = new JdbcCodeList();
        bean.setJdbcTemplate(jdbcTemplateForCodeList()); // (4)
        return bean;
    }
    
    @Bean("CL_AUTHORITIES")
    public JdbcCodeList clAuthorities() {
        JdbcCodeList jdbcCodeList = abstractJdbcCodeList(); // (5)
        jdbcCodeList.setQuerySql("SELECT authority_id, authority_name FROM t_authority ORDER BY authority_id"); // (6)
        jdbcCodeList.setValueColumn("authority_id"); // (7)
        jdbcCodeList.setLabelColumn("authority_name"); // (8)
        return jdbcCodeList;
    }
    

    項番

    説明

    (1)
    org.springframework.jdbc.core.JdbcTemplateクラスをbean定義する。
    独自にfetchSizeを設定するために必要となる。
    (2)
    fetchSizeを設定する。
    fetchSizeのデフォルト設定が、全件取得になっている場合があるため適切な値を設定すること。
    fetchSizeの設定が全件取得のままだと、JdbcCodeListの読み込む件数が大きい場合に、DBからリストを取得する際の処理性能が落ちてしまい、アプリケーションの起動時間が長期化する可能性がある。
    (3)
    JdbcCodeListの共通bean定義。
    他のJdbcCodeListの共通部分を設定している。そのため、基本JdbcCodeListのbean定義は当メソッドを呼び出す。
    (4)
    (1)で設定したjdbcTemplateを設定。
    fetchSizeを設定したJdbcTemplateを、JdbcCodeListに格納している。
    (5)
    JdbcCodeListのbean定義。
    parent属性を(3)のbean定義を親クラスとして設定することで、fetchSizeを設定したJdbcCodeListが設定される。
    このbean定義では、クエリに関する設定のみを行い、必要なCodeList分作成する。
    (6)
    querySqlプロパティに取得するSQLを記述する。その際、必ず「ORDER BY」を指定し、順序を確定させること。
    「ORDER BY」を指定しないと、取得する度に順序が変わってしまう。
    (7)
    valueColumnプロパティに、MapのKeyに該当する値を設定する。この例ではauthority_idを設定している。
    (8)
    labelColumnプロパティに、MapのValueに該当する値を設定する。この例ではauthority_nameを設定している。

4.9.2.3.2. コードリストの使用

下記に示す設定の詳細について、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。

jspの実装例

<form:checkboxes items="${CL_AUTHORITIES}"/>

出力HTML

<span>
    <input id="authorities1" name="authorities" type="checkbox" value="01"/>
    <label for="authorities1">STAFF_MANAGEMENT</label>
</span>
<span>
    <input id="authorities2" name="authorities" type="checkbox" value="02"/>
    <label for="authorities2">MASTER_MANAGEMENT</label>
</span>
<span>
    <input id="authorities3" name="authorities" type="checkbox" value="03"/>
    <label for="authorities3">STOCK_MANAGEMENT</label>
</span>
<span>
    <input id="authorities4" name="authorities" type="checkbox" value="04"/>
    <label for="authorities4">ORDER_MANAGEMENT</label>
</span>
<span>
    <input id="authorities5" name="authorities" type="checkbox" value="05"/>
    <label for="authorities5">SHOW_SHOPPING_CENTER</label>
</span>

出力画面

codelist checkbox

4.9.2.3.3. Javaクラスでのコードリスト使用

下記に示す設定の詳細について、前述したJavaクラスでのコードリスト使用を参照されたい。


4.9.2.4. EnumCodeListの使用方法

org.terasoluna.gfw.common.codelist.EnumCodeListは、 Enumクラスに定義した定数からコードリストを作成するクラスである。

Note

以下の条件に一致するアプリケーションでコードリストを扱う場合は、EnumCodeListを使用して、コードリストのラベルをEnumクラスで管理することを検討してほしい。

コードリストのラベルをEnumクラスで管理することで、コード値に紐づく情報と操作をEnumクラスに集約する事ができる。

  • コード値をEnumクラスで管理する必要がある(つまり、Javaのロジックでコード値を意識した処理を行う必要がある)

  • UIの国際化(多言語化)の必要がない


以下に、EnumCodeListの使用イメージを示す。

codelist enum

Note

EnumCodeListでは、Enumクラスからコードリストを作成するために必要な情報(コード値とラベル)を取得するためのインタフェースとして、org.terasoluna.gfw.common.codelist.EnumCodeList.CodeListItemインタフェースを提供している。

EnumCodeListを使用する場合は、作成するEnumクラスでEnumCodeList.CodeListItemインタフェースを実装する必要がある。


4.9.2.4.1. コードリスト設定例

Enumクラスの作成

EnumCodeListを使用する場合は、EnumCodeList.CodeListItemインタフェースを実装したEnumクラスを作成する。 以下に作成例を示す。

package com.example.domain.model;

import org.terasoluna.gfw.common.codelist.EnumCodeList;

public enum OrderStatus
    // (1)
    implements EnumCodeList.CodeListItem {

    // (2)
    RECEIVED  ("1", "Received"),
    SENT      ("2", "Sent"),
    CANCELLED ("3","Cancelled");

    // (3)
    private final String value;
    private final String label;

    // (4)
    private OrderStatus(String codeValue, String codeLabel) {
        this.value = codeValue;
        this.label = codeLabel;
    }

    // (5)
    @Override
    public String getCodeValue() {
        return value;
    }

    // (6)
    @Override
    public String getCodeLabel() {
        return label;
    }

}

項番

説明

(1)

コードリストとして使用するEnumクラスでは、 共通ライブラリから提供しているorg.terasoluna.gfw.common.codelist.EnumCodeList.CodeListItemインタフェースを実装する。

EnumCodeList.CodeListItemインタフェースには、コードリストを作成するために必要な情報(コード値とラベル)を取得するためのメソッドとして、

  • コード値を取得するgetCodeValueメソッド

  • ラベルを取得するgetCodeLabelメソッド

が定義されている。

(2)

定数を定義する。

定数を生成する際に、コードリストを作成するために必要な情報(コード値とラベル)を指定する。

上記例では、以下の3つの定数を定義している。

  • RECEIVED(コード値=”1“, ラベル=Received)

  • SENT (コード値=”2“, ラベル=Sent)

  • CANCELLED (コード値=”3“, ラベル=Cancelled)

Note

EnumCodeListを使用した際のコードリストの並び順は、定数の定義順となる。

(3)

コードリストを作成するために必要な情報(コード値とラベル)を保持するプロパティを用意する。

(4)

コードリストを作成するために必要な情報(コード値とラベル)を受け取るコンストラクタを用意する。

(5)

定数が保持するコード値を返却する。

このメソッドは、EnumCodeList.CodeListItemインタフェースで定義されているメソッドであり、 EnumCodeListが定数からコード値を取得する際に呼び出す。

(6)

定数が保持するラベルを返却する。

このメソッドは、EnumCodeList.CodeListItemインタフェースで定義されているメソッドであり、 EnumCodeListが定数からラベルを取得する際に呼び出す。


bean定義ファイルの定義

コードリスト用のbean定義ファイルに、EnumCodeListを定義する。
以下に定義例を示す。
  • XxxCodelist.java

    @Bean("CL_ORDERSTATUS")
    public EnumCodeList clEnumOrderstatus() {
        return new EnumCodeList(OrderStatus.class); // (7)(8)
    }
    

    項番

    説明

    (7)

    コードリストの実装クラスとして、EnumCodeListクラスを指定する。

    (8)

    EnumCodeListクラスのコンストラクタに、EnumCodeList.CodeListItemインタフェースを実装したEnumクラスを指定する。


4.9.2.4.2. コードリストの使用

JSP/テンプレートHTMLでコードリストを使用する方法については、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。


4.9.2.4.3. Javaクラスでのコードリスト使用

Javaクラスでコードリストを使用する方法については、前述したJavaクラスでのコードリスト使用を参照されたい。


4.9.2.5. I18nCodeListの使用方法

org.terasoluna.gfw.common.codelist.i18n.I18nCodeListは、国際化に対応しているコードリストである。
ロケール毎にコードリストを設定することで、ロケールに対応したコードリストを返却できる。

I18nCodeListの実装クラスとして、org.terasoluna.gfw.common.codelist.i18n.SimpleI18nCodeListおよびorg.terasoluna.gfw.common.codelist.i18n.SimpleReloadableI18nCodeListを提供している。

I18nCodeList(SimpleI18nCodeList)のイメージ

codelist i18n

4.9.2.5.1. コードリスト設定例

I18nCodeListは行がLocale、列がコード値、セルの内容がラベルである2次元のテーブルをイメージすると理解しやすい。

料金を選択するセレクトボックスの場合を例に挙げると以下のようなテーブルができる。

row=Locale,column=Code

0

10000

20000

30000

40000

50000

en

unlimited

Less than \10,000

Less than \20,000

Less than \30,000

Less than \40,000

Less than \50,000

ja

上限なし

10,000円以下

20,000円以下

30,000円以下

40,000円以下

50,000円以下

この国際化対応コードリストのテーブルを構築するためにSimpleI18nCodeListは3つの設定方法を用意している。

  • 行単位でLocale毎のCodeListを設定する

  • 行単位でLocale毎のjava.util.Map(key=コード値, value=ラベル)を設定する

  • 列単位でコード値毎のjava.util.Map(key=Locale, value=ラベル)を設定する

基本的には、「行単位でLocale毎のCodeListを設定する」方法でコードリストを設定することを推奨する。

SimpleReloadableI18nCodeListは更新可能なコードリストを行に持つ以下の設定方法を用意している。

  • 行単位でLocale毎のReloadableCodeListJdbcCodeList)を設定する

Note

terasoluna-gfw-common 5.4.2.RELEASEからリロードに対応したSimpleReloadableI18nCodeListが追加された。

更新可能なコードリストを行に持つSimpleI18nCodeListを利用している場合は、SimpleReloadableI18nCodeListに置き換えることを推奨する。

上記例の料金を選択するセレクトボックスの場合を行単位でLocale毎のCodeListを設定する方法について説明する。
他の設定方法については SimpleI18nCodeListのコードリスト設定方法参照されたい。

Bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_I18N_PRICE")
    public SimpleI18nCodeList clI18nPrice() {
        Map<Locale, CodeList> rows =  new LinkedHashMap<>(); // (1)
        rows.put(Locale.ENGLISH, clPriceEn());
        rows.put(Locale.JAPANESE, clPriceJa());
        SimpleI18nCodeList bean = new SimpleI18nCodeList();
        bean.setRowsByCodeList(rows);
        return bean;
    }
    

    項番

    説明

    (1)
    rowsByCodeListプロパティにkeyがjava.lang.LocaleのMapを設定する。
    Mapには、keyにロケール、value-refにロケールに対応したコードリストクラスの参照先を指定する。
    Mapのvalueは各ロケールに対応したコードリストクラスを参照する。

Locale毎にSimpleMapCodeListを用意する場合のBean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_I18N_PRICE")
    public SimpleI18nCodeList clI18nPrice() {
        Map<Locale, CodeList> rows =  new LinkedHashMap<>();
        rows.put(Locale.ENGLISH, clPriceEn());
        rows.put(Locale.JAPANESE, clPriceJa());
        SimpleI18nCodeList bean = new SimpleI18nCodeList();
        bean.setRowsByCodeList(rows);
        return bean;
    }
    
    // (2)
    @Bean("CL_PRICE_EN")
    public SimpleMapCodeList clPriceEn() {
        Map<String, String> enMap = new LinkedHashMap<>();
        enMap.put("0", "unlimited");
        enMap.put("10000", "Less than \\10,000");
        enMap.put("20000", "Less than \\20,000");
        enMap.put("30000", "Less than \\30,000");
        enMap.put("40000", "Less than \\40,000");
        enMap.put("50000", "Less than \\50,000");
        SimpleMapCodeList bean = new SimpleMapCodeList();
        bean.setMap(enMap);
        return bean;
    }
    
    // (3)
    @Bean("CL_PRICE_JA")
    public SimpleMapCodeList clPriceJa() {
        Map<String, String> jaMap = new LinkedHashMap<>();
        jaMap.put("0", "上限なし");
        jaMap.put("10000", "10,000円以下");
        jaMap.put("20000", "20,000円以下");
        jaMap.put("30000", "30,000円以下");
        jaMap.put("40000", "40,000円以下");
        jaMap.put("50000", "50,000円以下");
        SimpleMapCodeList bean = new SimpleMapCodeList();
        bean.setMap(jaMap);
        return bean;
    }
    

    項番

    説明

    (2)
    ロケールが”en”であるbean定義CL_PRICE_ENについて、コードリストクラスをSimpleMapCodeListで設定している。
    (3)
    ロケールが”ja”であるbean定義CL_PRICE_JAについて、コードリストクラスをSimpleMapCodeListで設定している。

Locale毎にJdbcCodeListを用意する場合のBean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_I18N_PRICE")
    public SimpleReloadableI18nCodeList clI18nPrice() {
        Map<Locale, CodeList> rows =  new LinkedHashMap<>();
        rows.put(Locale.ENGLISH, clPriceEn());
        rows.put(Locale.JAPANESE, clPriceJa());
        SimpleReloadableI18nCodeList bean = new SimpleReloadableI18nCodeList(); // (4)
        bean.setRowsByCodeList(rows);
        return bean;
    }
    
    @Bean("CL_PRICE_EN")
    public JdbcCodeList clPriceEn() {
        JdbcCodeList jdbcCodeList = abstractJdbcCodeList(); // (5)
        jdbcCodeList.setQuerySql("SELECT code, label FROM price WHERE locale = 'en' ORDER BY code");
        jdbcCodeList.setValueColumn("code");
        jdbcCodeList.setLabelColumn("label");
        return jdbcCodeList;
    }
    
    @Bean("CL_PRICE_JA")
    public JdbcCodeList clPriceJa() {
        JdbcCodeList jdbcCodeList = abstractJdbcCodeList(); // (6)
        jdbcCodeList.setQuerySql("SELECT code, label FROM price WHERE locale = 'ja' ORDER BY code");
        jdbcCodeList.setValueColumn("code");
        jdbcCodeList.setLabelColumn("label");
        return jdbcCodeList;
    }
    

    項番

    説明

    (4)
    更新可能なコードリストを行に持つ場合は、SimpleReloadableI18nCodeListを利用する。
    (5)
    ロケールが”en”であるbean定義CL_PRICE_ENについて、コードリストクラスをJdbcCodeListで設定している。
    (6)
    ロケールが”ja”であるbean定義CL_PRICE_JAについて、コードリストクラスをJdbcCodeListで設定している。

テーブル定義(priceテーブル)には以下のデータを投入する。

locale

code

label

en
0
unlimited
en
10000
Less than \10,000
en
20000
Less than \20,000
en
30000
Less than \30,000
en
40000
Less than \40,000
en
50000
Less than \50,000
ja
0
上限なし
ja
10000
10,000円以下
ja
20000
20,000円以下
ja
30000
30,000円以下
ja
40000
40,000円以下
ja
50000
50,000円以下

4.9.2.5.2. I18nCodeListにおけるロケール解決

I18nCodeListは要求されたロケールがコードリストに定義されていない場合、以下の順序でロケールの解決を行う。

  1. 国と言語を組み合わせたロケール(例:ja_JP)がコードリストに定義されていない場合、対応する言語のみのロケール(例:ja)を使用する。

  2. 言語のみのロケールがコードリストに定義されていない場合、デフォルトのロケールを使用する。

デフォルトのロケールは以下の順序で決定する。

  1. fallbackToプロパティが指定されている場合は、指定されたロケールを使用する。

  2. fallbackToプロパティが指定されていない場合は、JVMインスタンスのデフォルトロケール 、もしくは対応する言語のみのロケールを使用する。

Warning

デフォルトのロケールに対応するコードリストが定義されていなかった場合、Bean生成時にエラーとなりアプリケーションの起動に失敗する。

このため、様々な環境でアプリケーションを運用する場合や、デフォルトとしたいロケールとJVMインスタンスのデフォルトロケールが異なる場合は、fallbackToプロパティを指定することを強く推奨する。


fallbackToプロパティの設定例を以下に示す。

fallbackToプロパティの設定

@Bean("CL_I18N_PRICE")
public SimpleI18nCodeList clI18nPrice() {
    Map<Locale, CodeList> rows =  new LinkedHashMap<>(); // (1)
    rows.put(Locale.ENGLISH, clPriceEn());
    rows.put(Locale.JAPANESE, clPriceJa());
    SimpleI18nCodeList bean = new SimpleI18nCodeList();
    bean.setRowsByCodeList(rows);
    bean.setFallbackTo(Locale.ENGLISH);
    return bean;
}

項番

説明

(1)
fallbackToプロパティにロケール”en”を設定する。
これにより、要求されたロケールの言語ロケールが”en”、”ja”以外の場合、ロケール”en”が使用される。

4.9.2.5.3. コードリストの使用

JPS/テンプレートHTMLでコードリストを使用する方法については、前述した共通ライブラリから提供しているインタセプターの使用を参照されたい。

Note

CodeListInterceptorは、I18nCodeList#asMap()メソッドを利用して適切なロケールのコードリストを取得している。

asMap()メソッドについてはJavaクラスでのコードリスト使用を参照されたい。

jspの実装例

<form:select path="basePrice" items="${CL_I18N_PRICE}" />

出力HTML lang=en

<select id="basePrice" name="basePrice">
    <option value="0">unlimited</option>
    <option value="1">Less than \10,000</option>
    <option value="2">Less than \20,000</option>
    <option value="3">Less than \30,000</option>
    <option value="4">Less than \40,000</option>
    <option value="5">Less than \50,000</option>
</select>

出力HTML lang=ja

<select id="basePrice" name="basePrice">
    <option value="0">上限なし</option>
    <option value="1">10,000円以下</option>
    <option value="2">20,000円以下</option>
    <option value="3">30,000円以下</option>
    <option value="4">40,000円以下</option>
    <option value="5">50,000円以下</option>
</select>

出力画面 lang=en

codelist i18n en

出力画面 lang=ja

codelist i18n ja

4.9.2.5.4. Javaクラスでのコードリスト使用

I18nCodeListからコードリストを取得するには、以下のいずれかのメソッドを使用する。

  • asMap()メソッド
    org.springframework.context.i18n.LocaleContextHolderを利用して適切なロケールのコードリストを取得する。LocaleContextHolderorg.springframework.web.servlet.LocaleResolverを利用してクライアントから指定されたロケールを取得する。
  • asMap(Locale)メソッド
    指定されたロケールのコードリストを取得する。

Note

LocaleResolverの設定方法については、国際化を参照されたい。

LocaleResolverdefaultLocaleプロパティを指定している場合は、コードリストのfallbackToプロパティに同じロケールを指定することで、LocaleResolverで意図したロケールのコードリストを使用させることができる。


asMap()メソッドを利用する場合、前述したJavaクラスでのコードリスト使用と同様の方法で実装を行うことができる。

import org.springframework.stereotype.Service;
import org.terasoluna.gfw.common.codelist.i18n.I18nCodeList;

import jakarta.inject.Inject;
import jakarta.inject.Named;

@Service
public class OrderServiceImpl implements OrderService {

    @Inject
    @Named("CL_ORDERSTATUS")
    I18nCodeList orderStatusCodeList;

    public boolean existOrderStatus(String target) {
        return orderStatusCodeList.asMap().containsKey(target); // (1)
    }
}

項番

説明

(1)
I18nCodeList#asMap()メソッドでコードリストをjava.util.Map形式で取得する。

業務要件によって、特定のロケールのコードリストを取得する必要がある場合はasMap(Locale)メソッドを使用する

import java.util.Locale;

import org.springframework.stereotype.Service;
import org.terasoluna.gfw.common.codelist.i18n.I18nCodeList;

import jakarta.inject.Inject;
import jakarta.inject.Named;

@Service
public class OrderServiceImpl implements OrderService {

    @Inject
    @Named("CL_ORDERSTATUS")
    I18nCodeList orderStatusCodeList;

    public boolean existOrderStatus(String target) {
        return orderStatusCodeList.asMap(Locale.ENGLISH).containsKey(target); // (1)
    }
}

項番

説明

(1)
I18nCodeList#asMap(Locale)メソッドで、指定したロケールのコードリストをjava.util.Map形式で取得する。
ここでは Locale.ENGLISH(”en”)を指定している。

4.9.2.6. 特定のコード値からコード名を表示する

JSP/テンプレートHTMLからコードリストを参照する場合は、CodeListInterceptorがリクエストスコープにコードリストを java.util.Mapで格納しているため、Mapインターフェースと同じ方法で参照することができる。

コードリストを用いて特定のコード値からコード名を表示する方法について、以下に実装例を示す。

jspの実装例

Order Status : ${f:h(CL_ORDERSTATUS[orderForm.orderStatus])} // (1)

項番

説明

(1)

コードリストを定義したbeanID(この例ではCL_ORDERSTATUS) を属性名として、コードリスト(java.util.Mapインタフェース)を取得する。 取得したMapインタフェースのキーとしてコード値(この例ではorderStatusに格納された値) を指定することで、対応するコード名を表示することができる。


4.9.2.7. コードリストを用いたコード値の入力チェック

入力値がコードリスト内に定義されたコード値であるかどうかチェックするような場合、 共通ライブラリでは、BeanValidation用のアノテーション、org.terasoluna.gfw.common.codelist.ExistInCodeListを提供している。

BeanValidationや、メッセージ出力方法の詳細については、入力チェックを参照されたい。


4.9.2.7.1. @ExistInCodeList の設定例

コードリストを用いた入力チェック方法について、以下に実装例を示す。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_GENDER")
    public SimpleMapCodeList clGender() {
        SimpleMapCodeList bean = new SimpleMapCodeList();
        Map<String, String> map = new LinkedHashMap<>();
        map.put("M", "Male");
        map.put("F", "Femal");
        bean.setMap(map);
        return bean;
    }
    

Formオブジェクト

public class Person {
    @ExistInCodeList(codeListId = "CL_GENDER")  // (1)
    private String gender;

    // getter and setter omitted
}

項番

説明

(1)
入力チェックを行いたいフィールドに対して、@ExistInCodeListアノテーションを設定し、
codeListIdにチェック元となる、コードリストを指定する。

上記の結果、genderにM、F以外の文字が格納されている場合、エラーになる。

Note

terasoluna-gfw-common 5.4.2.RELEASEから、@ExistInCodeListの入力チェックの対象として、 CharSequenceインタフェースの実装クラス(Stringなど) または Characterに加え、Number継承クラス(Integerなど)をサポートするよう変更された。

NumberRangeCodeListvalueFormatプロパティを指定している場合、 Number型フィールドの値を当該プロパティを利用してフォーマットした値がコードリストに存在することをチェックする。


4.9.3. How to extend

4.9.3.1. コードリストをリロードする場合

前述した共通ライブラリで提供しているコードリストは、アプリケーション起動時に読み込まれ、それ以降は、基本的に更新されない。
しかし、コードリストのマスタデータを更新した時、コードリストも更新したい場合がある。

例:JdbcCodeListを使用して、DBのマスタを変更した時にコードリストの更新を行う場合。

共通ライブラリでは、コードリストを更新可能とするインタフェースを提供している。

  1. org.terasoluna.gfw.common.codelist.ReloadableCodeList:コードリストを更新する

  2. org.terasoluna.gfw.common.codelist.i18n.ReloadableI18nCodeList:行に持つコードリストを含むコードリストを更新する

コードリストの更新方法としては、以下2点の方法がある。

  1. Task Schedulerで実現する方法

  2. Controller(Service)クラスでrefreshメソッドを呼び出す方法

本ガイドラインでは、Springから提供されているTask Schedulerを使用して、コードリストを定期的にリロードする方式を基本的に推奨する。

ただし、任意のタイミングでコードリストをリフレッシュする必要がある場合はControllerクラスでrefreshメソッドを呼び出す方法で実現すればよい。

Note

ReloadableCodeListおよびReloadableI18nCodeListインターフェースを実装しているコードリストについては、コードリスト種類一覧を参照されたい。


4.9.3.1.1. Task Schedulerで実現する方法

Task Schedulerの設定例について、以下に示す。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    public class XxxCodelistConfig implements SchedulingConfigurer { // (2)
    
        @Value("${cron.codelist.refreshTime}")
        private String codelistRefreshTime;
    
        @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
            taskRegistrar.setScheduler(taskScheduler()); // (2)
            taskRegistrar.addTriggerTask(() -> clCronRefreshCodelist().refresh(), // (3)
                    new CronTrigger(codelistRefreshTime)); // (3)
        }
    
        // (1)
        @Bean("taskScheduler")
        public Executor taskScheduler() {
            return Executors.newScheduledThreadPool(10);
        }
    
        @Bean("CL_CRON_REFRESH_CODELIST")
        public JdbcCodeList clCronRefreshCodelist() {
            JdbcCodeList jdbcCodeList = abstractJdbcCodeList();
            jdbcCodeList.setQuerySql(
                    "SELECT authority_id, authority_name FROM t_authority ORDER BY authority_id");
            jdbcCodeList.setValueColumn("authority_id");
            jdbcCodeList.setLabelColumn("authority_name");
            return jdbcCodeList;
        }
    

    項番

    説明

    (1)
    TaskSchedulerを定義する。引数にはスレッドのプールサイズを指定する。
    (2)
    SchedulingConfigurerconfigureTasksメソッドを実装し、schedulerに、(1)で定義したTaskSchedulerを設定する。
    (3)
    実行するTaskを定義する。
    ここではJdbcCodeList#refreshを定義している。
    cron属性に、org.springframework.scheduling.support.CronExpressionでサポートされた形式で記述すること。
    cron属性は開発環境、商用環境など環境によってリロードするタイミングが変わることが想定されるため、プロパティファイルや、環境変数等から取得することを推奨する。

    cron属性の設定例
    「秒 分 時 月 年 曜日」で指定する。
    毎秒実行 「* * * * * *」
    毎時実行 「0 0 * * * *」
    平日の9-17時の毎時実行 「0 0 9-17 * * MON-FRI」

    cronの指定値の詳細については、CronExpressionのJavaDocを参照されたい。

4.9.3.1.2. Controller(Service)クラスでrefreshメソッドを呼び出す方法

refreshメソッドを直接呼び出す場合について、JdbcCodeListのrefreshメソッドをServiceクラスで呼び出す場合の実装例を、以下に示す。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_AUTHORITIES")
    public JdbcCodeList clAuthorities() {
        JdbcCodeList jdbcCodeList = abstractJdbcCodeList();
        jdbcCodeList.setQuerySql("SELECT authority_id, authority_name FROM t_authority ORDER BY authority_id");
        jdbcCodeList.setValueColumn("authority_id");
        jdbcCodeList.setLabelColumn("authority_name");
        return jdbcCodeList;
    }
    

Controllerクラス

@Controller
@RequestMapping(value = "codelist")
public class CodeListController {

    @Inject
    CodeListService codeListService; // (1)

    @RequestMapping(method = RequestMethod.GET, params = "refresh")
    public String refreshJdbcCodeList() {
        codeListService.refresh(); // (2)
        return "codelist/jdbcCodeList";
    }
}

項番

説明

(1)
ReloadableCodeListクラスのrefreshメソッドを実行するServiceクラスをインジェクションする。
(2)
ReloadableCodeListクラスのrefreshメソッドを実行するServiceクラスのrefreshメソッドを実行する。

Serviceクラス

以下は実装クラスのみ記述し、インターフェースクラスは省略。

@Service
public class CodeListServiceImpl implements CodeListService { // (3)

    @Inject
    @Named(value = "CL_AUTHORITIES") // (4)
    ReloadableCodeList codeListItem; // (5)

    @Override
    public void refresh() { // (6)
        codeListItem.refresh(); // (7)
    }
}

項番

説明

(3)
実装クラスCodeListServiceImplは、インターフェースCodeListServiceを実装する。
(4)
コードリストをインジェクションするとき、@Namedで、該当するコードリストを指定する。
value属性に取得したいbeanのIDを指定すること。
Bean定義ファイルに定義されているbeanタグのID属性”CL_AUTHORITIES”のコードリストがインジェクションされる。
(5)
フィールドの型にReloadableCodeListインターフェースを定義すること。
(4)で指定したBeanはReloadableCodeListインターフェースを実装していること。
(6)
Serviceクラスで定義したrefreshメソッド。
Controllerクラスから呼び出されている。
(7)
ReloadableCodeListインターフェースを実装したコードリストのrefreshメソッド。
refreshメソッドを実行することで、コードリストが更新される。

Note

terasoluna-gfw-common 5.4.2.RELEASEで追加されたSimpleReloadableI18nCodeListでは、refreshメソッドで行に持つすべてのReloadableCodeListを更新することが可能である。

アプリケーションの実装によっては、行に持つReloadableCodeListが更新されている前提でSimpleReloadableI18nCodeListのみ更新すれば良い場合もあり得る。

この場合は、ReloadableI18nCodeList#refresh(boolean)メソッドの引数にfalseをセットして実行すれば良い。


4.9.3.2. コードリストを独自カスタマイズする方法

共通ライブラリで提供している4種類のコードリストで実現できないコードリストを作成したい場合、コードリストを独自にカスタマイズすることができる。
独自カスタマイズする場合、作成できるコードリストの種類と実装方法について、以下の表に示す。

項番

Reloadable

継承するクラス

実装箇所

(1)
不要
org.terasoluna.gfw.common.codelist.AbstractCodeList
asMapをオーバライド
(2)
必要
org.terasoluna.gfw.common.codelist.AbstractReloadableCodeList
retrieveMapをオーバライド

org.terasoluna.gfw.common.codelist.CodeListorg.terasoluna.gfw.common.codelist.ReloadableCodeListインターフェースを直接実装しても実現はできるが、共通ライブラリで提供されている抽象クラスを拡張することで、最低限の実装で済む。

以下に、独自カスタマイズの実例について示す。
例として、今年と来年の年のリストを作るコードリストについて説明する。
(例:今年が2013の場合、コードリストには、”2013、2014”の順で格納される。)

コードリストクラス

package com.example.sample.domain.codelist;

// omitted

public class DepYearCodeList extends AbstractCodeList { // (1)

    private ClockFactory clockFactory;

    public void setClockFactory(ClockFactory clockFactory) { //(2)
        this.clockFactory = clockFactory;
    }

    @Override
    public Map<String, String> asMap() {  // (3)
        Clock clock = clockFactory.fixed();
        LocalDateTime localDateTime = LocalDateTime.now(clock);
        LocalDateTime nextYearDateTime = localDateTime.plusYears(1);

        Map<String, String> depYearMap = new LinkedHashMap<>();

        String thisYear = String.valueOf(localDateTime.getYear());
        String nextYear = String.valueOf(nextYearDateTime.getYear());

        depYearMap.put(thisYear, thisYear);
        depYearMap.put(nextYear, nextYear);

        return Collections.unmodifiableMap(depYearMap);
    }
}

項番

説明

(1)
AbstractCodeListを継承する。
今年と来年の年のリストを作る時、動的にシステム日付から算出して作成しているため、リロードは不要。
(2)
システム日付のDateクラスを作成するorg.terasoluna.gfw.common.time.ClockFactoryをインジェクションするためのセッターを用意する。
ClockFactoryを利用してシステム日時を取得することができる。
(3)
asMapメソッドをオーバライドして、今年と来年の年のリストを作成する。
作成したいコードリスト毎に実装が異なる。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    // (1)
    @Bean("CL_YEAR")
    public DepYearCodeList clYearCodelist(ClockFactory clockFactory) {
        DepYearCodeList bean = new DepYearCodeList(); // (1)
        bean.setClockFactory(clockFactory); // (2)
        return bean;
    }
    

    項番

    説明

    (1)
    作成したコードリストクラスをbean定義する。
    id にCL_YEARを指定することで、bean定義で設定したCodeListInterceptorによりコードリストをコンポーネント登録する。
    (2)
    システム日付のDateクラスを作成するClockFactoryを設定する。
    事前に、bean定義ファイルにDataFactory実装クラスを設定する必要がある。

jspの実装例

<form:select path="mostRecentYear" items="${CL_YEAR}" /> <!-- (1) -->

項番

説明

(1)
items属性にコンポーネント登録したCL_YEAR${}プレースホルダー で指定することで、該当のコードリストを取得することができる。

出力HTML

<select id="mostRecentYear" name="mostRecentYear">
    <option value="2013">2013</option>
    <option value="2014">2014</option>
</select>

出力画面

customized codelist

Note

リロード可能であるCodeListを独自カスタマイズする場合、スレッドセーフになるように実装すること。


4.9.4. Appendix

4.9.4.1. SimpleI18nCodeListのコードリスト設定方法

SimpleI18nCodeListのコードリスト設定について、I18nCodeListの使用方法で設定されているコードリスト設定の他に2つ設定方法がある。
料金を選択するセレクトボックスの場合の例を用いて、それぞれの設定方法を説明する。

4.9.4.1.1. 行単位でLocale毎のjava.util.Map(key=コード値, value=ラベル)を設定する

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_I18N_PRICE")
    public SimpleI18nCodeList clI18nPriceMapLocale() {
        Map<Locale, Map<String, String>> rowsMap = new LinkedHashMap<>();
        Map<String, String> enMap = new LinkedHashMap<>();
        enMap.put("0", "unlimited");
        enMap.put("10000", "Less than \\10,000");
        enMap.put("20000", "Less than \\20,000");
        enMap.put("30000", "Less than \\30,000");
        enMap.put("40000", "Less than \\40,000");
        enMap.put("50000", "Less than \\50,000");
        Map<String, String> jaMap = new LinkedHashMap<>();
        jaMap.put("0", "上限なし");
        jaMap.put("10000", "10,000円以下");
        jaMap.put("20000", "20,000円以下");
        jaMap.put("30000", "30,000円以下");
        jaMap.put("40000", "40,000円以下");
        jaMap.put("50000", "50,000円以下");
        rowsMap.put(Locale.ENGLISH, enMap);
        rowsMap.put(Locale.JAPANESE, jaMap);
        SimpleI18nCodeList bean = new SimpleI18nCodeList();
        bean.setRows(rowsMap); // (1)
        bean.setFallbackTo(Locale.ENGLISH);
        return bean;
    }
    

    項番

    説明

    (1)
    rowsプロパティに対して、”MapのMap”を設定する。外側のMapのkeyはjava.lang.Localeである。
    内側のMapのkeyはコード値、valueはロケールに対応したラベルである。

4.9.4.1.2. 列単位でコード値毎のjava.util.Map(key=Locale, value=ラベル)を設定する

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_I18N_PRCL_I18N_PRICEICE_MAP_CODE")
    public SimpleI18nCodeList clI18nPriceMapCode() {
        Map<String, Map<Locale, String>> columnsMap = new LinkedHashMap<>();
        Map<Locale, String> key1Map = new LinkedHashMap<>();
        key1Map.put(Locale.ENGLISH, "unlimited");
        key1Map.put(Locale.JAPANESE, "上限なし");
        Map<Locale, String> key2Map = new LinkedHashMap<>();
        key2Map.put(Locale.ENGLISH, "Less than \\10,000");
        key2Map.put(Locale.JAPANESE, "10,000円以下");
        Map<Locale, String> key3Map = new LinkedHashMap<>();
        key3Map.put(Locale.ENGLISH, "Less than \\20,000");
        key3Map.put(Locale.JAPANESE, "20,000円以下");
        Map<Locale, String> key4Map = new LinkedHashMap<>();
        key4Map.put(Locale.ENGLISH, "Less than \\30,000");
        key4Map.put(Locale.JAPANESE, "30,000円以下");
        Map<Locale, String> key5Map = new LinkedHashMap<>();
        key5Map.put(Locale.ENGLISH, "Less than \\50,000");
        key5Map.put(Locale.JAPANESE, "40,000円以下");
        Map<Locale, String> key6Map = new LinkedHashMap<>();
        key6Map.put(Locale.ENGLISH, "Less than \\50,000");
        key6Map.put(Locale.JAPANESE, "50,000円以下");
    
        columnsMap.put("0", key1Map);
        columnsMap.put("10000", key2Map);
        columnsMap.put("20000", key3Map);
        columnsMap.put("30000", key4Map);
        columnsMap.put("40000", key5Map);
        columnsMap.put("50000", key6Map);
        SimpleI18nCodeList bean = new SimpleI18nCodeList();
        bean.setColumns(columnsMap); // (1)
        bean.setFallbackTo(Locale.ENGLISH);
        return bean;
    }
    

    項番

    説明

    (1)
    columnsプロパティに対して、”MapのMap”を設定する。外側のMapのkeyはコード値である。
    内側のMapのkeyはjava.lang.Locale、valueはロケールに対応したラベルである。

4.9.4.2. NumberRangeCodeListのバリエーション

4.9.4.2.1. 降順のNumberRangeCodeListの作成

次に、Toの値をFromの値より小さくする(To < From)場合の実装例を、以下に示す。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_BIRTH_YEAR")
    public NumberRangeCodeList clBirthYear() {
        NumberRangeCodeList bean = new NumberRangeCodNumberRangeCodeListeList();
        bean.setFrom(2013); // (1)
        bean.setTo(2000); // (2)
        return bean;
    }
    

    項番

    説明

    (1)
    範囲開始の値を指定する。name属性”to”のvalue属性の値より大きい値を指定する。
    この指定によって、interval分減少した値を、To~Fromの範囲分のリストとして、降順に表示する。
    intervalは設定していないため、デフォルトの値1が適用される。
    (2)
    範囲終了の値を設定する。
    本例では、2000を指定することにより、リストには2013~2000までの範囲で1ずつ減少して格納される。

jspの実装例

<form:select path="birthYear" items="${CL_BIRTH_YEAR}" />

出力HTML

<select id="birthYear" name="birthYear">
    <option value="2013">2013</option>
    <option value="2012">2012</option>
    <option value="2011">2011</option>
    <option value="2010">2010</option>
    <option value="2009">2009</option>
    <option value="2008">2008</option>
    <option value="2007">2007</option>
    <option value="2006">2006</option>
    <option value="2005">2005</option>
    <option value="2004">2004</option>
    <option value="2003">2003</option>
    <option value="2002">2002</option>
    <option value="2001">2001</option>
    <option value="2000">2000</option>
</select>

出力画面

codelist numberrenge2

4.9.4.2.2. NumberRangeCodeListのインターバルの変更

次に、interval値を設定する場合の実装例を、以下に示す。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_BULK_ORDER_QUANTITY_UNIT")
    public NumberRangeCodeList clBulkOrderQuantityUnit() {
        NumberRangeCodeList bean = new NumberRangeCodeList();
        bean.setFrom(10);
        bean.setTo(50);
        bean.setInterval(10); // (1)
        return bean;
    }
    

    項番

    説明

    (1)
    増加(減少)値を指定する。この指定によって、interval値を増加(減少)した値を、From~Toの範囲内でコードリストとして格納する。
    上記の例だと、コードリストには10,20,30,40,50の順で格納される。

jspの実装例

<form:select path="quantity" items="${CL_BULK_ORDER_QUANTITY_UNIT}" />

出力HTML

<select id="quantity" name="quantity">
    <option value="10">10</option>
    <option value="20">20</option>
    <option value="30">30</option>
    <option value="40">40</option>
    <option value="50">50</option>
</select>

出力画面

codelist numberrenge3

Note

interval値分増加(減少)した値が、Form~Toの値が範囲を超えた場合は、コードリストに格納されない。

具体的には、

@Bean("CL_BULK_ORDER_QUANTITY_UNIT")
public NumberRangeCodeList clBulkOrderQuantityUnit() {
    NumberRangeCodeList bean = new NumberRangeCodeList();
    bean.setFrom(10);
    bean.setTo(55);
    bean.setInterval(10);
    return bean;
}

という定義を行った場合、

コードリストには10,20,30,40,50の計5つが格納される。

次のintervalである60及び範囲の閾値である55はコードリストに格納されない。


4.9.4.3. JSP/テンプレートHTMLから直接コードリストBeanを参照する

共通ライブラリから提供しているインタセプターの使用では、Spring MVCを経由する全てのリクエストに対して、CodeListIntercepterがコードリストのBeanをリクエスト属性として登録するため、コードリストの数が多くなるとリクエスト毎のオーバーヘッドの増加が懸念される。

ここでは、リクエスト毎のオーバーヘッドの増加を防ぐ方法の一つとして、コードリストBeanをJSPから直接参照する方法を紹介する。
JSP/ThymeleafではSpELを利用して直接Beanを参照することができるが、こちらを利用することでオーバーヘッドの増加を防止することができる。
いずれの方法を利用するかは、プロジェクトの要件によって適切に検討されたい。

bean定義ファイルの定義

  • SpringMvcConfig.java

    @EnableAspectJAutoProxy
    @EnableWebMvc
    @Configuration
    public class SpringMvcConfig implements WebMvcConfigurer {
    
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          registry.addInterceptor(codeListInterceptor()).addPathPatterns("/**"); // (1)
      }
    
      // (1)
      @Bean
      public CodeListInterceptor codeListInterceptor() {
          CodeListInterceptor codeListInterceptor = new CodeListInterceptor();
          codeListInterceptor.setCodeListIdPattern(Pattern.compile("CL_.+"));
          return codeListInterceptor;
      }
    

    項番

    説明

    (1)
    CodeListInterceptorの設定があれば、削除する。

bean定義ファイルの定義

  • XxxCodelistConfig.java

    @Bean("CL_ORDERSTATUS")
    public SimpleMapCodeList clOrderstatus() {
        Map<String, String> codeMap = new LinkedHashMap<>();
        codeMap.put("1", "Received");
        codeMap.put("2", "Sent");
        codeMap.put("3", "Cancelled");
        SimpleMapCodeList bean = new SimpleMapCodeList();
        bean.setMap(codeMap);
        return bean;
    }
    

jspの実装例

<spring:eval var="statuses" expression="@CL_ORDERSTATUS.asMap()"/> <!-- (1) -->
<form:select items="${statuses}" path="orderStatus" />

項番

説明

(1)
SpELにより取得したCodeList型BeanのasMapメソッドにより、Map形式で取得することができる。

出力HTML

<select id="orderStatus" name="orderStatus">
    <option value="1">Received</option>
    <option value="2">Sent</option>
    <option value="3">Cancelled</option>
</select>

4.9.4.3.1. SimpleI18nCodeListをテンプレートHTMLから直接参照する方法

ここでは、CodeListInterceptorの実装と同様に、リクエストのロケールに対するコードリストに定義されていなかった場合、デフォルトで設定したロケールに対するコードリストを表示する例を紹介する。

bean定義ファイルの定義

SpringMvcConfig.java/spring-mvc.xmlbean定義ファイルの定義と同様なため割愛する。


@Bean("CL_I18N_PRICE")
public SimpleI18nCodeList clI18nPrice() {
    Map<Locale, CodeList> rows =  new LinkedHashMap<>();
    rows.put(Locale.ENGLISH, clPriceEn());
    rows.put(Locale.JAPANESE, clPriceJa());
    SimpleI18nCodeList bean = new SimpleI18nCodeList();
    bean.setRowsByCodeList(rows);
    return bean;
}

@Bean("CL_PRICE_EN")
public SimpleMapCodeList clPriceEn() {
    Map<String, String> enMap = new LinkedHashMap<>();
    enMap.put("0", "unlimited");
    enMap.put("10000", "Less than \\10,000");
    enMap.put("20000", "Less than \\20,000");
    enMap.put("30000", "Less than \\30,000");
    enMap.put("40000", "Less than \\40,000");
    enMap.put("50000", "Less than \\50,000");
    SimpleMapCodeList bean = new SimpleMapCodeList();
    bean.setMap(enMap);
    return bean;
}

@Bean("CL_PRICE_JA")
public SimpleMapCodeList clPriceJa() {
    Map<String, String> jaMap = new LinkedHashMap<>();
    jaMap.put("0", "上限なし");
    jaMap.put("10000", "10,000円以下");
    jaMap.put("20000", "20,000円以下");
    jaMap.put("30000", "30,000円以下");
    jaMap.put("40000", "40,000円以下");
    jaMap.put("50000", "50,000円以下");
    SimpleMapCodeList bean = new SimpleMapCodeList();
    bean.setMap(jaMap);
    return bean;
}

プロパティファイル

simpleI18nCodeList.fallback.locale = en

Controllerクラス

...

@Controller
public class OrderController {

    @Value("${simpleI18nCodeList.fallback.locale}") // (1)
    private Locale fallBackLocale;

    @RequestMapping(value = "price", method = RequestMethod.GET)
    public String price(Model model, HttpServletRequest request) {
        model.addAttribute("requestLocale", RequestContextUtils
            .getLocale(request)); // (2)
        model.addAttribute("fallBackLocale",fallBackLocale); // (3)

        return "order/price";
    }
}

項番

説明

(1)
リクエストで指定したロケールがコードリストに定義されていなかった場合に、どのロケールのコードリストを取得するかをプロパティファイルから取得し、fallBackLocale変数に設定する。
(2)
org.springframework.web.servlet.support.RequestContextUtils利用してリクエストで指定されたロケールを取得し、Modelに登録する。
RequestContextUtilsgetLocaleメソッドは、引数にjakarta.servlet.http.HttpServletRequestを取るため、この場合はHttpServletRequestをハンドラメソッドの引数にとっても良い。
(3)
(1) で取得したfallBackLocaleをModelに登録する。

テンプレートHTML実装例

<select th:field="*{basePrice}">
    <option th:each="price : ${@CL_I18N_PRICE.asMap(requestLocale).isEmpty()} ?
    ${@CL_I18N_PRICE.asMap(fallBackLocale)} : ${@CL_I18N_PRICE.asMap(requestLocale)}"
    th:value="${price.key}" th:text="${price.value}"></option> <!--/* (1) */-->
</select>

項番

説明

(1)
リクエストで指定したロケールに対応するコードリストをMap形式で取得する。
リクエストで指定したロケールがコードリストに定義されていなかった場合、fallbackLocale変数に設定したロケールで対応するコードリストをMap形式で取得する。

出力HTML lang=en

<select id="basePrice" name="basePrice">
    <option value="0">unlimited</option>
    <option value="1">Less than \10,000</option>
    <option value="2">Less than \20,000</option>
    <option value="3">Less than \30,000</option>
    <option value="4">Less than \40,000</option>
    <option value="5">Less than \50,000</option>
</select>

出力HTML lang=ja

<select id="basePrice" name="basePrice">
    <option value="0">上限なし</option>
    <option value="1">10,000円以下</option>
    <option value="2">20,000円以下</option>
    <option value="3">30,000円以下</option>
    <option value="4">40,000円以下</option>
    <option value="5">50,000円以下</option>
</select>

出力HTML lang=undefined

<select id="basePrice" name="basePrice">
    <option value="0">unlimited</option> <!--/* (1) */-->
    <option value="1">Less than \10,000</option>
    <option value="2">Less than \20,000</option>
    <option value="3">Less than \30,000</option>
    <option value="4">Less than \40,000</option>
    <option value="5">Less than \50,000</option>
</select>

項番

説明

(1)
リクエストで指定したロケールがコードリストに定義されていなかった場合に、fallbackLocale変数で指定した”en” が設定されるため、ロケールが”en”であるCL_PRICE_ENコードリストが表示される。