NHibernate考察系列 02 使用入门 简单映射
NHibernate入门示例。
测试项目目录结构如下
建立一个Company类用来测试,对应的表为TBLCOMPANY。
1. 下载NHibernate(版本1.2.0.CR1),将NHibernate \bin\net-2.0下面的文件拷贝到lib目录。
2. 为实体建立Class Library的Domain工程。为工程添加\lib\Iesi.Collections.dll文件的引用(以后用)。
3. Company类的代码如下
为Domain工程添加Company.hbm.xml映射文件,在文件的属性->Build Action中选择Embedded Resource,文件内容如下
6. 将NHibernate源代码中的nhibernate-configuration.xsd文件拷贝到NHTest工程的目录下,也可以拷贝到VS 2005的系统目录,这是因为配置文件使用这个xsd进行校验、输入提示等。
在NHTest的App.config文件中添加如下配置:
7. 写代码测试。测试代码如下:
即使是最简单的例子,我们也可以发现一些东西。
如果你一边单步执行,一边打开SQL Server的SQL Profiler进行监控(监控的Event为Stored Procedures中的RPC:Completed),可以发现,在两个session.Save()方法以及session.Get<>()方法处,并没有产生任何跟数据库进行交互的SQL语句,而只有在trans.Commit()的时候,才会产生下面这样两条SQL:
在上面测试的基础上,运行如下的测试代码
另外一点需要注意,NHibernate不允许修改主键值,如果有修改,在事务提交时会丢出一个异常。这个特性在使用业务上有意义的字段作为主键时可能会面临一些问题,不过从另一个方面看,就算这种情况下NH允许修改主键,因为其它对象可能已经根据这个主键值引用该对象,所以修改同样会造成问题。至少目前我接触的项目中,对象的业务主键是不允许修改的。
测试项目目录结构如下
建立一个Company类用来测试,对应的表为TBLCOMPANY。
1. 下载NHibernate(版本1.2.0.CR1),将NHibernate \bin\net-2.0下面的文件拷贝到lib目录。
2. 为实体建立Class Library的Domain工程。为工程添加\lib\Iesi.Collections.dll文件的引用(以后用)。
3. Company类的代码如下
using System;
using Iesi.Collections.Generic;
namespace NH12.MyExample.Domain
{
#region Company
public class Company
{
private string _companyID;
private string _companyName;
public Company(string id, string name)
{
_companyID = id;
_companyName = name;
}
public Company()
{
}
public virtual string CompanyID
{
get { return _companyID; }
set { _companyID = value; }
}
public virtual string CompanyName
{
get { return _companyName; }
set { _companyName = value; }
}
#region override
public override bool Equals(object obj)
{
if (this == obj) return true;
if (obj == null || obj.GetType() != this.GetType())
return false;
Company company = obj as Company;
return company != null && company.CompanyID == _companyID;
}
public override int GetHashCode()
{
return _companyID.GetHashCode();
}
public override string ToString()
{
return _companyID;
}
#endregion
}
#endregion
}
4. 将NHibernate源代码中的nhibernate-mapping.xsd文件拷贝到Domain工程的目录下,也可以拷贝到VS 2005的系统目录,这是因为映射文件使用这个xsd进行校验、输入提示等。using Iesi.Collections.Generic;
namespace NH12.MyExample.Domain
{
#region Company
public class Company
{
private string _companyID;
private string _companyName;
public Company(string id, string name)
{
_companyID = id;
_companyName = name;
}
public Company()
{
}
public virtual string CompanyID
{
get { return _companyID; }
set { _companyID = value; }
}
public virtual string CompanyName
{
get { return _companyName; }
set { _companyName = value; }
}
#region override
public override bool Equals(object obj)
{
if (this == obj) return true;
if (obj == null || obj.GetType() != this.GetType())
return false;
Company company = obj as Company;
return company != null && company.CompanyID == _companyID;
}
public override int GetHashCode()
{
return _companyID.GetHashCode();
}
public override string ToString()
{
return _companyID;
}
#endregion
}
#endregion
}
为Domain工程添加Company.hbm.xml映射文件,在文件的属性->Build Action中选择Embedded Resource,文件内容如下
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NH12.MyExample.Domain" assembly="Domain">
<class name="Company" table="TBLCOMPANY">
<id name="CompanyID" >
<column name="COMPANY_ID" sql-type="nvarchar" length="4" not-null="true"/>
<generator class="assigned" />
</id>
<property name="CompanyName">
<column name="COMPANY_NAME" length="70" sql-type="nvarchar" not-null="true" />
</property>
</class>
</hibernate-mapping>
5. 添加一个Console Application的NHTest工程。为工程添加\lib\Iesi.Collections.dll、\lib\NHibernate.dll文件的引用,添加Domain项目引用。<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NH12.MyExample.Domain" assembly="Domain">
<class name="Company" table="TBLCOMPANY">
<id name="CompanyID" >
<column name="COMPANY_ID" sql-type="nvarchar" length="4" not-null="true"/>
<generator class="assigned" />
</id>
<property name="CompanyName">
<column name="COMPANY_NAME" length="70" sql-type="nvarchar" not-null="true" />
</property>
</class>
</hibernate-mapping>
6. 将NHibernate源代码中的nhibernate-configuration.xsd文件拷贝到NHTest工程的目录下,也可以拷贝到VS 2005的系统目录,这是因为配置文件使用这个xsd进行校验、输入提示等。
在NHTest的App.config文件中添加如下配置:
<configSections>
<section
name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"
/>
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.connection_string">Server=localhost;initial catalog=NH;user id=sa;password=123</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="use_proxy_validator">False</property>
<mapping assembly="Domain" />
</session-factory>
</hibernate-configuration>
数据库连接字符串信息改成自己的。注意1.2版本中配置信息不再支持<add name="" value="" />方式,而改用property元素,相应的name只需要把原来name值中的nhibernate.这个前缀去掉就可以,value的方式没有发生变化。<section
name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"
/>
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.connection_string">Server=localhost;initial catalog=NH;user id=sa;password=123</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="use_proxy_validator">False</property>
<mapping assembly="Domain" />
</session-factory>
</hibernate-configuration>
7. 写代码测试。测试代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using NH12.MyExample.Domain;
using NHibernate;
using NHibernate.Cfg;
using Iesi.Collections.Generic;
namespace NH12.MyExample.Test
{
class Program
{
static void Main(string[] args)
{
ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
ISession session = null;
ITransaction trans = null;
try
{
session = sessionFactory.OpenSession();
trans = session.BeginTransaction();
Company company = new Company("1000", "BenQ Guru Co., Ltd.");
session.Save(company);
Console.WriteLine("company 1000 has been created");
Company company2 = new Company("2000", "test company 2");
session.Save(company2);
Console.WriteLine("company 2000 has been created");
Company company3 = session.Get<Company>("1000");
Console.WriteLine("Company : id: {0} name: {1}", company3.CompanyID, company3.CompanyName);
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
}
finally
{
session.Close();
}
sessionFactory.Close();
Console.ReadLine();
}
}
}
现在可以运行这个最简单的映射示例,运行完之后可以在TBLCOMPANY表中查询到记录。using System.Collections.Generic;
using System.Text;
using NH12.MyExample.Domain;
using NHibernate;
using NHibernate.Cfg;
using Iesi.Collections.Generic;
namespace NH12.MyExample.Test
{
class Program
{
static void Main(string[] args)
{
ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
ISession session = null;
ITransaction trans = null;
try
{
session = sessionFactory.OpenSession();
trans = session.BeginTransaction();
Company company = new Company("1000", "BenQ Guru Co., Ltd.");
session.Save(company);
Console.WriteLine("company 1000 has been created");
Company company2 = new Company("2000", "test company 2");
session.Save(company2);
Console.WriteLine("company 2000 has been created");
Company company3 = session.Get<Company>("1000");
Console.WriteLine("Company : id: {0} name: {1}", company3.CompanyID, company3.CompanyName);
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
}
finally
{
session.Close();
}
sessionFactory.Close();
Console.ReadLine();
}
}
}
即使是最简单的例子,我们也可以发现一些东西。
如果你一边单步执行,一边打开SQL Server的SQL Profiler进行监控(监控的Event为Stored Procedures中的RPC:Completed),可以发现,在两个session.Save()方法以及session.Get<>()方法处,并没有产生任何跟数据库进行交互的SQL语句,而只有在trans.Commit()的时候,才会产生下面这样两条SQL:
exec sp_executesql N'INSERT INTO TBLCOMPANY (COMPANY_NAME, COMPANY_ID) VALUES (@p0, @p1)',
N'@p0 nvarchar(19),@p1 nvarchar(4)',
@p0 = N'BenQ Guru Co., Ltd.', @p1 = N'1000'
exec sp_executesql N'INSERT INTO TBLCOMPANY (COMPANY_NAME, COMPANY_ID) VALUES (@p0, @p1)',
N'@p0 nvarchar(14),@p1 nvarchar(4)',
@p0 = N'test company 2', @p1 = N'2000'
这是因为NHibernate在同一个session内会做对象的状态管理和缓存,对对象属性的修改被缓存起来,直到事务提交的时刻才将所有数据库更新操作应用到数据库上;在session.Get()获取对象时,先从当前session的缓存中查找是否已经存在该对象,如果有,则直接取出这个对象,而不会产生一条SQL查询。所以上面的测试只有两个INSERT语句。N'@p0 nvarchar(19),@p1 nvarchar(4)',
@p0 = N'BenQ Guru Co., Ltd.', @p1 = N'1000'
exec sp_executesql N'INSERT INTO TBLCOMPANY (COMPANY_NAME, COMPANY_ID) VALUES (@p0, @p1)',
N'@p0 nvarchar(14),@p1 nvarchar(4)',
@p0 = N'test company 2', @p1 = N'2000'
在上面测试的基础上,运行如下的测试代码
ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
ISession session = null, session2=null;
ITransaction trans = null;
try
{
session = sessionFactory.OpenSession();
session2 = sessionFactory.OpenSession();
trans = session.BeginTransaction();
Company company = session.Get<Company>("2000");
company.CompanyName = "My Test Company ...";
Company company2 = session2.Get<Company>("2000");
session.Update(company);
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
}
finally
{
session.Close();
session2.Close();
}
sessionFactory.Close();
Console.ReadLine();
执行的SQL语句如下:ISession session = null, session2=null;
ITransaction trans = null;
try
{
session = sessionFactory.OpenSession();
session2 = sessionFactory.OpenSession();
trans = session.BeginTransaction();
Company company = session.Get<Company>("2000");
company.CompanyName = "My Test Company ...";
Company company2 = session2.Get<Company>("2000");
session.Update(company);
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
}
finally
{
session.Close();
session2.Close();
}
sessionFactory.Close();
Console.ReadLine();
exec sp_executesql
N'SELECT company0_.COMPANY_ID as COMPANY1_0_0_, company0_.COMPANY_NAME as COMPANY2_0_0_
FROM TBLCOMPANY company0_ WHERE company0_.COMPANY_ID=@p0',
N'@p0 nvarchar(4)', @p0 = N'2000'
exec sp_executesql
N'SELECT company0_.COMPANY_ID as COMPANY1_0_0_, company0_.COMPANY_NAME as COMPANY2_0_0_
FROM TBLCOMPANY company0_ WHERE company0_.COMPANY_ID=@p0',
N'@p0 nvarchar(4)', @p0 = N'2000'
exec sp_executesql N'UPDATE TBLCOMPANY SET COMPANY_NAME = @p0 WHERE COMPANY_ID = @p1',
N'@p0 nvarchar(19),@p1 nvarchar(4)', @p0 = N'My Test Company ...', @p1 = N'2000'
其中第1、2句分别在session.Get()和session2.Get()调用时产生,因为这是两个不同的session,并且缓存中都还没有要取的company对象。第3句是在trans.Commit()时产生的。N'SELECT company0_.COMPANY_ID as COMPANY1_0_0_, company0_.COMPANY_NAME as COMPANY2_0_0_
FROM TBLCOMPANY company0_ WHERE company0_.COMPANY_ID=@p0',
N'@p0 nvarchar(4)', @p0 = N'2000'
exec sp_executesql
N'SELECT company0_.COMPANY_ID as COMPANY1_0_0_, company0_.COMPANY_NAME as COMPANY2_0_0_
FROM TBLCOMPANY company0_ WHERE company0_.COMPANY_ID=@p0',
N'@p0 nvarchar(4)', @p0 = N'2000'
exec sp_executesql N'UPDATE TBLCOMPANY SET COMPANY_NAME = @p0 WHERE COMPANY_ID = @p1',
N'@p0 nvarchar(19),@p1 nvarchar(4)', @p0 = N'My Test Company ...', @p1 = N'2000'
另外一点需要注意,NHibernate不允许修改主键值,如果有修改,在事务提交时会丢出一个异常。这个特性在使用业务上有意义的字段作为主键时可能会面临一些问题,不过从另一个方面看,就算这种情况下NH允许修改主键,因为其它对象可能已经根据这个主键值引用该对象,所以修改同样会造成问题。至少目前我接触的项目中,对象的业务主键是不允许修改的。