web-dev-qa-db-ja.com

列挙型の使用と永続化のベストプラクティス

Enumのような値を処理および永続化する最良の方法について、ここでいくつかの質問/ディスカッションを見てきました(例 enumsに適したデータの永続化NHibernateを使用して列挙型を永続化する方法 )、そして私は一般的なコンセンサスとは何かを尋ねたいと思います。

特に:

  • これらの値はコードでどのように処理する必要がありますか?
  • それらはどのようにデータベースに永続化する必要がありますか(テキストとして/数値として)?
  • さまざまなソリューションのトレードオフは何ですか?

注:この質問に元々含まれていた説明を回答に移動しました。

75
sleske

私は私の理解を要約しようとしました。修正がある場合は、自由に編集してください。だからここに行く:

コード内

コードでは、列挙型は、言語のネイティブ列挙型(少なくともJavaおよびC#))、または "typesafe enum pattern" のようなものを使用して処理する必要があります。型の安全性を失うため(そしてメソッドなどの正当な入力である値を理解するのが困難になるため)、プレーンな定数(整数など)の使用はお勧めしません。

これら2つのどちらを選択するかは、列挙型に追加する機能の量によって異なります。

  • 機能のロードを列挙型に入れたい場合は(これは常にswitch()をオンにしないので良いことです)、通常はクラスがより適切です。
  • 一方、単純な列挙型の値の場合、言語の列挙型は通常、より明確です。

特に、少なくともJava enumは別のクラスから継承できません。そのため、スーパークラスに入れたい同様の動作を持つ複数のenumがある場合、Javaのenumは使用できません。

永続的な列挙型

列挙型を永続化するには、各列挙値に一意のIDを割り当てる必要があります。これは整数でも短い文字列でもかまいません。ニーモニックになる可能性があるため、短い文字列が推奨されます(データベース管理者などがデータベース内の生データを理解しやすくなります)。

  • ソフトウェアでは、すべての列挙型に、列挙型(ソフトウェア内で使用)とID値(永続化)の間で変換するマッピング関数が必要です。一部のフレームワーク((N)Hibernateなど)では、これを自動的に行うためのサポートが制限されています。それ以外の場合は、それを列挙型/クラスに配置する必要があります。
  • データベースには、(理想的には)正当な値をリストする各列挙のテーブルが含まれている必要があります。 1つの列はID(上記を参照)、つまりPKです。追加の列は、たとえば、説明。その列挙型からの値を含むすべてのテーブル列は、この「列挙型テーブル」をFKとして使用できます。これにより、不正な列挙値が永続化されないことが保証され、DBが「独自に」機能できるようになります。

このアプローチの1つの問題は、正当な列挙値のリストが2か所(コードとデータベース)に存在することです。これは避けるのが難しいため、多くの場合許容できると考えられますが、次の2つの方法があります。

  • 値のリストはDBにのみ保持し、ビルド時に列挙型を生成します。エレガントですが、ビルドを実行するにはDB接続が必要であり、問​​題があるようです。
  • 信頼できるコードの値のリストを定義します。実行時(通常は起動時)にDBの値をチェックし、不一致があると不平を言う/中止します。
7
sleske

私はあなたの言うことの多くに同意します。ただし、列挙型の永続性について追加したいことが1つあります。DB値からのビルド時の列挙型の生成は受け入れられないと思いますが、実行時チェックは適切な解決策ではないと思います。 3番目の方法を定義します。列挙型の値をデータベースに対してチェックする単体テストを用意します。これにより、「偶発的な」相違が防止され、コードが実行されるたびにデータベースに対して列挙型をチェックするオーバーヘッドが回避されます。

18
Paul Sonier

最初の記事は私には元気に見えます。それでも、コメントに基づいて、Java列挙型に関するいくつかのコメントはいくつかのことを明確にする可能性があります。

Javaの列挙型は当然のことながらクラスですが、他のいくつかの言語のように「許可された値のリスト」に関連付けるので、多くのプログラマーはこれを忘れがちです。それだけではありません。 。

したがって、これらのswitchステートメントを回避するために、enumクラスにいくつかのコードと追加のメソッドを配置するのが妥当な場合があります。別の「列挙型の実クラス」を作成する必要はほとんどありません。

ドキュメント化のポイントも考慮してください。データベース内の列挙型の実際の意味をドキュメント化しますか?値(列挙型)を反映するソースコード、または一部の外部ドキュメントで?私は個人的にソースコードを好みます。

速度などの理由でデータベースに列挙値を整数として表示する場合、そのマッピングはJava enumにも存在する必要があります。デフォルトでは文字列名のマッピングを取得し、私はそれに満足しています。各列挙値に関連付けられた序数がありますが、それをコードとデータベース間のマッピングとして直接使用することはあまり明るくありません。なぜなら、その序数は、誰かがソースコードの値を並べ替えると変更されるためです。または、既存の値の間に追加の列挙値を追加するか、一部の値を削除します。

(もちろん、誰かがソースコードで列挙型の名前を変更した場合、デフォルトの文字列マッピングも酸っぱくなりますが、それが誤って発生する可能性は低くなります。必要に応じて、実行時チェックを行うことで、より簡単に保護できます。ここですでに提案されているように、データベースの制約を確認してください。

13
lokori

C#のコード処理では、0値のデリカリングの定義を見逃しています。私はほぼ間違いなく常に最初の値を次のように宣言します:

public enum SomeEnum
{
    None = 0,
}

Null値として機能するように。バッキングタイプは整数であり、整数のデフォルトは0であるため、列挙型が実際にプログラムで設定されているかどうかを知ることは、多くの場所で非常に役立ちます。

6
Quibblesome

JavaまたはC#では、コードで常に列挙型を使用する必要があります。免責事項:私の背景はC#です。

値をデータベースに永続化する場合は、各列挙型メンバーの整数値を明示的に定義して、後でコードを変更しても、変換された列挙型の値が誤って変更されず、アプリケーションの動作が変更されないようにする必要があります。

Enum名のリファクタリングから保護するために、値は常に整数値としてデータベースに永続化する必要があります。 Wiki内の各列挙に関するドキュメントを保持し、タイプをドキュメント化するWikiページを指すコメントをデータベースフィールドに追加します。また、Wikiエントリへのリンクを含む列挙型にXMLドキュメントを追加して、Intellisenseから利用できるようにします。

ツールを使用してCRUDコードを生成する場合、生成されたコードオブジェクトが常に列挙型メンバーを使用するように、列に使用する列挙型を定義できる必要があります。

列挙型メンバーにカスタムロジックを適用する必要がある場合は、いくつかのオプションがあります。

  • 列挙型MyEnumがある場合は、switchステートメントまたは必要な手段によって、列挙型メンバーに関する追加情報を検出するユーティリティメソッドを提供する静的クラスMyEnumInfoを作成します。クラス名の列挙名の最後に「Info」を追加すると、それらがIntelliSenseで互いに隣り合うようになります。
  • 列挙型メンバーを属性で装飾して、追加のパラメーターを指定します。たとえば、列挙値で満たされたASP.NETドロップダウンを作成するEnumDropDownコントロールを開発しました。EnumDisplayAttributeは、各メンバーに使用する適切にフォーマットされた表示テキストを指定します。

私はこれを試していませんが、SQL Server 2005以降では、理論的には、列挙情報を含むデータベースにC#コードを登録し、ビューやその他の構成で使用するために値を列挙型に変換して、 DBAが使用しやすい方法でデータを収集します。

5
David Boike

ええと、私の経験では、オプションを(フラグとして)即時メソッド呼び出しに渡す以外の目的で列挙型を使用すると、ある時点でswitch- ingが発生します。

  • コード全体でenumを使用する場合、保守がそれほど容易ではないコード(悪名高いswitchステートメント)になる可能性があります。
  • 列挙型を拡張するのは面倒です。新しい列挙型アイテムを追加し、すべてのコードを調べてすべての条件をチェックします。
  • .NET 3.5では、列挙型に拡張メソッドを追加して、クラスのように動作させることができます。ただし、実際の機能をこの方法で追加することは、クラスではないため、それほど簡単ではありません(他の場所でないと、拡張メソッドでswitch- esを使用することになります)。

したがって、機能がもう少し多い列挙型のエンティティの場合、いくつかの点を考慮して、少し時間をかけてクラスとして作成する必要があります。

  • クラスを列挙型のように動作させるには、各派生クラスを強制的にシングルトンとしてインスタンス化するか、Equalsをオーバーライドして、異なるインスタンスの値を比較できます。
  • クラスが列挙型である場合、それはシリアル化可能な状態を含まないことを意味するはずです-逆シリアル化はそのタイプのみ(あなたが言ったような「ID」の一種)から可能であるべきです。
  • 永続化ロジックは基本クラスのみに限定する必要があります。そうしないと、「列挙型」の拡張が悪夢になります。シングルトンパターンを使用した場合は、シングルトンインスタンスへの適切な逆シリアル化を確認する必要があります。
3
Groo

列挙型へのコード変更で「マジックナンバー」を使用して自分を見つけるたびに。時間の節約に加えて(バグが来ると魔法が消えるので...)それはあなたの目とメモリを節約します(意味のある列挙型はコードをより読みやすく自己文書化します)。あなた自身のコード

3
Yordan Georgiev

Enumのテキスト値をデータベースに格納することは、整数を格納するよりも、必要な追加のスペースと検索速度の低下のため、あまり好ましくありません。数値よりも意味があるという点で価値がありますが、データベースはストレージ用であり、プレゼンテーションレイヤーは物事を美しく見せるためのものです。

3
cjk

Imho、コード部分は:

always列挙には 'enum'タイプを使用してください。そうすると、基本的に多くの景品が得られます。タイプセーフ、カプセル化、スイッチの回避、EnumSetEnumMapなどのいくつかのコレクションのサポートとコード明快さ。

永続化の部分については、常に列挙型の文字列表現を永続化し、enum.valueOf(String)メソッドを使用してロードし直すことができます。

2
MahdeTo

私はこれが古いフォーラムであることを知っていますが、データベースに他のものが直接統合されている場合はどうなりますか?例えば。結果のDBがコードの唯一の目的である場合。次に、すべての統合で列挙型を定義します。それらをDBに入れる方が良いでしょう。それ以外の場合は、元の投稿に同意します。

1
Chalky