Nablarchのコード管理機能を使用する

TISでは、豊富な基幹システム構築経験から得られたナレッジを集約したJavaアプリケーション開発/実行基盤として Nablrch を提供しています。 SpringからNablarchの機能を利用することで、Springに不足している機能を補うことができます。

この例では、Nablarchの機能である コード管理 を使用して、コード情報を管理する実装方法を説明します。

コード管理機能ではデータベース上でコード情報を管理し、言語毎の名称取得や、コード値をバリデーションすることができます。

サンプルコードの動作確認環境については、 動作確認環境と依存ライブラリについて を参照してください。

サンプル全体は nablarch-code を参照してください。

Nablarchを使用するための準備

pom.xml

Nablarchのモジュールバージョンを管理するために、dependencyManagementにnablarch-bomを追加します。

  <dependency>
    <groupId>com.nablarch.profile</groupId>
    <artifactId>nablarch-bom</artifactId>
    <version>6</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>

コード管理機能を使うため、依存ライブラリにnablarch-common-codeとnablarch-common-code-jdbcを追加します。

<!-- Nablarch:コード管理 -->
<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-common-code</artifactId>
</dependency>
<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-common-code-jdbc</artifactId>
</dependency>

Tip

Nablarchでは独自のログ出力機能を提供していますが、別のログライブラリを使用するためのアダプタが提供されています。この例では、SLF4Jを使用するためのアダプタである nablarch-slf4j-adaptor を使用しています。 詳細は logアダプタ を参照してください。

コード管理で使用するテーブルの作成

コード管理はデータベースで行うため、コードパターンテーブルとコード名称テーブルを用意します。

この例では、Flywayによるマイグレーションで起動時にデータベースを構築しているため、マイグレーション用のSQLファイルを作成します。 同じコード値でもパターンを切り替えることで表示内容を切り替えることができるように、1つのコード値に対してパターンを2つ用意しています。

テーブル構造の詳細については コード管理機能を使用する為の初期設定を行う を参照してください。

Tip

テーブル名やカラム名はあくまで一例であり、スキーマ情報を保持するBeanを定義する際に任意のカラム名を設定することができます。

V001__create_code_table.sql
create table code_pattern
(
    code_id    varchar(6) not null,
    code_value varchar(6) not null,
    pattern01  varchar(1) not null,
    pattern02  varchar(1),
    pattern03  varchar(1),
    pattern04  varchar(1),
    pattern05  varchar(1),
    pattern06  varchar(1),
    pattern07  varchar(1),
    pattern08  varchar(1),
    pattern09  varchar(1),
    pattern10  varchar(1),
    pattern11  varchar(1),
    pattern12  varchar(1),
    pattern13  varchar(1),
    pattern14  varchar(1),
    pattern15  varchar(1),
    pattern16  varchar(1),
    pattern17  varchar(1),
    pattern18  varchar(1),
    pattern19  varchar(1),
    pattern20  varchar(1),
    primary key (code_id, code_value)
);

create table code_name
(
    code_id    varchar(6) not null,
    code_value varchar(6) not null,
    lang       varchar(2) not null,
    sort_order varchar(2) not null,
    code_name  varchar(50) not null,
    short_name varchar(50),
    option01   varchar(40),
    option02   varchar(40),
    option03   varchar(40),
    option04   varchar(40),
    option05   varchar(40),
    option06   varchar(40),
    option07   varchar(40),
    option08   varchar(40),
    option09   varchar(40),
    option10   varchar(40),
    primary key (code_id, code_value, lang)
);

insert into code_pattern (code_id, code_value, pattern01, pattern02) values ('CODE01', 'MALE', '1', '1');
insert into code_pattern (code_id, code_value, pattern01, pattern02) values ('CODE01', 'FEMALE', '1', '1');
insert into code_pattern (code_id, code_value, pattern01, pattern02) values ('CODE01', 'OTHER', '0', '1');

insert into code_name (code_id, code_value, lang, sort_order, code_name) values ('CODE01', 'MALE', 'ja', '1', '男性');
insert into code_name (code_id, code_value, lang, sort_order, code_name) values ('CODE01', 'FEMALE', 'ja', '2', '女性');
insert into code_name (code_id, code_value, lang, sort_order, code_name) values ('CODE01', 'OTHER', 'ja', '3', 'その他');

