假设有这样一个枚举:

/// <summary>
/// 字典项类型
/// </summary>
public enum DicItemType
{
[EnumDescription("程序使用")]
Program = , [EnumDescription("用户自定义")]
Custom =
}

NHibernate默认是映射为数据库中的数字类型,也就是0或者1。当我们使用数据库管理工具(例如PLSql/Developer)直接浏览数据库里的数据时,看到的一排又一排的0或者1,可读性不太好,总是要再去找枚举定义看0代表的是什么,1代表的又是什么。当使用存储过程写一些复杂查询时,也会写 CASE WHEN ITEM_TYPE = 1 THEN ... 或者 WHERE ITEM_TYPE = 1 这样包含了神奇数字的语句,可读性同样不好。虽然可以在后面跟上注释,也还是很烦。能不能让NHibernate把枚举保存为数据库里的字符串呢?例如不是保存0或者1,而是保存为 "Program" 或者 "Custom"。答案是肯定的。NHibernate提供了自定义类型的机制,通过将枚举映射为一个自定义类型,就可以自定义从数据库中读取枚举数据和保存枚举到数据库中的操作。下面详细描述一下实现方法。环境:.Net Framework 4.0,Oracle 11g R2,NHibernate 3.3.1.4000,FluentNHibernate 1.3.0.733,使用Oracle的64位ODP Oracle.DataAccess 4.112.3.0。

新增自定义类型

首先,需要新增一个自定义类型 NullableEnumCusType,它实现 IUserType 接口。这个自定义类型告诉NHibernate要把枚举保存为数据库中的 NHibernateUtil.AnsiString.SqlType 类型,以及如何从数据库中读取和保存枚举值(经由 NullSafeGet() 和 NullSafeSet() 方法)。

using System;
using NHibernate;
using NHibernate.SqlTypes;
using NHibernate.UserTypes; namespace Zen.Framework.Data
{
public class EnumCusType<TEnum> : IUserType where TEnum:struct
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
} public object DeepCopy(object value)
{
return value;
} public object Disassemble(object value)
{
return DeepCopy(value);
} public bool Equals(object x, object y)
{
return x.Equals(y); // 由于装箱后 x == y 会总是返回 false, 所以要使用 Equals()
} public int GetHashCode(object x)
{
return x.GetHashCode();
} public bool IsMutable
{
get { return true; }
} public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
return Enum.Parse(typeof(TEnum), NHibernate.NHibernateUtil.AnsiString.NullSafeGet(rs, names[]).ToString());
} public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
NHibernate.NHibernateUtil.AnsiString.NullSafeSet(cmd, value.ToString(), index);
} public object Replace(object original, object target, object owner)
{
return target;
} public Type ReturnedType
{
get { return typeof(TEnum); }
} public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get { return new SqlType[] { NHibernateUtil.AnsiString.SqlType }; }
}
}
}

EnumCusType

使用自定义类型

在实体的枚举属性映射时,要指定上面新增的自定义枚举类型。这样就已经可以实现枚举与数据库字符串之间的映射和转换了。

Map(t => t.ItemType, "ITEM_TYPE").CustomType<EnumCusType<DicItemType>>();

配置可为空类型的枚举

对于可为空类型的属性,例如:

/// <summary>
/// 字典项类型2
/// </summary>
public virtual DicItemType? ItemType2 { get; set; }

需要在增加一个针对可为空枚举的自定义类型:

using System;
using NHibernate;
using NHibernate.SqlTypes;
using NHibernate.UserTypes; namespace Zen.Framework.Data
{
public class NullableEnumCusType<TEnum> : IUserType
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
} public object DeepCopy(object value)
{
return value;
} public object Disassemble(object value)
{
return DeepCopy(value);
} public bool Equals(object x, object y)
{
if (x == null)
{
return x == y;
}
else
{
return x.Equals(y); // 由于装箱后 x == y 会总是返回 false, 所以要使用 Equals()
}
} public int GetHashCode(object x)
{
return x.GetHashCode();
} public bool IsMutable
{
get { return true; }
} public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
var v = NHibernate.NHibernateUtil.AnsiString.NullSafeGet(rs, names[]);
if (v == null)
{
return null;
}
else
{
return Enum.Parse(typeof(TEnum).GetGenericArguments()[], v.ToString());
}
} public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
NHibernate.NHibernateUtil.AnsiString.NullSafeSet(cmd, (value == null? null : value.ToString()), index);
} public object Replace(object original, object target, object owner)
{
return target;
} public Type ReturnedType
{
get { return typeof(TEnum); }
} public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get { return new SqlType[] { NHibernateUtil.AnsiString.SqlType }; }
}
}
}

