NHibernate已经足够了吗?
并且在博客园开了一个专栏写了一系列的文章来介绍如何使用它。到2005年后半年的时候,国内类似的代码生成工具已经开始“泛滥”了,O/RM一下子流行起来。在2005年底,我开始关注NHibernate,并逐渐转移到NHibernate上面来,对XCodeFactory的依赖也越来越小了。毕竟,NHibernate是如此得强大,以至于我不再需要XCodeFactory。但是,对于经常要编写数据库访问代码的.NET开发人员而言,拥有NHibernate就已经足够了吗?我认为是不够的。NHibernate在基于对象方面做得非常好,这也是它初始的目标,它非常出色地实现了这个目标。但是在O/RM之外了?比如,我们需要进行批量的更新、删除等,使用NHibernate进行类似的操作不仅繁琐而且效率低下。当然,这些内容不是NHibernate的职责,NHibernate也不用关心这些O/RM之外的东西。但是,我们也许都知道了,NHibernate需要另外一个工具来作为有力的补充,这个补充主要基于关系、而不是对象来操作数据库中的数据。有人选择了EnterpriseLibrary中的数据访问Block作为O/RM之外的补充,这很不错。而DataRabbit是另外一个选择。相信来我blog的朋友都一定知道ESFramework,ESFramework源于EnterpriseServerBase类库的Network命名空间,而DataRabbit来源于EnterpriseServerBase类库的DataAccess命名空间,EnterpriseServerBase.DataAccess也是XCodeFactory核心库。DataRabbit中包含多个基于关系的数据访问接口,并且内置了对SqlServer、Oracle、Ole的支持,如果要支持其它类型的数据库,只需实现相应的接口即可,并且可以插件的形式提供。(1)简化通常的数据访问,体现在IADOBase接口,IADOBase接口和以下所有接口均独立于数据库类型。(2)数据分页 IPagerManager(3)事务 Transaction(4)分布式事务 Distributed(5)操作数据大纲(DataSchema) IDataSchemaOperator ,比如获取某个表的大纲结构(Column信息、主键信息、外键信息),依据大纲在指定的数据库中创建表(6)数据库适配器插件 IADOBaseAddin(7)简单数据迁移 SimpleDataTransfer这些接口将在后面的系列文章中逐一介绍。下面也看一个分页的例子,来点感性的认识。
什么是hibernate
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任。扩展资料:发展历程2004年,整个Java社区开始从实体bean向Hibernate转移,特别是在Rod Johnson的著作《Expert One-on-One J2EE Development without EJB》出版后,由于这本书以扎实的理论、充分的论据和详实的论述否定了EJB,提出了轻量级敏捷开发理念之后,以Hibernate和Spring为代表的轻量级开源框架开始成为Java世界的主流和事实标准。在2004年Sun领导的J2EE5.0标准制定当中的持久化框架标准正式以Hibernate为蓝本。2006年,J2EE5.0标准正式发布以后,持久化框架标准Java Persistent API(简称JPA)基本上是参考Hibernate实现的,而Hibernate在3.2版本开始,已经完全兼容JPA标准。参考资料来源:百度百科-开放源代码参考资料来源:百度百科-Hibernate
ASP.NET项目实际开发中,是使用传统的ADO.NET的多还是使用NHibernate的多?
怎么说呢 一般的项目开发是 ADO.NET 是传统的数据库连接类型 繁琐复杂而 Nhibernate 来源于非常优秀的基于Java的Hibernate 关系型持久化工具NHibernate 从数据库底层来持久化你的.Net 对象到关系型数据库。NHibernate 为你处理这些,远胜于你不得不写SQL去从数据库存取对象。你的代码仅仅和对象关联,NHibernat 自动产生SQL语句,并确保对象提交到正确的表和字段中去
如何使用Fluent Nhibernate中的Automapping进行OR Mapping映射
由于在项目中使用了NHibernate来作为ORMapping构建数据访问层,那么就必须要配置Object和DataTable的映射。最早
的项目中,我们使用了最传统的XML配置文件的方式编写映射关系,但是这样太麻烦,每次修改class和表时都要去修改对应的XML文件,而且还容易出
错,一定有疏忽遗漏的地方,还不容易找出错误,所以在第二个项目中,我们使用了Fluent
NHibernate的Mapping方式代替XML配置。使用Fluent NHibernate的最大好处是降低了出错的机会,因为Fluent
Nhibernate的配置是使用C#来编写,可以智能感知,而且还能编译,不像原始的XML配置,写错了都不知道。
public sealed class ConfigMapping : ClassMap
{
public ConfigMapping()
{
Table("CONFIG");
Id(x => x.Id, "CONFIG_ID").GeneratedBy.HiLo("1000000000");
Map(x => x.ConfigKey, "CONFIG_KEY");
Map(x => x.ConfigValue, "CONFIG_VALUE");
}
}
但是使用Fluent
NHibernate的配置方式仍然是需要编写Mapping代码的,也就意味着,如果我更改class或者DataTable的时候,还要对应的更改该
Mapping文件。更多的修改意味着更多的风险,为了减少这方面的风险,同时为了减少配置的工作量,所以在最新的项目中采用了Fluent
NHibernate中的Automapping。我们只需要定义好映射的规则,就可以不对每个表和类分别编写映射配置,而是按照规则进行自动的
Mapping工作。这样在修改class或者DataTable时,只需要修改类和表即可,不需要再修改配置文件。
要做到Automapping,就一定要定义好严格的命名规范,然后按照规范编写Automapping规则,实现自动化的映射。比如我们可以定义如下的规则:
类名和字段名采用每个单词首字母大写的方式而数据库表名和列名使用全部大写,单词之间下划线分割的方式。(比如CostCenter类对应表COST_CENTER)
类中的主键使用Id命名,表中的主键使用表名+“_ID”的命名方式。(比如CostCenter中有public virtual long Id{get;set;},对应表中的列COST_CENTER_ID)
对于一对多的关系,使用父方的类名作为属性名,表中使用父表的主键列名作为对应的外键列的列名。(比如一个班对应多个学生,在Class类中就有
public virtual IList
Students{get;set;},而在Student类中就必须使用Class作为属性名:public virtual Class
Class{get;set;})
对于SubClass,采用将多个子对象都存在同一个表中的方式实现,使用“TYPE”列作为DiscriminatorColumn,使用之类的类名作为子类的唯一标识。
对于多对多的关系,把两个类对应的表名进行排序,将小的排前面,然后将两个表名连接起来,中间使用“_”分割。(比如Course和Student是多对多关系,那么产生的中间表表名为COURSE_STUDENT)
对于枚举,在数据库中使用tinyint也就是一个Byte来存储,枚举在Automapping中作为UserType进行处理。
下面就来编写Automapping的转换规则,首先对String写一个扩展方法,实现CostCenter到COST_CENTER的转换:
static string ToDatabaseName(this string s)
{
return Regex.Replace(s, @"\B[A-Z]", match => "_" + match.ToString()).ToUpper();
}
对于1,需要实现IClassConvention实现如下:
public class ClassNameConvention : IClassConvention
{
public virtual void Apply(IClassInstance instance)
{
var tableName = instance.EntityType.Name.ToDatabaseName();
instance.Table(tableName);
}
}
同时对于列,需要使用IPropertyConvention接口,实现如下:
public class PropertyConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Column(instance.Name.ToDatabaseName());
}
}
对于2,需要实现IIdConvention接口,另外我们采用的是Hilo值的主键生成方式,使用一个表HIBERNATE_UNIQUE_KEY存储每个表的流水。具体实现如下:
public class PrimaryKeyConvention : IIdConvention
{
public const string NextHiValueColumnName = "VALUE";
public const string NHibernateHiLoIdentityTableName = "HIBERNATE_UNIQUE_KEY";
public const string TableColumnName = "TABLE_NAME";
public virtual void Apply(IIdentityInstance instance)
{
var tableName = instance.EntityType.Name.ToDatabaseName();
instance.Column(tableName + "_ID");//这里设置主键的命名为表名+“_ID”
if (instance.Type == typeof(long))//接下来设置主键的生成方式为HiLo值方式
{
instance.GeneratedBy.HiLo(
NHibernateHiLoIdentityTableName,
NextHiValueColumnName,
"1000000000",
builder => builder.AddParam("where", string.Format("{0} = '{1}'", TableColumnName, tableName)));
}
}
}
对于3,一对多的情况,需要设置“一”的一方的Collection和“多”的一方的Reference,具体如下:
public class CollectionConvention : ICollectionConvention
{
public void Apply(ICollectionInstance instance)
{
string colName;
var entityType = instance.EntityType;
var childType = instance.ChildType;
if (entityType == childType)//这里是专门对自身关联一对多的情况进行特殊处理,统一使用PARENT_ID作为外键列
colName = "PARENT_ID";
else
{
colName = entityType.Name.ToDatabaseName() + "_ID";
}
instance.Key.Column(colName);
instance.Cascade.AllDeleteOrphan();
}
}
public class ReferenceConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
string colName = null;
var referenceType = instance.Class.GetUnderlyingSystemType();
var entityType = instance.EntityType;
var propertyName = instance.Property.Name;
//Self association
if (referenceType == entityType)
colName = "PARENT_ID";
else
colName = propertyName.ToDatabaseName() + "_ID";
instance.Column(colName);
}
}
对于4SubClass的处理,需要涉及到指定要进行Discriminate的类,还有DiscriminateColumn,然后指定DiscriminateColumn中如何对Subclass进行Mapping。
这里就需要重写DefaultAutomappingConfiguration类,在该类中指定主键、Discriminate的类等,具体代码如下:
public class AutoMapConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return (type.IsClass && type.Namespace.StartsWith("OurProject.Core.Model"));//指定了哪些类是要进行AutoMapping的
}
public override bool IsId(Member member)
{
return member.Name == "Id";//指定了每个类中的Id属性就是该类的主键
}
public override bool IsDiscriminated(Type type)//指定了哪些类是需要进行SubClass继承,将其SubClass都存放在一个表中的。
{
return type.In(
typeof(Client),
typeof (Classification),
typeof (MultiClassification)
);
}
public override string GetDiscriminatorColumn(Type type)
{
return "TYPE";//指定了SubClass的区分列就是有一个叫做TYPE的列
}
}
然后就是关于DiscriminateColumn中的值如何映射成对应的Subclass,需要实现ISubclassConvention接口,代码如下:
public class SubclassConvention : ISubclassConvention
{
public void Apply(ISubclassInstance instance)
{
instance.DiscriminatorValue(instance.EntityType.Name);
}
}
对于5多对多,就需要实现IHasManyToManyConvention接口,在这个接口中对两个表名进行排序,然后进行连接表示中间表。具体代码如下:
public class HasManyToManyConvention : IHasManyToManyConvention
{
public void Apply(IManyToManyCollectionInstance instance)
{
var entityDatabaseName = instance.EntityType.Name.ToDatabaseName();
var childDatabaseName = instance.ChildType.Name.ToDatabaseName();
var name = GetTableName(entityDatabaseName, childDatabaseName);//对两个表名进行排序,然后连接组成中间表名
instance.Table(name);
instance.Key.Column(entityDatabaseName + "_ID");
instance.Relationship.Column(childDatabaseName + "_ID");
}
private string GetTableName(string a, string b)
{
var r = System.String.CompareOrdinal(a, b);
if (r > 0)
{
return "{0}_{1}".Fill(b, a);
}
else
{
return "{0}_{1}".Fill(a, b);
}
}
}
对于6枚举的处理,需要指定枚举为UserType,实现接口IUserTypeConvention,具体代码如下:
public class EnumConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
public void Apply(IPropertyInstance instance)
{
instance.CustomType(instance.Property.PropertyType);
}
}
实现了以上这几个接口,那么大部分情况下的Automapping都可以实现了。最后是将这些接口通知给FluentNhibernate,让其应用这些接口,导入指定Assembly中的DomainModel,具体的实现方法是:
public virtual AutoPersistenceModel Generate(string[] domainAssemblies, string[] dalAssemblies)
{
var mappings = AutoMap.Assemblies(
new AutoMapConfiguration(), domainAssemblies.Select(Assembly.LoadFrom).ToArray());
foreach (var ignoredBaseType in IgnoredBaseTypes)
{
mappings.IgnoreBase(ignoredBaseType);
}
foreach (var includeBaseType in IncludeBaseTypes)
{
mappings.IncludeBase(includeBaseType);
}
mappings.Conventions.Setup(GetConventions());//指定了Automapping转换的接口实现
foreach (var dalAssembly in dalAssemblies)
{
mappings.UseOverridesFromAssembly(Assembly.LoadFrom(dalAssembly));
}
return mappings;
}
public static IList IgnoredBaseTypes = new List//这里列出的类都是些Base类,不会Mapping到具体某个表
{
typeof (Entity) //该对象其实就只有Id这个属性,作为所有要Mapping的类的父类
};
public static IList IncludeBaseTypes = new List//默认情况下抽象类是不会Mapping成表的,所以这里需要指明这些类是要Mapping成表的
{
typeof (Classification),
typeof (MultiClassification),
typeof(Client)
};
protected Action GetConventions()
{
return finder =>
{
finder.Add();
finder.Add();
finder.Add();
finder.Add();
finder.Add();
finder.Add();
finder.Add();
finder.Add();
finder.Add();
finder.Add();
};
}
该方法返回了一个AutoPersistenceModel,使用这个对象注册到NHibernate中即可。
Entity Framework和NHibernate的区别是什么?
1.可以这么说。。都是ORM框架不过,EF秉承微软的出品的一贯作风,很难容易上手。另外,EF可以很方便的按照数据库生成实体类,而NH默认没有这个能力,必须用其他代码生成方案。我目前对两者都不是很深入,但是从学习成本上看,NH的成本远超EF。而在性能方面,我个人认为二者没有绝对差距。。。关键还是编程人员的应用,以及调优。基本上都会说linq to sql是轻量级的框架,而ef,nh则是重量级的。。。引用一段“LINQ to SQL和Entity Framework各有所长,LINQ to SQL是一个轻量级的ORM框架,旨在为Microsoft SQL Server数据库提供快速的应用程序开发,其优点是易于使用、简单、高性能。而Entity Framework的优点在于:其为创建数据库架构和实体类之间的映射提供了更好的灵活性,它还通过提供程序支持除了SQL Server之外的第三方数据库。"我是没看出来。。而且我个人觉得单从易用性来看,ef以及相当简单了。。
NHibernate和LINQ哪个好
相似点并不多,异同如下……建议按需求选择!NHibernate是ORM,是把Java的Hibernate核心部分移植到Microsoft .NET Framework上。它是一个对象关系映射工具,其目标是把.NET对象持久化到关系数据库。LINQ是一种技术,可以对数据进行查询,可以对XML,Object,DataSet,SQL,进行查询,以下是MSDN的定义语言集成查询 (LINQ) 是一组技术的名称,这些技术建立在将查询功能直接集成到 C# 语言(以及 Visual Basic 和可能的任何其他 .NET 语言)的基础上。借助于 LINQ,查询现在已是高级语言构造,就如同类、方法、事件等等。而LINQ TO SQL则是ORM,实现和NHibernate类似的功能,实现对象关系映射,不过没有NHibernate强大,只能支持SQL ServerEF的话是微软开发的ORM框架