データアクセス機能を動作させるための設定

Nablarchのコード管理機能は、NablarchのDIコンテナ機能とデータアクセス機能を使用して動作するようになっています。Nablarchにはそれらを動作させるための設定が組み込まれていますが、Springからそのまま使用することは出来ないため、Nablarchに組み込まれている設定と同等の設定をSpring側で行います。

データアクセス機能を使用できるようにするため、Springのトランザクション管理機能と統合させるためのクラスを作成します。

UnmanagedSimpleDbTransactionManager
/**
 * Springのトランザクション管理機能と統合するSimpleDbTransactionManagerサブクラス。
 */
public class UnmanagedSimpleDbTransactionManager extends SimpleDbTransactionManager {

    /**
     * トランザクション名
     */
    private static final String DB_TRANSACTION_NAME = TransactionContext.DEFAULT_TRANSACTION_CONTEXT_KEY;

    /**
     * ConnectionFactory
     */
    private final ConnectionFactory connectionFactory;

    /**
     * Springのトランザクションマネージャ
     */
    private final PlatformTransactionManager transactionManager;

    /**
     * トランザクションの状態を保持するスレッドローカル
     */
    private final ThreadLocal<TransactionStatus> transactionStatusHolder = new ThreadLocal<>();

    /**
     * コンストラクタ。
     * 
     * @param connectionFactory ConnectionFactory
     * @param transactionManager Springのトランザクションマネージャ
     */
    public UnmanagedSimpleDbTransactionManager(ConnectionFactory connectionFactory,
            PlatformTransactionManager transactionManager) {
        this.connectionFactory = connectionFactory;
        this.transactionManager = transactionManager;
    }

    @Override
    public void beginTransaction() {
        // トランザクションを開始、または既存トランザクションへ参加し、状態をスレッドローカルへ保存する
        TransactionStatus status = transactionManager.getTransaction(null);
        transactionStatusHolder.set(status);

        // Nablarchで使用するためAppDbConnectionを作成してDbConnectionContextへセット
        AppDbConnection dbConnection = connectionFactory.getConnection(DB_TRANSACTION_NAME);
        DbConnectionContext.setConnection(DB_TRANSACTION_NAME, dbConnection);
    }

    @Override
    public void commitTransaction() {
        // トランザクションを開始した場合はコミットを行う。
        // 既存トランザクションへ参加した場合は何も行わない。
        TransactionStatus status = transactionStatusHolder.get();
        if (status != null && status.isNewTransaction()) {
            transactionManager.commit(status);
        }
    }

    @Override
    public void rollbackTransaction() {
        // トランザクションを開始した場合はロールバックを行う。
        // 既存トランザクションへ参加した場合は何も行わない。
        TransactionStatus status = transactionStatusHolder.get();
        if (status != null && status.isNewTransaction()) {
            transactionManager.rollback(status);
        }
    }

    @Override
    public void endTransaction() {
        transactionStatusHolder.remove();
        DbConnectionContext.removeConnection(DB_TRANSACTION_NAME);
    }
}

作成したトランザクション管理のクラスや、その他に必要となるクラスをBean定義します。

DbAccessConfiguration
/**
 * Nablarchのデータアクセス機能を使用するための設定。
 */
public class DbAccessConfiguration {

    /**
     * SimpleDbTransactionManagerを構築する。
     * 
     * @param connectionFactory ConnectionFactory
     * @param transactionManager PlatformTransactionManager
     * @return 構築されたインスタンス
     */
    @Bean
    public SimpleDbTransactionManager dbManager(ConnectionFactory connectionFactory,
                                                PlatformTransactionManager transactionManager) {
        return new UnmanagedSimpleDbTransactionManager(connectionFactory, transactionManager);
    }

    /**
     * BasicSqlStatementExceptionFactoryを構築する。
     * 
     * @return 構築されたインスタンス
     */
    @Bean
    public BasicSqlStatementExceptionFactory sqlStatementExceptionFactory() {
        return new BasicSqlStatementExceptionFactory();
    }