NullableEnumCusType

然后把可为空类型的实体属性映射为可为空的自定义枚举类型,这样可为空的枚举也没问题了。

Map(t => t.ItemType2, "ITEM_TYPE2").CustomType<NullableEnumCusType<DicItemType>>();

使用 EnumConvention 统一配置自定义枚举类型

之前,我们是可以在 EnumConvention 中对枚举类型进行统一配置的,也就是说,在实体映射的时候,不需要区分枚举和普通的字符型以及数字型属性,例如可以这样:

/// <summary>
/// 字典类别
/// </summary>
public class DicItemEntityMap : InputItemMap<DicItem>
{
public DicItemEntityMap()
{
Table("SYS_DIC_ITEM");
Id(t => t.Id, "DIC_ITEM_ID");
Map(t => t.IsStop, "IS_STOP");
Map(t => t.ItemType, "ITEM_TYPE");
Map(t => t.ItemType2, "ITEM_TYPE2");
References(t => t.Category, "DIC_CATEGORY_ID");
}
}

这是因为 FluentNHibernate 提供了 Convention  机制可以对各种类型进行统一配置。例如只要实现这样一个 EnumConvention 就可以对所有枚举统一配置:

public class EnumConvention : IUserTypeConvention
{
public void Accept(FluentNHibernate.Conventions.AcceptanceCriteria.IAcceptanceCriteria<FluentNHibernate.Conventions.Inspections.IPropertyInspector> criteria)
{
// 匹配枚举或可为空枚举
criteria.Expect(x => x.Property.PropertyType.IsEnum
||
(x.Property.PropertyType.Name == "Nullable`1" &&
x.Property.PropertyType.GetGenericArguments().Length > &&
x.Property.PropertyType.GetGenericArguments()[].IsEnum));
} public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
instance.CustomType(instance.Property.PropertyType);
}
}

EnumConvention

能不能把上面的 EnumConvention 改造一下,适配新增的 EnumCusType 和 NullableEnumCusType 呢?这样就不用每次都写 ” Map(t => t.ItemType, "ITEM_TYPE").CustomType<EnumCusType<DicItemType>>() “ 这样烦人的配置了。

但是你马上就会发现 EnumCusType 是非常坑爹的泛型,在 EnumConvention 里没法直接调用 instance.CustomType() 进行配置。好在微软提供了非常强大的Emit,动态生成一个泛型类型非常轻松:

public class EnumConvention : IUserTypeConvention
{
public void Accept(FluentNHibernate.Conventions.AcceptanceCriteria.IAcceptanceCriteria<FluentNHibernate.Conventions.Inspections.IPropertyInspector> criteria)
{
// 匹配枚举或可为空枚举
criteria.Expect(x => x.Property.PropertyType.IsEnum
||
(x.Property.PropertyType.Name == "Nullable`1" &&
x.Property.PropertyType.GetGenericArguments().Length > &&
x.Property.PropertyType.GetGenericArguments()[].IsEnum));
} public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
//instance.CustomType(instance.Property.PropertyType);
if (instance.Property.PropertyType.Name == "Nullable`1")
{
// 转换为可空枚举自定义类型
Type t = TypeBuilder.GetType(
string.Format("Zen.Framework.Data.NullableEnumCusType`1[[{0}]]",
instance.Property.PropertyType.AssemblyQualifiedName)); instance.CustomType(t);
}
else
{
// 转换为非可空枚举自定义类型
Type t = TypeBuilder.GetType(
string.Format("Zen.Framework.Data.EnumCusType`1[[{0}]]",
instance.Property.PropertyType.AssemblyQualifiedName)); instance.CustomType(t);
}
}
}

使用Emit配置泛型自定义类型

