30
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

区分値とその名称の管理 Enum(Java) vs DB

Posted at

システム開発をしていると、
「1:男、2:女」「A:優 B:良 C:可 D:否」というように、区分値とその意味を対応させたいことが多々あります。
また、区分値から文字列に変換したいケースも多いです。

そこで、今回はJavaのEnumを使った方法と、DBのマスタテーブルに保存する方法で、比較したいと思います。

言語はJava、DBはMySQLを使う想定です。

1. Enumクラス

Java5以降では、列挙型という概念があります。
Effective Javaでも定数として使うことを推奨されていますね。

まずは、以下のようなエンコーダーインタフェースを定義します。

エンコーダーインタフェース
/**
 * Enum用エンコーダーインターフェース
 *
 * @param <T> Enum定義
 */
public interface Encodable<T extends Serializable> {
    /**
     * エンコーダー
     *
     * @return Enum定義
     */
    T getCode();

    /**
     * コードの名称を返す.
     *
     * @return 名称
     */
    String getName();
}

区分値から文字列を生成するために、デコーダークラスを以下のように定義します。

デコーダークラス
/**
 * Enum用デコーダークラス
 *
 * @param <K> コード値
 * @param <V> バリュー
 */
public class Decoder<K extends Serializable, V extends Encodable<K>> {

    /** マップ定義 */
    private Map<K, V> map;

    /**
     * デコーダー
     *
     * @param values Enum群
     */
    private Decoder(V[] values) {
        map = new HashMap<K, V>(values.length);

        for (V value : values) {
            V old = map.put(value.getCode(), value);

            // コード値の重複はサポートしない
            if (old != null) {
                throw new IllegalArgumentException("duplicated code: " + value);
            }
        }
    }

    /**
     * デコーダー
     *
     * @param code コード値
     * @return Enumクラス
     */
    public V decode(K code) {
        return map.get(code);
    }

    /**
     * 型引数の指定を省略するための定義
     *
     * @param <L> コード値
     * @param <W> バリュー
     * @param values Enum群
     * @return デコーダー
     */
    public static <L extends Serializable, W extends Encodable<L>> Decoder<L, W> create(W[] values) {
        return new Decoder<L, W>(values);
    }
}

実際のクラスは以下のような形になります。
性別の場合以下のようになります。

性別の列挙型クラス
/**
 * 性別の列挙型クラス.
 * 
 */
public enum Gender implements
                     Encodable<Integer> {

    /** 男 */
    MALE(1, "男"),
    /** 女 */
    FEMALE(2, "女");

    /** デコーダー */
    private static final Decoder<Integer, Gender> DECODER = Decoder.create(values());

    /** コード値 */
    private final Integer code;

    /** 名称 */
    private final String name;

    /**
     * コンストラクタ.
     * 
     * @param code コード値
     * @param name 名称
     */
    private Gender(Integer code, String name) {
        this.code = code;
        this.name = name;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    /**
     * コード値からEnumクラスを取得する.
     * 
     * @param code コード値
     * @return 受領形式Enumクラス
     */
    public static Gender decode(Integer code) {
        return DECODER.decode(code);
    }

    /**
     * 名称を取得するメソッド.
     * 
     * @return 名称
     */
    public String getName() {
        return name;
    }
}

以下のように、decodeクラスとgetNameメソッドで、名称が取得できます。

使用例
// 1:男性
Gender male = Gender.decode(1);
String name = male.getName();

メリット

保守性が高い

プログラム内に記載し、意味のあるクラス名をつければ、可読性、保守性が高く、
保守がしやすいコードになります。

ロジックの記載が容易

DBにいちいち接続しなくても、区分名称が取得できるため、処理としても楽です。

デメリット

区分値の追加、名称の変更が容易でない

仕様変更などで、区分値の追加や、名称の変更がある際、プログラム修正が発生し、リリース作業も発生します。

どんな時に使うか

性別、日本の都道府県など、まず変更されえないものをEnumで管理すると良いと思います。
変更される場合、ロジックの修正とかが必要になるケースも多いと思います。

2. DBのマスタテーブル

以下のようなマスタテーブルを使って、idとその名称を管理するような形になります。
名称以外にも付加情報を入れることももちろん可能です。

ログインユーザマスタ
CREATE TABLE login_user_mst (
    id VARCHAR(11) NOT NULL,
    password CHAR(40) NOT NULL comment 'ログインパスワード',
    name VARCHAR(255) NOT NULL comment '氏名',
    name_kana VARCHAR(255) default NULL comment '氏名カナ',

    PRIMARY KEY(id)
) CHARACTER SET 'utf8'
  COMMENT 'ログインユーザー';

データの例

id password name name_kana
10000001 xxxxxxxxxxxxxx 鈴木 奈々子 スズキナナコ
10000002 xxxxxxxxxxxxxx 松本 かずお マツモトカズオ
10000003 xxxxxxxxxxxxxx 渡邊 拓 ワタナベタク

メリット

名称の変更がしやすい

区分値の名称を変更するのは、DBのレコードを更新するだけで良いので、
プログラム修正が発生せずに変更が可能なので、コストがかなり少ないです。

テーブル結合で区分名称を取得できる

マスタテーブルを結合して、SQLだけで名称を取得できるので、
SQLの結果を、画面で表示したりCSVに出力するだけ、というロジックだけで良くなります。

デメリット

区分値による分岐処理の際は意味がない

例えば、区分値によって、処理の分岐が起こる場合、
その区分値をアプリ側に持たざるを得ないため、
DBに値を持っていても、ダブルメンテナンス状態になります。

テーブルの数が増えると管理しにくい

「定数はすべてマスタテーブルで管理しよう」というルールがあるプロジェクトだと、
定数の種類が多くなると、定数ごとにテーブルを作っていると、膨大な数のマスタテーブルが出来上がります。
マスタテーブルであれば、"mst_"という接頭辞をつけるというようなルールがあれば、まだ管理しやすいかと思いますが。。

どんな時に使うか

ログインユーザの管理や、営業拠点情報、
比較的変更されるケースが多めのものに使うと良いかと思います。

終わりに

区分値と名称の管理をどうするか、というのは永遠の課題だと考えています(個人的に)。
今のプロジェクトでは、Enum型、DBマスタの両方を使っています。
それぞれの特性を理解して使うべきかと思います。

30
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?