    /**
     * BasicSqlParameterParserFactoryを構築する。
     * 
     * @param sqlConvertors SqlConvertorのリスト
     * @return 構築されたインスタンス
     */
    @Bean
    public BasicSqlParameterParserFactory sqlParameterParserFactory(List<SqlConvertor> sqlConvertors) {
        BasicSqlParameterParserFactory sqlParameterParserFactory = new BasicSqlParameterParserFactory();
        sqlParameterParserFactory.setSqlConvertors(sqlConvertors);
        return sqlParameterParserFactory;
    }

    /**
     * BasicStatementFactoryを構築する。
     *
     * @param sqlStatementExceptionFactory SqlStatementExceptionFactory
     * @param sqlParameterParserFactory SqlParameterParserFactory
     * @return 構築されたインスタンス
     */
    @Bean
    @ConfigurationProperties(prefix = "nablarch.db.statement-factory")
    public BasicStatementFactory statementFactory(SqlStatementExceptionFactory sqlStatementExceptionFactory,
                                                  SqlParameterParserFactory sqlParameterParserFactory) {
        BasicStatementFactory statementFactory = new BasicStatementFactory();
        statementFactory.setSqlStatementExceptionFactory(sqlStatementExceptionFactory);
        statementFactory.setUpdatePreHookObjectHandlerList(List.of());
        statementFactory.setSqlParameterParserFactory(sqlParameterParserFactory);
        return statementFactory;
    }

    /**
     * BasicDbAccessExceptionFactoryを構築する。
     * 
     * @return 構築されたインスタンス
     */
    @Bean
    public BasicDbAccessExceptionFactory dbAccessExceptionFactory() {
        return new BasicDbAccessExceptionFactory();
    }

    /**
     * H2のDialectを構築する。
     *
     * @return 構築されたインスタンス
     */
    @Bean
    public H2Dialect dialect() {
        return new H2Dialect();
    }

    /**
     * BasicDbConnectionFactoryForDataSourceを構築する。
     *
     * @param dataSource データソース
     * @param statementFactory StatementFactory
     * @param dbAccessExceptionFactory DbAccessExceptionFactory
     * @param dialect Dialect
     * @return 構築されたインスタンス
     */
    @Bean
    @ConfigurationProperties(prefix = "nablarch.db.connection-factory")
    public BasicDbConnectionFactoryForDataSource connectionFactory(
            DataSource dataSource,
            StatementFactory statementFactory,
            DbAccessExceptionFactory dbAccessExceptionFactory,
            Dialect dialect) {
        BasicDbConnectionFactoryForDataSource connectionFactory = new BasicDbConnectionFactoryForDataSource();
        connectionFactory.setDataSource(new TransactionAwareDataSourceProxy(dataSource));
        connectionFactory.setStatementFactory(statementFactory);
        connectionFactory.setDbAccessExceptionFactory(dbAccessExceptionFactory);
        connectionFactory.setDialect(dialect);
        return connectionFactory;
    }
}

Tip

Nablarchのデータアクセス機能では、データベースによるSQLの違いを吸収するためにダイアレクトを設定する必要があります。 この例ではデータベースにH2を使用するため、H2用のダイアレクトを設定しています。

コード管理機能を動作させるための設定

コード管理で使用するために用意したテーブル定義に合わせて、スキーマ情報を保持するBeanを定義します。前述のテーブル定義に合わせて、テーブル名やカラム名を設定します。

