NHibernate3.2+Asp.net MVC3+Extjs 4.0.2项目实践(二): NHibernate数据访问层实现
关于NHibernate的ORM映射可以通过Hbm映射文件来完成,代码生成工具使得这一步骤变得简化;而NHibernate3.2版本集成Mapping-By-Code(代码映射),不同于其他映射方式,具体可以参考李永京博客(NHibernate剖析:Mapping篇之Mapping-By-Code),此处不再赘述;Mapping-By-Code采用手动配置实体映射,顾名思义会增加程序代码编写量,而且数据库表关系映射(ont-to-many, many-to-one,many-to-many)编写比较复杂,所以我摒弃这种方式而采用了Mindscape NHibernate Model Designer设计工具。为此,首先介绍一下NHibernate数据访问层,Web.config文件配置、NHibernate Configuration类和Session Manager类。
Web.config<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
<add key="TESZConnectionString" value="User id=sa;Password=sql123!!;data source=localhost;persist security info=True;initial catalog=TESZ_NEW;"/>
</appSettings>
Web.config是采用appSettings来设置数据库connection string,而非NHiberate标准配置。
NHibernateCfg.cspublic static class NHibernateCfg
{
private static string _ConnectionString;
public static Configuration GetConfiguration()
{
_ConnectionString = System.Configuration.ConfigurationManager.AppSettings["TESZConnectionString"].ToString();
var configure = new Configuration();
configure.SessionFactoryName("TESZ_NEW");
configure.DataBaseIntegration(db =>
{
db.Dialect<MsSql2005Dialect>();
db.Driver<SqlClientDriver>();
db.ConnectionString = _ConnectionString;
});
ApplyConfiguration(configure);
return configure;
}
public static void ApplyConfiguration(Configuration configuration)
{
configuration.AddXml(Location_Country.MappingXml.ToString());
configuration.AddXml(Location_StateProvince.MappingXml.ToString());
configuration.AddXml(Location_City.MappingXml.ToString());
configuration.AddXml(Common_Date.MappingXml.ToString());
configuration.AddXml(System_Purview.MappingXml.ToString());
configuration.AddAssembly(typeof(NHibernateCfg).Assembly);
}
}
此处在NHibernateCfg类中进行数据库连接属性的配置,并把由Mindscape NHibernate Model Designer工具生成的实体类中的MappingXml配置给Configuration,来进行ORM映射。
SessionManager.cs/// <summary>
/// 使用 NHibernate 操作数据库的Session
/// </summary>
public sealed class SessionManager
{
private const string CurrentSessionKey = "nhibernate.current_session";
private static readonly ISessionFactory sessionFactory;
private static Configuration conf = null;
private static ModelMapper mapper = null;
static SessionManager()
{
mapper = new ModelMapper();
if (sessionFactory == null)
{
conf = NHibernateCfg.GetConfiguration();
sessionFactory = conf.BuildSessionFactory();
}
}
public static ISession GetCurrentSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items[CurrentSessionKey] as ISession;
if (currentSession == null)
{
currentSession = sessionFactory.OpenSession();
context.Items[CurrentSessionKey] = currentSession;
}
else
{
currentSession = sessionFactory.OpenSession();
}
return currentSession;
}
public static void CreateDataTable()
{
//配置数据库a
SchemaMetadataUpdater.QuoteTableAndColumns(conf);
//创建数据库
new SchemaExport(conf).Create(false, true);
}
public static void DropDataTable()
{
//配置数据库
SchemaMetadataUpdater.QuoteTableAndColumns(conf);
//删除数据库
new SchemaExport(conf).Drop(false, true);
}
public static void CloseSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items[CurrentSessionKey] as ISession;
if (currentSession == null)
{
// No current session
return;
}
currentSession.Close();
context.Items.Remove(CurrentSessionKey);
}
public static void CloseSessionFactory()
{
if (sessionFactory != null)
{
sessionFactory.Close();
}
}
}
以上是NHibernate Session管理类。
接着就该讲如何通过Mindscape NHibernate Model Designer工具生成实体类了。具体方法是:选择新建项,在“已安装的模版”的数据分类下选择NHibernate Model,然后从“服务器资源管理器”中拖拽表至Mindscape设计器中,并可以通过NHibernate Model工具栏的Model->Entities->实体类的属性进行实体类的更名。生成的实体类如下图:
Location_Country.cs[System.CodeDom.Compiler.GeneratedCode("NHibernateModelGenerator", "1.0.0.0")]
public partial class Location_Country
{
public virtual int CountryId { get; set; }
public virtual string CountryCode1 { get; set; }
public virtual string CountryCode2 { get; set; }
public virtual System.Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual string ModifiedBy { get; set; }
public virtual string CountryDialCode { get; set; }
public virtual string CountryName { get; set; }
private IList<Location_StateProvince> _locationStateProvinces = new List<Location_StateProvince>();
public virtual IList<Location_StateProvince> LocationStateProvinces
{
get { return _locationStateProvinces; }
set { _locationStateProvinces = value; }
}
static partial void CustomizeMappingDocument(System.Xml.Linq.XDocument mappingDocument);
public static System.Xml.Linq.XDocument MappingXml
{
get
{
var mappingDocument = System.Xml.Linq.XDocument.Parse(@"<?xml version='1.0' encoding='utf-8' ?>
<hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'
assembly='" + typeof(Location_Country).Assembly.GetName().Name + @"'
namespace='TESZ.Data.Model'
>
<class name='Location_Country'
table='`Location_Country`'
>
<id name='CountryId'
column='`CountryId`'
>
<generator class='identity'>
</generator>
</id>
<property name='CountryCode1'
column='`CountryCode1`'
/>
<property name='CountryCode2'
column='`CountryCode2`'
/>
<property name='ModifiedDate'
column='`ModifiedDate`'
/>
<property name='ModifiedBy'
column='`ModifiedBy`'
/>
<property name='CountryDialCode'
column='`CountryDialCode`'
/>
<property name='CountryName'
column='`CountryName`'
/>
<bag name='LocationStateProvinces'
inverse='true'
>
<key column='`CountryId`' />
<one-to-many class='Location_StateProvince' />
</bag>
</class>
</hibernate-mapping>");
CustomizeMappingDocument(mappingDocument);
return mappingDocument;
}
}
}
此处请注意一下三项(以上实体类已经经过修改):
1.自动生成的实体类文件中,需要删除ConfigurationHelper类,因为之前在NHibernateCfg中已经配置MappingXml;
2.实体类中的internal static System.Xml.Linq.XDocument MappingXml的internal需改为public,否则NHibernateCfg的configuration.AddXml(Location_Country.MappingXml.ToString());会报错;
3.如果数据表有one-to-many关系,则必有many-to-one关系,MappingXml中会自动生成;比如<many-to-one name='Country' class='Location_Country' column='`CountryId`' />,则需要在其中添加lazy='false' 属性,否则实体类Location_StateProvince通过mant-to-one关系访问实体Location_Country时会抛出异常;
4.如果数据表结构发生变化,可以在Mindscape设计器中以Update Model from Database来更新实体类,之后还要重复以上三个步骤。
如需自动生成one-to-many和many-to-one关系,则在进行数据库创建时,要配置数据表对应关系,如下图:
数据表关系设计最后简单讲一下数据访问接口和接口实现,在这儿我就不仔细说了,直接贴代码吧!
ILocation_CountryRepository.cspublic interface ILocation_CountryRepository
{
IList<Location_Country> FindAll();
IList<Location_Country> FindIndistinct(Country_Query_Index country_Query_Index , object o);
bool Create(Location_Country location_Country);
bool Update(Location_Country location_Country);
bool Delete(Location_Country location_Country);
Location_Country FindOne(Country_Query_Index country_Query_Index, object o);
}
通过QueryOver和Lambda表达式进行数据查询
Location_CountryRepository.cspublic class Location_CountryRepository : ILocation_CountryRepository
{
public Location_CountryRepository()
{
}
public IList<Location_Country> FindAll()
{
using (var session = SessionManager.GetCurrentSession())
{
var query = session.QueryOver<Location_Country>()
.OrderBy(p => p.CountryId).Asc
.List();
return query;
}
}
//模糊查询
public IList<Location_Country> FindIndistinct(Country_Query_Index country_Query_Index, object o)
{
using (var session = SessionManager.GetCurrentSession())
{
IList<Location_Country> query=null;
switch (country_Query_Index)
{
case Country_Query_Index.CountryName:
{
query = session.QueryOver<Location_Country>()
.WhereRestrictionOn(k => k.CountryName).IsLike(o)
.OrderBy(p => p.CountryId).Asc
.List();
break;
}
case Country_Query_Index.Code1:
{
query = session.QueryOver<Location_Country>()
.WhereRestrictionOn(k => k.CountryCode1).IsLike(o)
.OrderBy(p => p.CountryId).Asc
.List();
break;
}
case Country_Query_Index.Code2:
{
query = session.QueryOver<Location_Country>()
.WhereRestrictionOn(k => k.CountryCode2).IsLike(o)
.OrderBy(p => p.CountryId).Asc
.List();
break;
}
case Country_Query_Index.DialCode:
{
query = session.QueryOver<Location_Country>()
.WhereRestrictionOn(k => k.CountryDialCode).IsLike(o)
.OrderBy(p => p.CountryId).Asc
.List();
break;
}
}
return query;
}
}
public Location_Country FindOne(Country_Query_Index country_Query_Index,object o)
{
using (var session = SessionManager.GetCurrentSession())
{
Location_Country query = null;
switch (country_Query_Index)
{
case Country_Query_Index.CountryId:
{
query = session.QueryOver<Location_Country>()
.Where(p => p.CountryId==(int)o)
.SingleOrDefault();
break;
}
case Country_Query_Index.CountryName:
{
query = session.QueryOver<Location_Country>()
.Where(p => p.CountryName == o.ToString())
.SingleOrDefault();
break;
}
case Country_Query_Index.Code1:
{
query = session.QueryOver<Location_Country>()
.Where(p => p.CountryCode1 == o.ToString())
.SingleOrDefault();
break;
}
case Country_Query_Index.Code2:
{
query = session.QueryOver<Location_Country>()
.Where(p => p.CountryCode2 == o.ToString())
.SingleOrDefault();
break;
}
case Country_Query_Index.DialCode:
{
query = session.QueryOver<Location_Country>()
.Where(p => p.CountryDialCode == o.ToString())
.SingleOrDefault();
break;
}
}
return query;
}
}
public bool Create(Location_Country model)
{
bool result = false;
using (var session = SessionManager.GetCurrentSession())
{
using (var trans = session.BeginTransaction())
{
session.Save(model);
trans.Commit();
result = true;
}
}
return result;
}
public bool Update(Location_Country model)
{
bool result = false;
using (var session = SessionManager.GetCurrentSession())
{
using (var trans = session.BeginTransaction())
{
session.Update(model);
trans.Commit();
result = true;
}
}
return result;
}
public bool Delete(Location_Country model)
{
bool result = false;
using (var session = SessionManager.GetCurrentSession())
{
using (var trans = session.BeginTransaction())
{
session.Delete(model);
trans.Commit();
result = true;
}
}
return result;
}
}