标签:
最近在做项目的时候用到了NHibernate,使用它并不困难,但是很麻烦。如果我的数据库有几百张表如果想要一个个的映射岂不是很麻烦,所以这种情况下使用NHibernate就会很笨重,虽然这个ORM框架本身功能强大,但属于重量级的在使用的时候太笨重了,所以需要在项目中改良。这时候就应运而生了FluentNHibernate,它是流畅版的NHibernate,支持所有的NHibernate功能,而且还封装了配置文件的映射功能,也就是说可以将映射使用C#代码编写,这样在维护时就会很简单。
首先创建一个数据库的配置文件,刚开始使用的话手动编写太麻烦,这时候可以考虑使用自带的配置文件,在官网下载后会有一个名为Configuration_Templates的文件夹,里面有不同数据库的配置文件,可以使用它的默认设置,但是需要将名称改为Nhibernate.cfg.xml。这里使用如下的配置:
<?xml version="1.0" encoding="utf-8"?> <!-- This is the System.Data.dll provider for SQL Server --> <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> <session-factory name="KaddzvoteNHibernateFactory"> <property name="connection.driver_class"> NHibernate.Driver.SqlClientDriver </property> <property name="connection.connection_string"> Data Source=.;Initial Catalog=Mapping;Integrated Security=true;Pooling=True;Min Pool Size=20;Max Pool Size=60 </property> <property name="dialect"> NHibernate.Dialect.MsSql2005Dialect </property> <property name="current_session_context_class">thread_static</property> <property name="generate_statistics">true</property> <property name="proxyfactory.factory_class">NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate</property> <property name="query.substitutions"> true 1, false 0, yes 'Y', no 'N' </property> <!--配置是否显示sql语句,true代表显示--> <property name="show_sql">true</property> </session-factory> </hibernate-configuration>
NHibernate的基本映射和Hibernate是完全相同的,有关基本的映射这里不再详细的讨论,可以翻阅笔者的前几篇文章。下面自己做了一个小的项目Demo,演示如何使用NHibernate创建一个数据库的映射,具体的数据库结构图如下:
上图的数据库结构图中涵盖了基本的映射关系,在实际的项目中也就是上面出现的几种基本的关系,其中涵盖了一对一、多对一、多对多的关联关系,接下来将会使用FluentNHibernate来实现基本的映射关系。
添加完配置文件后使用第三方工具将数据库表导出为实体对象,也就是添加数据库表的实体对象。添加完成后继续添加数据库的映射类,添加映射类时需要继承NHibernate的ClassMap<T>类,将数据库实体放置到对象内部,这样在映射时能够直接使用,它使用的是泛型来实现的。数据库表的实体如下代码:
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using FluentNHibernate.Automapping; using FluentNHibernate.Conventions; using NHibernate; using NHibernate.Collection.Generic; namespace ClassLibrary1.mapping { public abstract class Entity { virtual public int ID { get; set; } } public class User : Entity { virtual public string Name { get; set; } public virtual string No { get; set; } public virtual UserDetails UserDetails { get; set; } } public class Project : Entity { public Project() { Task=new List<Task>(); Product=new List<Product>(); } public virtual string Name { get; set; } public virtual User User { get; set; } public virtual IList<Product> Product { get; set; } public virtual IList<Task> Task{get;protected set; } } public class Product : Entity { public Product() { Project=new List<Project>(); } public virtual IList<Project> Project { get; set; } public virtual string Name { get; set; } public virtual string Color { get; set; } } public class Task : Entity { public virtual string Name { get; set; } public virtual Project Project { get; set; } } public class UserDetails : Entity { public virtual User User { get; set; } public virtual int Sex { get; set; } public virtual int Age { get; set; } public virtual DateTime BirthDate { get; set; } public virtual decimal Height { get; set; } } }
在添加映射文件时需要继承FluentNHibernate的ClassMap<T>类,然后在类的构造函数中添加映射的方法,具体的方法是使用的lamuda表达式来映射的,方法的名称跟配置文件的名称基本一致,书写也很方便,接下来将会拆分映射来详细讲解FluentNHibernate的Mapping使用方法。
首先来看看一对一的映射关系,用户和用户信息表在实际中是一对一的关系,这两个表之间是通过使用UserID来相互关联的,它们有一个共同的ID,在插入Users表的同时也要写入UserDetails表,所以需要添加一对一的限制关系,具体的在Users和UsersDetails两表的映射方法如下代码:
public class UsersMapping : ClassMap<User> { public UsersMapping() { Table("Users"); LazyLoad(); Id(x => x.ID).Column("UserID").GeneratedBy.Identity(); HasOne(x => x.UserDetails).Cascade.All().PropertyRef("User"); Map(x => x.Name).Nullable(); Map(x => x.No).Nullable(); } }
public class UserDetailsMapping : ClassMap<UserDetails> { public UserDetailsMapping() { Table("UserDetails"); LazyLoad(); Id(x => x.ID).Column("UserID").GeneratedBy.Foreign("User"); Map(x => x.Height).Nullable(); Map(x => x.Age).Nullable(); Map(x => x.Sex).Nullable(); Map(x => x.BirthDate).Nullable(); HasOne(x => x.User).Cascade.All(); } }
using System; using System.Collections.Generic; using ClassLibrary1.mapping; using FluentNHibernate.Testing; using ClassLibrary1; using NHibernate; using NUnit.Framework; namespace UnitTestProject1 { [TestFixture] public class UnitTest1:NHConfig { [Test] public void TestUsers_UserDetails() { //get user from database User user1 = Session.Load<User>(1); //save the User data Session.Transaction.Begin(); User user=new User() { Name = "Jack", No = "12321" }; UserDetails userDetails=new UserDetails() { Age = 12, BirthDate = DateTime.Now.Date, Height = 240, Sex = 1 }; user.UserDetails = userDetails; userDetails.User = user; Session.Save(user); Session.Transaction.Commit(); } } }
一对多和多对一是相对而言的正如上例中的Projects和Tasks类似,一个Projects有多个Tasks,反过来说就是多个Tasks可能会对应一个Projects所以有时一对多的关系也就是多对一的关系,只不过是一种特殊的多对一。这里的多对一比较特殊,常见的多对一的关系比如学生和班级的关系,多个学生属于一个班级。
一对多的映射方法和一对一的映射方法其实很多地方是类似的,只不过一对多的关系里面要添加一个外键引用关系,然后在多的一端添加一个外键,在一的一端添加HasMany,映射到Projects和Tasks中就是在Tasks(多)中添加Project的外键。
首先时Tasks表的映射,因为Tasks表是多的一端,所以要添加对Projects表的外键引用关系,另外因为是一种外键引用不关系到父表的操作,所以这里可以使用Cascade.None()。
public class TasksMappping : ClassMap<ClassLibrary1.mapping.Task> { public TasksMappping() { Table("Tasks"); LazyLoad(); Id(x => x.ID).Column("TaskID").GeneratedBy.Identity(); References(x => x.Project).Nullable().Column("ProjectID").Cascade.None(); Map(x => x.Name).Nullable(); } }
public class ProjectsMapping:ClassMap<Project> { public ProjectsMapping() { Table("Projects"); LazyLoad(); Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity(); References(x => x.User).Column("UserID").Cascade.None(); Map(x => x.Name).Nullable(); HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate(); } }
[Test] public void TestOneToMany() { Project project = Session.Get<Project>(15); //save the User data Session.Transaction.Begin(); Task task = new Task() { Name ="create", Project = project }; Session.Save(task); Session.Transaction.Commit(); Task task1 = Session.Get<Task>(1); }
执行查看结果:
这里使用的一对多的关联只是单向的关联,在关联中不仅有单向的另外还有双向关联,具体使用方法这里不再详细讨论,有兴趣学习的话可以翻阅笔者的前篇文章有关Hibernate的关联关系。
在项目中常见的多对多的关系有很多,比如本例中使用的Product和Project的关系,一个Project会有有很多Product,同时一个Product也可能会在多个Project中,它们之间就形成了多对多的关联关系。反映到映射关系中,代码如下:
public class ProjectsMapping:ClassMap<Project> { public ProjectsMapping() { Table("Projects"); LazyLoad(); Id(x => x.ID).Column("ProjectID").GeneratedBy.Identity(); References(x => x.User).Column("UserID").Cascade.None(); Map(x => x.Name).Nullable(); HasMany(x => x.Task).KeyColumn("ProjectID").LazyLoad().Cascade.SaveUpdate(); HasManyToMany(x => x.Product).ParentKeyColumn("ProjectID").ChildKeyColumn("ProductID").Table("ProjectProduct"); } } public class ProductMapping : ClassMap<Product> { public ProductMapping() { Table("Product"); Id(x => x.ID).Column("ProductID").GeneratedBy.Identity(); Map(x => x.Name).Nullable(); Map(x => x.Color).Nullable(); HasManyToMany(x => x.Project).ParentKeyColumn("ProductID").ChildKeyColumn("ProjectID").Table("ProjectProduct"); } }
[Test] public void TestManyToMany() { Session.Transaction.Begin(); //get the Project ICriteria query = Session.CreateCriteria<Project>(); IList<Project> projects = query.List<Project>(); //create the Product Product product=new Product() { Name = "Product1", Color = "Red" }; product.Project = projects; Session.Save(product); Session.Transaction.Commit(); }
从上图可以看出已经获取到了与projects相关联的Product,这就是多对多的关系,在获取projects时同时获取了与它关联的Products,如果这里使用Lazyload方式的话就不会获取所有的信息,所以要根据具体的情况而定。
继续运行测试,成功执行。
查看数据库发现数据已经成功添加,如下图:
本文主要讨论了FluentNHibernate的基本使用技巧,突出讨论了一对一的双向关联映射,一对多的单向关联和多对多的双向关联关系,它们使用相当简单,因为有了FluentNHibernate,只需要了解关联的规则就可以了,从数据模型到对象模型真的就很简单了。
虽然使用FluentNHibernate在一定程度上减少了编写代码,但是并不能真正的解决代码冗余的繁琐问题,可否有一中不需要编写Mapping代码的方法来实现映射关系呢?是的FluentNHibernate还封装了一种AutoMapping方式来映射对象,是一种自动映射的方法,只需要继承实现数据库表到对象的转换规则就可以了,具体的实现方法将会在下篇文章中详细讨论。
【架构之路之ORM】--FluentNHibernate之基本映射详解
标签:
原文地址:http://blog.csdn.net/zhang_xinxiu/article/details/42131907