CodeManagementConfiguration
    /**
     * CodePatternSchemaを構築する。
     * テーブル名やカラム名など、プロジェクトに合わせてカスタマイズすること。
     * 
     * @return 構築されたインスタンス。
     */
    @Bean
    public CodePatternSchema codePatternSchema() {
        CodePatternSchema codePatternSchema = new CodePatternSchema();
        codePatternSchema.setTableName("code_pattern");
        codePatternSchema.setIdColumnName("code_id");
        codePatternSchema.setValueColumnName("code_value");
        codePatternSchema
                .setPatternColumnNames(new String[] {
                        "pattern01", "pattern02", "pattern03", "pattern04", "pattern05", "pattern06", "pattern07",
                        "pattern08", "pattern09", "pattern10", "pattern11", "pattern12", "pattern13", "pattern14",
                        "pattern15", "pattern16", "pattern17", "pattern18", "pattern19", "pattern20"

                });
        return codePatternSchema;
    }

    /**
     * CodeNameSchemaを構築する。
     * テーブル名やカラム名など、プロジェクトに合わせてカスタマイズすること。
     * 
     * @return 構築されたインスタンス。
     */
    @Bean
    public CodeNameSchema codeNameSchema() {
        CodeNameSchema codeNameSchema = new CodeNameSchema();
        codeNameSchema.setTableName("code_name");
        codeNameSchema.setIdColumnName("code_id");
        codeNameSchema.setValueColumnName("code_value");
        codeNameSchema.setLangColumnName("lang");
        codeNameSchema.setSortOrderColumnName("sort_order");
        codeNameSchema.setNameColumnName("code_name");
        codeNameSchema.setShortNameColumnName("short_name");
        codeNameSchema
                .setOptionNameColumnNames(new String[] {
                        "option01", "option02", "option03", "option04", "option05", "option06", "option07", "option08",
                        "option09", "option10"
                });
        return codeNameSchema;
    }

スキーマ情報のBeanとやデータアクセス機能のBeanを使用して、データベースからコードをロードするための BasicCodeLoader をBean定義します。 BasicCodeLoaderには初期化用のメソッドがあるため、Bean初期化時に呼び出すように設定しておきます。

CodeManagementConfiguration
    /**
     * BasicCodeLoaderを構築する。
     *
     * @param codePatternSchema CodePatternSchema
     * @param codeNameSchema CodeNameSchema
     * @param dbManager SimpleDbTransactionManager
     * @return 構築されたインスタンス
     */
    @Bean(initMethod = "initialize")
    public BasicCodeLoader codeLoader(
            CodePatternSchema codePatternSchema,
            CodeNameSchema codeNameSchema,
            SimpleDbTransactionManager dbManager) {
        BasicCodeLoader codeLoader = new BasicCodeLoader();
        codeLoader.setCodeNameSchema(codeNameSchema);
        codeLoader.setCodePatternSchema(codePatternSchema);
        codeLoader.setDbManager(dbManager);
        return codeLoader;
    }

コード情報にアクセスする際に使用する BasicCodeManager をBean定義します。

CodeManagementConfiguration
    /**
     * BasicStaticDataCacheを構築する。
     * 
     * @param loader BasicCodeLoader
     * @return 構築されたインスタンス
     */
    @Bean(initMethod = "initialize")
    @ConfigurationProperties(prefix = "nablarch.code.code-definition-cache")
    public BasicStaticDataCache<Code> codeDefinitionCache(BasicCodeLoader loader) {
        BasicStaticDataCache<Code> basicStaticDataCache = new BasicStaticDataCache<>();
        basicStaticDataCache.setLoader(loader);
        return basicStaticDataCache;
    }

    /**
     * BasicCodeManagerを構築する。
     * 
     * @param codeDefinitionCache BasicStaticDataCache
     * @return 構築されたインスタンス
     */
    @Bean
    public BasicCodeManager codeManager(BasicStaticDataCache<Code> codeDefinitionCache) {
        BasicCodeManager codeManager = new BasicCodeManager();
        codeManager.setCodeDefinitionCache(codeDefinitionCache);
        return codeManager;
    }

Bean定義したBasicCodeManagerはNablarchの内部でも使用するため、NablarchのDIコンテナであるシステムリポジトリに登録します。

CodeManagementConfiguration
    /**
     * CodeManagementSystemRepositoryLoaderを構築する。
     * 
     * @param codeManager BasicCodeManager
     * @return 構築されたインスタンス
     */
    @Bean
    public CodeManagementSystemRepositoryLoader codeManagementSystemRepositoryLoader(CodeManager codeManager) {
        return new CodeManagementSystemRepositoryLoader(codeManager);
    }
CodeManagementSystemRepositoryLoader
/**
 * コード管理機能に必要なインスタンスを{@link SystemRepository}へ登録するクラス。
 */
public class CodeManagementSystemRepositoryLoader implements InitializingBean {