配置NHibernate将枚举保存为Oracle数据库中的字符串的更多相关文章

  1. ||在oracle数据库中起到字符串拼接的作用

    例子:select org.id from org where inner_code like '12011601001' || '%' ||在oracle数据库中起到字符串拼接的作用,上面等同于'1 ...

  2. Oracle数据库中调用Java类开发存储过程、函数的方法

    Oracle数据库中调用Java类开发存储过程.函数的方法 时间:2014年12月24日  浏览:5538次 oracle数据库的开发非常灵活,不仅支持最基本的SQL,而且还提供了独有的PL/SQL, ...

  3. 详解大数据采集引擎之Sqoop&采集oracle数据库中的数据

    一.Sqoop的简介: Sqoop是一个数据采集引擎/数据交换引擎,采集关系型数据库(RDBMS)中的数据,主要用于在RDBMS与HDFS/Hive/HBase之间进行数据传递,可以通过sqoop i ...

  4. Oracle数据库中,误删除或者修改数据恢复方法

    在我们实际工作中,误删除或者修改Oracle数据库中的数据,怎么办呢?这里给大家分享一种解决办法.假如你误操作的时间不超过30分钟(数据库默认的回滚保持段里的数据时间,可以在pl/sql执行窗口按ct ...

  5. 将Oracle数据库中的数据写入Excel

    将Oracle数据库中的数据写入Excel 1.准备工作 Oracle数据库"TBYZB_FIELD_PRESSURE"表中数据如图: Excel模板(201512.xls): 2 ...

  6. --关于null在oracle数据库中是否参与计算,进行验证,

    --关于null在oracle数据库中是否参与计算,进行验证,with td as (select null id,1 name from dual ),td1 as ( select null id ...

  7. Oracle数据库中SYS、SYSTEM、DBSNMP、SYSMAN四用户的区别

    [转]   SYS.SYSTEM.DBSNMP. Oracle 数据库中 SYS.SYSTEM.DBSNMP.SYSMAN 四用户的区别 用户: SYS 用户: SYS,默认密码为 CHANGE_ON ...

  8. 【转】Oracle数据库中Sequence的用法

    在Oracle数据库中,sequence等同于序列号,每次取的时候sequence会自动增加,一般会作用于需要按序列号排序的地方. 1.Create Sequence (注释:你需要有CREATE S ...

  9. oracle数据库中提供的5种约束

    约束作用:用来保持数据的完整性,防止无效数据进入到数据库中.oracle数据库中提供的5种约束,都是限定某个列或者列的组合的.1.主键约束(PRIMARY KEY):在一个表中能唯一的标识一行.主键可 ...

随机推荐

  1. mac中显示隐藏文件

    显示隐藏文件 defaults write com.apple.finder AppleShowAllFiles -bool ture 重新隐藏 defaults write com.apple.fi ...

  2. 高性能的JavaScript--数据访问(1)

    写在前面 数据存储在哪里,关系到代码运行期间数据被检索到的速度.在JavaScript中,此问题相对简单,因为数据存储只有少量方式可供选择.正如其他语言那样,数据存储位置关系到访问速度.在JavaSc ...

  3. 用WinForm写的员工考勤项目!!!!!!

    先说几句,作为一个还在学习的程序员,掌握的知识有限:但我利用自身所学,给一些像我一样还在学习的码农提供我的绵薄之力! 写的不好,但是尽力了,希望大牛指点.多多吐槽!!! 好了开始说项目需求: 实现新增 ...

  4. fragment嵌套问题

    fragment嵌套里面不能用再用getActivity().getFragmentManager()要用getChildFragmentManager()

  5. Good Bye 2016 - B

    题目链接:http://codeforces.com/contest/750/problem/B 题意:地球的子午线长度为40000,两极点的距离为20000.现在你从北极出发,按照题目输入方式来走. ...

  6. Apple、Google、Microsoft的用户体验设计原则

    轻巧的Apple 注重设计过程: 在设计过程中引入用户交互的5个目标: 了解您的目标客户 分析用户的工作流 构造原型系统 观察用户测试 制定观察用户准则 做出设计决定 避免功能泛滥 80% 方案 优秀 ...

  7. Web API返回JSON数据

    对Web API新手来说,不要忽略了ApiController 在web API中,方法的返回值如果是实体的话实际上是自动返回JSON数据的例如: 他的返回值就是这样的: { "Conten ...

  8. [软件推荐]快速文件复制工具(Limit Copy) V4.0 绿色版

    快速文件复制工具(Limit Copy)绿色版是一款智能变频超快复制绿色软件. 快速文件复制工具(Limit Copy)功能比较完善,除了文件复制还可以智能变频,直接把要复制的文件拖入窗口即可,无需手 ...

  9. XVI Open Cup named after E.V. Pankratiev. GP of Ukraine

    A. Associated Vertices 首先求出SCC然后缩点,第一次求出每个点能到的点集,第二次收集这些点集即可,用bitset加速,时间复杂度$O(\frac{nm}{64})$. #inc ...

  10. [翻译svg教程]Path元素 svg中最神奇的元素!

    先看一个实例 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999 ...