NHibernate从入门到精通系列(4)——持久对象的生命周期(上)
内容摘要
持久对象的状态的概念
持久对象的状态Demo
一、持久对象的状态的概念
在NHibernate中有三种状态,对它的深入理解,才能更好的理解NHibernate的运行机理,刚开始不太注意这些概念,后来发现它是重要的。对于NHibernate和SQL的关系有更好的理解;对于理解需要持久化的.NET对象,在它的生命周期中三种状态之间的互相转化有很大帮助。如图1.1所示
图1.1
- 临时态(Transient):用new创建的对象,它没有持久化,没有纳入Session中,随时可以被垃圾回收,处于此状态的对象叫临时对象。特点:数据库中没有与之对应的记录;
- 持久态(Persistent):已经持久化,加入到了Session缓存中。通过NHibernate保存的对象或通过Get/Load等方法获取出的对象,其对象没有脱离Session的管理,处于此状态的对象叫持久对象;
- 游离态(Detached):持久化对象脱离了Session的对象。如Session缓存被清空的对象。特点:已经持久化,但不在Session缓存中。处于此状态的对象叫游离对象;
二、持久对象的状态Demo
2.1 准备工作
(1)建立名为“NHibernateTest”的项目
(2)引用相应的程序集并引入上节课的“Domain”项目。
(3)复制上节课的“hibernate.cfg.xml”配置模板
<!--
This template was written to work with NHibernate.Test.
Copy the template to your NHibernate.Test project folder and rename it in hibernate.cfg.xml and change it
for your own use before compile tests in VisualStudio.
-->
<!-- This is the System.Data.dll provider for SQL Server -->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="NHibernateTest">
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">
server=.\SQLEXPRESS;database=NHibernateDemo;uid=sa;pwd=;
</property>
<property name="adonet.batch_size">10</property>
<property name="show_sql">true</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="use_outer_join">true</property>
<property name="command_timeout">60</property>
<property name="hbm2ddl.auto">update</property>
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
<mapping assembly="Domain"/>
</session-factory>
</hibernate-configuration>
(4)引用“log4net.dll”并配置App.config,用于输出日志
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<!--log4net配置-->
<log4net debug="true">
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<param name="File" value="Logs\Log.log" />
<param name="datePattern" value="MM-dd HH:mm" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<appender name="HttpTraceAppender" type="log4net.Appender.ASPNetTraceAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="Logs/Log.log" />
<param name="AppendToFile" value="true" />
<param name="MaxSizeRollBackups" value="10" />
<param name="MaximumFileSize" value="100K" />
<param name="RollingStyle" value="Size" />
<param name="StaticLogFileName" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
(5)复制“LinFu.DynamicProxy.dll”和“NHibernate.ByteCode.LinFu.dll”文件,粘贴到项目中。
(6)增加用于单元测试的类文件“LifecycleTest.cs”
public class LifecycleTest
{
private ISessionFactory sessionFactory;
public LifecycleTest()
{
log4net.Config.XmlConfigurator.Configure();
}
[SetUp]
public void Init()
{
var cfg = new NHibernate.Cfg.Configuration().Configure("Config/hibernate.cfg.xml");
sessionFactory = cfg.BuildSessionFactory();
}
}
如图2.1.1所示,准备完成后,便可以开始我们的演示。
图2.1.1
2.2 临时态(Transient)到持久态(Persistent)
先new一个对象,该对象的状态为Transient,然后调用Save()方法将该对象持久化到数据库中,该对象的状态变为Persistent。在未关闭Session前,修改该对象的属性,最后提交事务。
/// 临时态-->持久态
/// </summary>
[Test]
public void TransientToPersistentTest()
{
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
//Transient
var product = new Product
{
ID = Guid.NewGuid(),
BuyPrice = 10M,
Code = "ABC123",
Name = "电脑",
QuantityPerUnit = "20x1",
SellPrice = 11M,
Unit = "台"
};
try
{
//Persistent
session.Save(product);
//保存记录后修改数据,观察数据库中数据的变化
product.SellPrice = 12M;
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
}
运行效果如图2.2.1所示,首先生成了insert into语句,然后生成了update语句。
图2.2.1
一开始,Product的SellPrice属性,我设置为“11M”,然后调用“Save”方法持久化“Product”对象,接下来修改SellPrice属性到“12M”。最后让我们打开数据库,看一下里面的数据到底是“11M”,还是“12M”。如图2.2.2所示,数据是“12M”。
图2.2.2
这时,我们心里便产生了一个疑问:把Product的SellPrice属性从“11M”修改为“12M”后,并没有调用Save()或者Update()的方法,为什么数据库中的数据会变呢?
这是因为,当对象处于Persistent状态,并没有脱离Session管理时,其属性发生改变后,数据库相对应的记录会与之同步。
2.3 持久态(Persistent)到游离态(Detached),再到持久态(Persistent)
/// 持久态-->游离态-->持久态
/// </summary>
[Test]
public void PersistentToTestDetached()
{
//Transient
var product = new Product
{
ID = Guid.NewGuid(),
BuyPrice = 10M,
Code = "ABC123",
Name = "电脑",
QuantityPerUnit = "20x1",
SellPrice = 11M,
Unit = "台"
};
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
try
{
//Persistent
session.Save(product);
product.SellPrice = 12M;
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
//Detached
product.SellPrice = 13M;
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
try
{
//Persistent
session.Update(product);
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
}
运行效果如图2.3.1所示。当对象处于游离态(Detached)时,修改其属性,是不会与数据库发生同步的。调用Update()方法后,对象则变回持久态(Persistent)。
图2.3.1
2.4 Get方法得到持久态(Persistent)
通过Get()方法获取持久态(Persistent)对象,然后修改其属性,观察是否与数据库发生同步。运行效果如图2.4.1所示,先生成insert into语句,然后生成select语句,最后生成update语句。
图2.4.1
我们能够得出结论,通过Get()方法,是可以得到持久态(Persistent)对象的。
2.5 Get和Load()方法的区别
我们模拟一个数据库中不存在的对象,分别调用Get和Load()方法来测试产生的效果。
Get方法的代码如下:
/// 查询空记录
/// </summary>
[Test]
public void GetNullTest()
{
Guid id = Guid.NewGuid();
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
try
{
//Persistent
var product = session.Get<Product>(id);
Console.WriteLine("调用 Get()方法");
//断言为空
Assert.Null(product);
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
}
Get()方法的运行效果,如图2.5.1所示。调用Get()方法后,数据库中不存在的对象返回值为null,并且一但调用Get()方法,就会生成SQL语句。
图2.5.1
Load()方法的代码如下:
/// 查询空记录
/// </summary>
[Test]
public void LoadTest()
{
Guid id = Guid.NewGuid();
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
try
{
//Persistent
var product = session.Load<Product>(id);
Console.WriteLine("调用 Load()方法");
//断言为空
Assert.NotNull(product);
//当查看其属性时,则会生成SQL语句
string name = product.Name;
Assert.NotNull(name); //断言name不为空
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
}
Load()方法的运行效果,如图2.5.2所示。调用Load()方法查询数据库中不存在的对象,返回值不为空;当调用Load()方法时,不立刻产生SQL语句,查看其属性后才产生SQL语句,并且查看数据库中不存在对象的属性时会抛出异常。原因是调用Load()方法会返回一个“代理类”,这是NHibernate的一个重要的特性——延迟加载。
延迟加载(lazy load)也叫“懒加载”,是NHibernate关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用的时候,才会发出SQL语句进行查询。 延迟加载的有效期是在Session打开的情况下,当Session关闭后,会报异常。NHibernate的代理对象是由第三方组件“Antlr3.Runtime”提供的。
图2.5.2
2.6 Delete()方法
先得到一个持久态(Persistent)对象,然后调用Delete()方法删除该对象,这时该对象变回临时态(Transient)
代码如下:
public void DeleteTest()
{
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
//Transient
var product = new Product
{
ID = Guid.NewGuid(),
BuyPrice = 10M,
Code = "ABC123",
Name = "电脑",
QuantityPerUnit = "20x1",
SellPrice = 11M,
Unit = "台"
};
try
{
//Persistent
session.Save(product);
//Transient
session.Delete(product);
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
}
运行效果如图2.6.1所示,先生成insert into语句,再生成delete语句。
图2.6.1
2.7 Update()方法
先手动打造new一个数据库中存在的游离态(Detached)对象,然后直接调用Update()方法将对象的状态设置为持久态(Persistent)。
代码如下:
public void UpdateTest()
{
Guid id = Guid.NewGuid();
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
//Transient
var product = new Product
{
ID = id,
BuyPrice = 10M,
Code = "ABC123",
Name = "电脑",
QuantityPerUnit = "20x1",
SellPrice = 11M,
Unit = "台"
};
try
{
//Persistent
session.Save(product);
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
using (ISession session = this.sessionFactory.OpenSession())
{
using (ITransaction tran = session.BeginTransaction())
{
//Detached
var product = new Product
{
ID = id,
Code = "ABC456",
};
try
{
//Persistent
session.Update(product);
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
throw ex;
}
}
}
}
运行效果如图2.7.1所示,生成了update语句,并已经修改了对应的记录。有的朋友就会问,为什么new的时候也能得到游离态(Detached)对象?因为判断是否为游离态(Detached)对象,是根据数据库中是否存在与之对应的记录定夺的。
图2.7.1
出处:http://www.cnblogs.com/GoodHelper/archive/2011/02/17/nhibernate_04.html
欢迎转载,但需保留版权。