    /**
     * CodeManager
     */
    private final CodeManager codeManager;

    /**
     * コンストラクタ。
     * 
     * @param codeManager CodeManager
     */
    public CodeManagementSystemRepositoryLoader(CodeManager codeManager) {
        this.codeManager = codeManager;
    }

    @Override
    public void afterPropertiesSet() {
        SystemRepository.load(() -> Map.of("codeManager", codeManager));
    }
}

ドメインバリデーションを動作させるための設定

ドメインバリデーションを動作させるための設定については、 Nablarchのドメインバリデーションを使用して入力値をチェックする を参照してください。

コード値バリデーションの使用例

Nablarchが提供するバリデーターでは、各種エラーに対応するメッセージを定義する必要がありますので、使用するバリデーターに合わせてメッセージを定義します。

使用するプロパティ名については、Nablarchのデフォルト設定として定義されています。Nablarchのデフォルト設定の詳細については デフォルト設定一覧 を参照してください。

message.properties
nablarch.core.validation.ee.Required.message=入力してください。
nablarch.common.code.validator.ee.CodeValue.message=不正な値が指定されました。

ドメインバリデーションで使用するドメインBeanに、 @CodeValue アノテーションでコード値のバリデーションを設定します。使用するコード値とパターンを指定し、対応するコード情報を設定します。

DomainBean
/**
 * ドメインのバリデーションルール。
 */
public class DomainBean {

    @CodeValue(codeId = "CODE01", pattern = "pattern01")
    public String codePattern01;

    @CodeValue(codeId = "CODE01", pattern = "pattern02")
    public String codePattern02;
}

Controllerで受け取るBeanのプロパティに対して、 @Domain アノテーションで対応するドメイン名を指定します。Springでバリデーションが実行される際、ドメインBeanに設定したバリデーションルールに従ってバリデーションが実行されます。

CodeManagementForm
public class CodeManagementForm {

    @Required
    @Domain("codePattern01")
    private String codePattern01;

    @Domain("codePattern02")
    private String codePattern02;

    // (アクセサの記載は省略します)

画面での使用例

Bean定義したBasicCodeManagerを使用することで、コード情報にアクセスすることができるため、Thymeleafから使用するためのヘルパークラスを作成します。

CodeViewHelper
/**
 * コード値に関するViewHelper。
 */
@Component
@Transactional(readOnly = true)
public class CodeViewHelper {

    @Autowired
    private CodeManager codeManager;

    /**
     * {@link CodeManager#getName(String, String)}を呼び出す。
     * 
     * @param codeId コードID
     * @param value コード値
     * @return 対応するコード名称
     * @throws IllegalArgumentException 指定したコードIDが存在しないか、
     *                                   対象のコード値または言語に対応するデータが存在しない場合
     */
    public String getName(String codeId, String value) throws IllegalArgumentException {
        return StringUtils.hasText(value) ? codeManager.getName(codeId, value) : null;
    }

    /**
     * {@link CodeManager#getValues(String, String)}を呼び出す。
     * 
     * @param codeId コードID
     * @param pattern 使用するパターンのカラム名(大文字・小文字を区別せずに使用する)
     * @return コードIDとパターンに紐付くコード値
     * @throws IllegalArgumentException 指定したコードIDが存在しないか、
     *                                   パターンまたは言語に対応するデータが存在しない場合
     */
    public List<String> getValues(String codeId, String pattern) throws IllegalArgumentException {
        return codeManager.getValues(codeId, pattern);
    }
}

画面では、作成したヘルパークラスを使用してコード値と名称を取得し、プルダウンに設定します。

index.html
  <div style="margin-bottom: 8px">
    コード(パターン1):
    <select  name="codePattern01" th:field="*{codePattern01}">
      <option value=""></option>
      <option value="ER">未登録のコード</option>
      <option
              th:each="codeValue : ${@codeViewHelper.getValues('CODE01','pattern01')}"
              th:value="${codeValue}"
              th:text="${@codeViewHelper.getName('CODE01', codeValue)}"
      >
        ...
      </option>
    </select>
    <div th:if="${#fields.hasErrors('codePattern01')}" th:errors="*{codePattern01}" style="color: red"></div>
  </div>