NHibernate 和 MySQL - 一个简单的例子






4.19/5 (13投票s)
一篇简短的文章,介绍了如何将 NHibernate 和 MyQL 结合使用。
引言
我通常喜欢立刻尝试新事物,所以在开始学习 NHibernate 时,我也这样做。我启动了我的 Visual Studio 2008,创建了一个新的 Web 应用程序,以便探索这种新的、出色的技术。
我曾阅读过(或许一如既往地不够仔细)文档,也搜索了一些简单的示例来入门。由于我一直很喜欢 MySQL,所以我想在我的第一个测试应用程序中使用它。不幸的是,我立刻遇到了一些问题,因为所有我看到的示例以及所有文档样本都没有真正说明使用的是哪个版本的 NHibernate,也没有说明尝试过哪个版本的 MySQL。因此,我花了很长时间才将一切设置妥当并运行起来。
因此,我决定写一篇简短的文章,介绍如何使用 NHibernate 和 MySQL,特别是针对 NHibernate 2.0.0.1001、MySQL 5.0.45,并使用 MySql ADO.Net 驱动程序 5.1.5.0。我并不打算写得非常详尽,而是希望给出一个最简单的实现示例。
我假设您熟悉本文中使用的技术和名称。如果您想获取更新,可以点击此处阅读并下载最新版本。
- NHibernate,它负责将普通的 .NET 对象持久化到关系数据库中并从中读取。
- MySQL,一个流行的开源数据库引擎(现归 Sun 所有)。
Using the Code
为了开始,我想要一个 VS2008 中的全新干净项目。所以我通过选择“文件”菜单下的“新建/项目”菜单项创建了一个新的 ASP.NET Web 应用程序,并在 Visual C#/Web 部分创建了一个新应用程序;我们将其命名为 WebApp1。
WebApp1 只包含一个默认页面,Default.aspx。我在其中添加了一个 Label
控件 Label1
,一个名为 TextBox1
的 TextBox
,以及两个 Button
。我的意图是在页面加载时通过 NHibernate 从数据库中检索单个对象,并将该对象的 ID 显示在 Label
控件的 Text
属性中,并将该对象的一个文本值显示在 TextBox
的 Text
属性中。这两个按钮用于更新和删除。
NHibernate
为了让 NHibernate 工作,我必须为我的项目添加一些引用。正如我所发现的,不仅需要 NHibernate.dll,还需要以下文件:
- Castle.Core
- Castle.DynamicProxy2
- Iesi.Collections
- log4net
- Lucene.Net
- Rhino.Mocks
根据我查阅的文档和示例,NHibernate 的配置主要有三种方式:通过 web.config、通过代码,或者通过一个单独的 NHibernate 配置文件,名为 hibernate.cfg.xml。我选择了最后一种方法,因为它能将配置与其他东西分开,看起来很方便。所以,让我们开始看看这个配置文件。
hibernate.cfg.xml 文件包含几行,都很容易理解。下面对其中一些进行解释。我开始使用的文件如下:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<!-- an ISessionFactory instance -->
<session-factory>
<!-- properties -->
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="connection.driver_class">
NHibernate.Driver.MySqlDataDriver
</property>
<property name="connection.connection_string">
Server=localhost;Database=test;User ID=test;Password=test;
</property>
<property name="dialect">
NHibernate.Dialect.MySQL5Dialect
</property>
<!-- mapping files -->
<mapping resource="WebApp1.Site.hbm.xml" assembly="WebApp1" />
</session-factory>
</hibernate-configuration>
connection.provider
设置 NHibernate 连接数据库时应使用的连接提供程序。connection.driver_class
设置应使用的驱动程序。在本例中,使用 MySQL 时,MySqlDataDriver 是一个合乎逻辑的选择。connection.connection_string
是数据库的连接字符串。dialect
指示 NHibernate 类名称,该名称启用某些依赖于平台的特性。在本例中,由于我使用的是 MySQL 5,显而易见的选择是 MySQL5Dialect。
配置文件中的映射部分告诉 NHibernate 使用哪些映射文件进行对象/关系映射。我将在本文稍后介绍。
持久化类
NHibernate 可以使用普通类在关系数据库中持久化对象,并使用映射技术将持久化类的属性连接到关系数据库表中的列。对于我的简单示例,我创建了一个名为 Site
的简单类,其代码如下:
namespace WebApp1
{
public class Site
{
private int id;
public virtual int Id
{
get { return id; }
set { id = value; }
}
private string name;
public virtual string Name
{
get { return name; }
set { name = value; }
}
public Site()
{
}
}
}
Site.cs 包含两个我稍后将展示的属性,它们对应于我正在使用的数据库表中的列。NHibernate 实际上并不局限于只使用属性类型,所有 .NET 类型和基本类型都可以映射,包括来自 System.Collections
命名空间中的类。但在本简单示例中,我只使用了 int
和 string
。
ID
属性非常重要,因为它将对应于数据库表中的主键。即使 NHibernate 不是强制要求使用它(NHibernate 可以内部处理标识符),但对我来说,这是一种自然的架构方法。
如您所见,所有公共属性都声明为 virtual
。这是因为 NHibernate 利用此特性进行一些运行时增强,否则根据文档,这些特性将无法工作。同时,建议为类提供一个默认构造函数。
映射
为了能够使用 Site
类,必须创建一个映射文件,其中包含 NHibernate 用于对象/关系映射的元数据,即连接类声明、属性到列以及数据库表中的键。在本例中,名为 Site.hbm.xml 的映射文件声明如下:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="WebApp1.Site, WebApp1" table="site">
<id name="Id" column="ID" type="int">
<generator class="native"></generator>
</id>
<property name="Name" column="name"
type="String"></property>
</class>
</hibernate-mapping>
此映射文件对应的数据库表声明如下:
CREATE TABLE 'site' (
'ID' int(5) unsigned NOT NULL auto_increment,
'name' varchar(100) NOT NULL,
PRIMARY KEY ('ID')
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
映射文件包含一系列节点,其简要解释如下:
hibernate-mapping
表明这是一个 hibernate 映射文件,xmlns
属性声明了应使用的 XML 命名空间。class
指示此映射所连接的持久化类。name
属性必须指定完全限定的 .Net 类名,并且还必须包含程序集名称。table
属性指定数据库中的表名。id
是描述数据库表中主键列的节点。name
属性告诉 NHibernate 使用持久化类中的哪个属性。column
属性告诉数据库表中的哪个列是主键。type
属性告诉 NHibernate 数据库类型。在大多数情况下,这应该可以自动检索,但我发现这是一个问题,因此指定了类型。generator
是id
节点必需的子元素。class
属性指定了应使用哪个 .NET 类为持久化类的实例生成唯一标识符。这个类可以是应用程序的特定实现,也可以是 NHibernate 提供的内置实现之一。在本例中,是与底层数据库功能相关的本地类,对于 MySQL,它使用具有自动递增功能的 identity 列。property
是一个或多个元素,用于描述对应于数据库表中列的持久化类的属性。在本例中,只有一个列,因此是这样映射的:name
属性告诉 NHibernate 使用类中的哪个属性。column
属性对应于数据库中的列名。type
属性告诉 NHibernate 数据库列的类型。在大多数情况下,应该可以自动检测到,但正如前面所描述的,这可能会带来一些麻烦。
此映射文件被添加到我的项目根目录。正如我所发现的,XML 文件的“生成操作”应设置为“嵌入的资源”,这样 NHibernate 就可以在运行时解析它,从而简化了使用映射功能所需的编码。
编码
好了,既然我们已经有了应用程序的配置和映射部分,现在让我们回到编码。如前所述,我只有一个页面,其中包含一个 Label
控件。我的意图是能够使用 NHibernate 的对象/关系映射从数据库中检索单个值。所以,让我们直接看 Default.aspx.cs 的代码部分。
在 Page_Load
中,我使用了以下代码从数据库检索站点名称列表:
System.Collections.IList siteList;
ISessionFactory factory =
new NHibernate.Cfg.Configuration().Configure().BuildSessionFactory();
using (ISession session = factory.OpenSession())
{
ICriteria sc = session.CreateCriteria(typeof(Site));
siteList = sc.List();
session.Close();
}
factory.Close();
Label1.Text = ((Site)siteList[0]).Id.ToString();
TextBox1.Text = ((Site)siteList[0]).Name
第一行是我声明了一个普通的 IList
,它将包含我期望从 NHibernate 返回的 Site
对象列表。
根据 NHibernate 文档,ISessionFactory
是一个线程安全的、用于编译单个数据库映射的缓存。当我们将映射文件设置为嵌入式资源时,这是获取此类工厂所需的唯一代码。
下一部分使用 ISession
实例,该实例被描述为一个短生命周期的对象,代表应用程序和持久化存储(即数据库)之间的对话,实际上封装了一个 ADO.NET 连接。要获取 ISession
实例,需要通过 OpenSession()
函数向 ISessionFactory
实例请求打开并返回这样的对象。
下一部分使用 ICriteria
实例。它实际上是 NHibernate 提供的一个查询 API,允许我们通过更面向对象的方法动态构建查询,并代表对特定持久化类的查询。在我的示例中,我使用 ICriteria
从 NHibernate 检索 Site
实例列表。这是通过从会话对象获取条件实例来完成的,同时也告诉会话我们希望返回的对象类型。
获取对象数组的最简单方法是使用 Criteria
实例的 List()
函数,它实际上返回一个 IList
实例,其中包含我们为条件定义的特定持久化类的实例数组。
为了释放我们使用的资源,我在会话对象和工厂对象上都调用了 Close()
。
最后,我假设我已经从数据库获取了一些结果,并从列表中检索了第一个 Site
对象,然后获取了 ID
和 Name
属性,并将它们显示在我的 Label1.Text
和 TextBox1.Text
属性中。
由于我也想更新 Name
属性,所以我为创建的第一个按钮添加了以下事件处理程序:
protected void Button1_Click(object sender, EventArgs e)
{
ISessionFactory factory =
new NHibernate.Cfg.Configuration().Configure().BuildSessionFactory();
Site s;
using (ISession session = factory.OpenSession())
{
s = (Site)session.Load(typeof(Site), int.Parse(Label1.Text));
s.Name = TextBox1.Text;
session.SaveOrUpdate(s);
session.Flush();
session.Close();
}
factory.Close();
}
这段代码实现的功能是:当 Button1
被点击时,我像之前一样创建一个 ISessionFactory
,然后使用它创建一个 ISession
。之后,我使用该 ISession
通过会话实例的 Load
方法检索具有从 Label1.Text
属性获取的 Id
的相应 Site
对象,指示会话检索一个 Site
对象。然后,我用新的 Name
更新检索到的对象,然后发出 SaveOrUpdate
命令,该命令指示 NHibernate 信息已更新。为了将更改持久化到数据库,使用 Flush
命令至关重要,否则更改不会传播回数据库(如果使用事务,则不完全正确,但这又是另一篇文章的内容了)。
第二个按钮 Button2
用于删除指定对象,与之关联的事件与更新部分非常相似:
protected void Button2_Click(object sender, EventArgs e)
{
ISessionFactory factory =
new NHibernate.Cfg.Configuration().Configure().BuildSessionFactory();
using (ISession session = factory.OpenSession())
{
s = (Site)session.Load(typeof(Site), int.Parse(Label1.Text));
session.Delete(s);
session.Flush();
session.Close();
}
factory.Close();
}
与更新代码一样,第一步是检索我们感兴趣的对象,然后使用会话实例在该对象上调用 Delete
命令,最后,刷新更改回数据存储。
摘要
这篇简短的文章展示了一个例子,说明如何使用 NHibernate 和 MySQL 以更面向对象的方式呈现来自关系数据库的信息。它在很多方面都不完整,例如我遗漏了异常处理等,但它展示了这些技术如何使用,或许也能激发某人更深入地研究它,因为它是一种非常有趣的方法,并且简化了我们在应用程序中几乎总是使用的数据库连接。
关注点
在处理这段代码和配置时,我几乎总是遇到与产品版本和兼容性问题相关的某些问题,这些问题在大多数情况下也与开源开发项目的文档更新问题有关。
历史
- 2008-05-15:初版。
- 2008-10-16:更新了文本,增加了更新和删除功能。