NHibernate で enum のプロパティ/列をマッピングするときのメモです。
普通にマッピングすると
以下の様な enum のプロパティをマッピングすると、"Gender" は 1, "Female" は 2 という感じで enum の中身の整数(int)でマッピングされます。
モデル
public enum Gender { Male = 1, Female = 2 }
public class Student { public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual Gender Gender { get; set; } }
テーブル
テーブルのほうはこんな感じですね。
CREATE TABLE [dbo].[Students] ( [Id] INT IDENTITY NOT NULL PRIMARY KEY, [Name] NVARCHAR (100) NOT NULL, [Gender] INT NOT NULL, CHECK ([Gender] = 1 OR [Gender] = 2) );
マッピング
マッピングはこうです。
<class name="Student" table="Students"> <id name="Id"> <generator class="identity"></generator> </id> <property name="Name"></property> <property name="Gender"></property> </class>
データ
Id | Name | Gender |
---|---|---|
1 | スタン | 1 |
2 | カイル | 1 |
3 | カートマン | 1 |
4 | ケニー | 1 |
5 | バターズ | 1 |
6 | ウェンディ | 2 |
文字列でマッピングしたいときは
でも整数ではなく文字列でテーブルに持ちたいときもあります。データをこんな風にしたいときですね。
Id | Name | Gender |
---|---|---|
1 | スタン | Male |
2 | カイル | Male |
3 | カートマン | Male |
4 | ケニー | Male |
5 | バターズ | Male |
6 | ウェンディ | Female |
そんなときは NHibernate の IUserType インターフェイスを実装してマッピングに利用します。 例えばこんな感じの基底クラスを用意しておいて、
public class EnumStringType<TEnum> : IUserType { public bool Equals(object x, object y) { if (ReferenceEquals(x, y)) return true; if (x == null || y == null) return false; return x.Equals(y); } public int GetHashCode(object x) { if (x == null) return 0; return x.GetHashCode(); } public object NullSafeGet(IDataReader rs, string[] names, object owner) { var value = NHibernateUtil.String.NullSafeGet(rs, names); if (value == null) return null; return (TEnum) Enum.Parse(typeof (TEnum), (string) value); } public void NullSafeSet(IDbCommand cmd, object value, int index) { string stringValue = null; if (value != null) stringValue = ((TEnum) value).ToString(); NHibernateUtil.String.NullSafeSet(cmd, stringValue, index); } public object DeepCopy(object value) { return value; } public object Replace(object original, object target, object owner) { return original; } public object Assemble(object cached, object owner) { return cached; } public object Disassemble(object value) { return value; } public SqlType[] SqlTypes { get { return new[] {NHibernateUtil.String.SqlType}; } } public Type ReturnedType { get { return typeof (TEnum); } } public bool IsMutable { get { return false; } } }
これを派生した、目的の enum 用のクラスを作ればOKです。
public class GenderStringType : EnumStringType<Gender> { }
テーブル
あとはテーブルの列の型を文字列に変更して...
CREATE TABLE [dbo].[Students] ( [Id] INT IDENTITY NOT NULL PRIMARY KEY, [Name] NVARCHAR (100) NOT NULL, [Gender] NVARCHAR (10) NOT NULL, CHECK ([Gender] = ‘Male‘ OR [Gender] = ‘Female‘) );
マッピング
マッピングに利用する UserType を指定してあげます。
<class name="Student" table="Students"> <id name="Id"> <generator class="identity"></generator> </id> <property name="Name"></property> <property name="Gender" type="Sample.NH.UserTypes.GenderStringType, Sample.NH"></property> </class>
これで enum を文字列で永続化できます。
補足
例では簡潔にするために省きましたが、整数/文字列どちらで保存する場合も、 実際にはマスタテーブル(この例だと Genders とか)を作って外部キー制約を指定しておいたほうがいいですね。
CREATE TABLE [dbo].[Students] ( [Id] INT IDENTITY NOT NULL PRIMARY KEY, [Name] NVARCHAR (100) NOT NULL, [Gender] NVARCHAR (10) NOT NULL, FOREIGN KEY (Gender) REFERENCES Genders (Value) );
CREATE TABLE [dbo].[Genders] ( [Id] INT NOT NULL PRIMARY KEY, [Value] NVARCHAR (10) NOT NULL, UNIQUE (Value) );
0 件のコメント:
コメントを投稿