So Tired !_! 逆水行舟, 不进则退!

25May/10

NHibernate链接access数据库的配置文件

Posted by Nick Xu

NHibernate链接access数 据库的配置文件:

官方没有提供链接access数据的专门的类,建议使用NHibernate.Dialect.SybaseDialect。它可以兼容对access数据库的操作。
方法:在app.config文件中配置如下信息:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="masparameter">
<property name="dialect">NHibernate.Dialect.SybaseDialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.OleDbDriver</property>
<property name="connection.connection_string">Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\WorkPlatform.mdb</property>
<property name="connection.isolation">ReadCommitted</property>
<!-- HBM Mapping Files -->
<mapping assembly="wmadata" />
</session-factory>
</hibernate-configuration>

这样就完成了配置信息。

25May/10

解决 Unknown entity class 或 Association references unmapped class

Posted by Nick Xu

在使用NHibernate时可能出现这样的错误:

未处理的NHibernate.MappingException Message="Unknown entity class :某个类名"

或则NHibernate.MappingException :Association references unmapped class:某个类名

我在网上搜了很多资料,都没找到解决方案,但是最后我终于找到了问题的所在。一下是解决方案:

1,查看类文件和.hbm.xml文件,确保信息和数据库信息一致。数据类型一致,关系一致(多对一和一对多的关系)。另外在.hbm.xml要确 保路径要一致。

2,如果确保以上正确后还存在问题,打开项目根目录下的的csproj文件,查看是否存在

<EmbeddedResource Include="类路径">或<EmbeddedResource Include=".hbm.xml的路径">

路径是否正确?

如果是<Content Include="类路径">或<Content Include=".hbm.xml的路径">话,用EmbeddedResource 代替Content ,然后保存。

我的问题到此已经解决,如果你还存在问题,请加我QQ:40432039,我们可以一起交流。

25May/10

NHibernate的调试技巧和Log4Net配置

Posted by Nick Xu

对新手而言NHibernate的调试事件很痛苦的事情,不知道NHibernate做了些什么,错误出现在什么地方。

我给出一些常用的调试办法希望对你有所帮助。

1.查看NHibernate写在控制台里的Sql语句

在配置文件中有这么个选项,如果把它设置为true,NHibernate会把执行的Sql显示在控制台上。

<property name="show_sql">true</property>

对于控制台应用程序我们可以设置断点后很轻松的看到NHibernate执行了什么Sql。

下图是从数据库中读取一条数据。


如果你写了单元测试,从NUnit同样可以很容易地看到。



2.配置Log4net来查看NHibernate留下的日志

如果你的程序是Asp.Net程序。那就看不到控制台信息了。那么就使用第二招配置Log4net。

按习惯,我还是使用单独的配置文件。当然你也可以配置在应用程序配置文件中(App.config或Web.Config)。

<?xml version="1.0" encoding="utf-8" ?>
<log4net>

<appender name="rollingFile" type="log4net.Appender.RollingFileAppender,log4net" >

<param name="File" value="log.txt" />
<param name="AppendToFile" value="false" />
<param name="RollingStyle" value="Date" />
<param name="DatePattern" value="yyyy.MM.dd" />
<param name="StaticLogFileName" value="true" />

<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] &lt;%X{auth}&gt; - %m%n" />
</layout>
</appender>

<root>
<!--如果只需要看看Sql设置INFO就够了,如果你要调试可以设置为DEBUG或ALL-->
<priority value="INFO" />
<appender-ref ref="rollingFile" />
</root>

</log4net>



读取log4net配置的代码

XmlConfigurator.Configure(new FileInfo("log4net.cfg.xml"));

运行了程序后你可以在应用程序目录找到log.txt的配置文件。里面记录了包括Sql的NHibernate的运行信息。

这是一个日志的片断

2006-08-08 22:22:16,921 [2036] INFO NHibernate.Cfg.Environment [(null)] <(null)> - NHibernate 1.0.2
2006-08-08 22:22:16,968 [2036] INFO NHibernate.Cfg.Environment [(null)] <(null)> - nhibernate section not found in application configuration file
2006-08-08 22:22:16,968 [2036] INFO NHibernate.Cfg.Environment [(null)] <(null)> - Using reflection optimizer
2006-08-08 22:22:17,000 [2036] INFO NHibernate.Cfg.Configuration [(null)] <(null)> - Searching for mapped documents in assembly: DDLLY.MyDoc.NHibernateTest.Log4NetTest
2006-08-08 22:22:17,000 [2036] INFO NHibernate.Cfg.Configuration [(null)] <(null)> - Found mapping documents in assembly: DDLLY.MyDoc.NHibernateTest.Log4NetTest.User.hbm.xml
2006-08-08 22:22:17,062 [2036] INFO NHibernate.Dialect.Dialect [(null)] <(null)> - Using dialect: NHibernate.Dialect.MsSql2000Dialect
2006-08-08 22:22:17,109 [2036] INFO NHibernate.Cfg.Binder [(null)] <(null)> - Mapping class: DDLLY.MyDoc.NHibernateTest.Log4NetTest.User -> users
2006-08-08 22:22:17,156 [2036] INFO NHibernate.Cfg.Configuration [(null)] <(null)> - Configured SessionFactory: DDLLY.MyDoc.NHibernateTest.Log4NetTest
2006-08-08 22:22:17,171 [2036] INFO NHibernate.Cfg.Configuration [(null)] <(null)> - processing one-to-many association mappings
2006-08-08 22:22:17,171 [2036] INFO NHibernate.Cfg.Configuration [(null)] <(null)> - processing one-to-one association property references
2006-08-08 22:22:17,171 [2036] INFO NHibernate.Cfg.Configuration [(null)] <(null)> - processing foreign key constraints
2006-08-08 22:22:17,187 [2036] INFO NHibernate.Dialect.Dialect [(null)] <(null)> - Using dialect: NHibernate.Dialect.MsSql2000Dialect
2006-08-08 22:22:17,187 [2036] INFO NHibernate.Cfg.SettingsFactory [(null)] <(null)> - use outer join fetching: True
2006-08-08 22:22:17,187 [2036] INFO NHibernate.Connection.ConnectionProviderFactory [(null)] <(null)> - Intitializing connection provider: NHibernate.Connection.DriverConnectionProvider
2006-08-08 22:22:17,187 [2036] INFO NHibernate.Connection.ConnectionProvider [(null)] <(null)> - Configuring ConnectionProvider
2006-08-08 22:22:17,187 [2036] INFO NHibernate.Cfg.SettingsFactory [(null)] <(null)> - Optimize cache for minimal puts: False
2006-08-08 22:22:17,203 [2036] INFO NHibernate.Cfg.SettingsFactory [(null)] <(null)> - echoing all SQL to stdout
2006-08-08 22:22:17,203 [2036] INFO NHibernate.Cfg.SettingsFactory [(null)] <(null)> - Query language substitutions: {false=0, no='N', yes='Y', true=1}
2006-08-08 22:22:17,203 [2036] INFO NHibernate.Cfg.SettingsFactory [(null)] <(null)> - cache provider: NHibernate.Cache.HashtableCacheProvider
2006-08-08 22:22:17,203 [2036] INFO NHibernate.Cfg.Configuration [(null)] <(null)> - instantiating and configuring caches
2006-08-08 22:22:17,218 [2036] INFO NHibernate.Impl.SessionFactoryImpl [(null)] <(null)> - building session factory
2006-08-08 22:22:17,812 [2036] INFO NHibernate.Impl.SessionFactoryObjectFactory [(null)] <(null)> - Factory name:DDLLY.MyDoc.NHibernateTest.Log4NetTest
2006-08-08 22:22:17,859 [2036] INFO NHibernate.Loader.Loader [(null)] <(null)> - SELECT user0_.Id as Id0_, user0_.Email as Email0_, user0_.UserName as UserName0_, user0_.Password as Password0_ FROM users user0_ WHERE user0_.Id=@p0

从这个文件我们可以看到NHibernate都做了些什么(包括执行了什么Sql,看上面的最后一行)。

当你想更详细的信息可以把priority设置为ALL,这样可以看到所有信息。

提示:NHibernate会把一般信息记录为INFO,调试信息记录为Debug,错误信息记录为Error。

Log4Net中支持多个appender你可以也把日志记录到数据库等其他地方,请参看Log4Net的文档,这里不做讲解。

3.让NHibernate的日志不影响你使用Log4Net写日志

NHibernate总是会调用配置<root>里面的“appender-ref”来写配置。

所以如果你系统本省也使用了Log4Net记录日志,而不想让NHibernate的日志影响,则可以定义logger。

<?xml version="1.0" encoding="utf-8" ?>
<log4net>

<root>
<!--如果只需要看看Sql设置INFO就够了,如果你要调试可以设置为DEBUG或ALL-->
<priority value="INFO" />
<appender-ref ref="rollingFile" />
</root>

<logger name="ApplicationInfoLog">
<level value="INFO" />
<appender-ref ref="rollingFile1" />
</logger>

<appender name="rollingFile" type="log4net.Appender.RollingFileAppender,log4net" >

<param name="File" value="log.txt" />
<param name="AppendToFile" value="false" />
<param name="RollingStyle" value="Date" />
<param name="DatePattern" value="yyyy.MM.dd" />
<param name="StaticLogFileName" value="true" />

<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] &lt;%X{auth}&gt; - %m%n" />
</layout>
</appender>

<appender name="rollingFile1" type="log4net.Appender.RollingFileAppender,log4net" >

<param name="File" value="log1.txt" />
<param name="AppendToFile" value="false" />
<param name="RollingStyle" value="Date" />
<param name="DatePattern" value="yyyy.MM.dd" />
<param name="StaticLogFileName" value="true" />

<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] &lt;%X{auth}&gt; - %m%n" />
</layout>
</appender>

</log4net>

日志记录代码

ILog applicationInfoLog=applicationInfoLog = LogManager.GetLogger("ApplicationInfoLog");
applicationInfoLog.Info("记录日志");

此例中NHibernater日志会存在log.txt,系统日志记录在log1.txt。

4.在使用SqlServer时使用事件查看器监视Sql

对于SqlServer数据库,如果你没有条件可以完成上面的功能,那么你可以使用事件查看器来监视执行的Sql,使用比较简单,不多说了。

25May/10

使用NHIBERNATE过程中遇到一些问题的解决方法收集

Posted by Nick Xu

在生成实体类的时候,有时候一些表是不需要自增列的,那么需要在对应的XML配置文件中设置如下:

无自增列:

<id name="UserID" column="UserID" type="Int32" unsaved-value="0">
<generator class ="assigned"></generator>
</id>

有自增列:

<id name="UserID" column="UserID" type="Int32" unsaved-value="0">
<!--<generator class="identity" />-->
</id>

ACCESS中插入数据后获取自增列的值需在NHIBERNATE.XML中加入下面的设置:

<property name="connection.release_mode">on_close</property>

25May/10

NHibernate查询、更新、删除

Posted by Nick Xu

[TestFixture]

public class UserFixture {
Configuration cfg;
ISessionFactory factory ;
readonly string userId=”test_user”;

//准备测试前的工作
[SetUp]
public void SetUp() {

//Configuration对象能够解析所有.Net对象和后台数据库中的映射 关系。
cfg = new Configuration();

//Configuration对象会搜索装配件里的任何以hbm.xml 结尾的文件,前提是
//*.hbm.xml要编译为“嵌套的资源”
cfg.AddAssembly(”NHibernate.Examples”);
factory = cfg.BuildSessionFactory();
}

[TearDown]

public void TearDown() {
//清除一些操作中生成的临时数据
}

///<summary>
///清除数据库中 Users表中的所有数据,提供一个干净的测试数据环境
///</summary>
[Test]
public void ClearData() {
//ISession对象提供一个到后台数据库的连接,参数由App.config指定
//ITransaction对象提供一个可以被NHibernate管理的事 务。
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();

//使用查询删除所有数据
session.Delete(”Select From User Where 1=1″);

transaction.Commit();

session.Close();

}

///<summary>
///测试往数据库中添加一个对象
///</summary>
[Test]
public void AddTest() {
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();

User newUser = new User();
newUser.Id = userId;
newUser.UserName = “SkyDev”;
newUser.Password = “abc123″;
newUser.EmailAddress = skydev@pyp.edu.cn;
newUser.LastLogon = DateTime.Now;
session.Save(newUser);

transaction.Commit();
session.Close();
}

///<summary>
///更新数据库中的对象
///</summary>
[Test]
public void UpdateTest() {
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();

User aUser;

try {

//当userId指定的数据在数据库中不存在时会抛出异常
aUser= (User)session.Load(typeof(User), userId);

// set Joe Cool’s Last Login property
aUser.LastLogon = DateTime.Now;

// flush the changes from the Session to the Database
session.Flush();

transaction.Commit();
}               catch {  transaction.Rollback(); }

finally {  session.Close(); }
}

///<summary>
///删除指定的对象
///</summary>
[Test]
public void DeleteTest() {
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();

User aUser;

try {
//当userId指定的数据在数据库中不存在时会抛出异常
aUser = session.Load(typeof(User), userId) as User;

if (aUser!=null) { session.Delete(aUser); }

transaction.Commit();
}

catch { transaction.Rollback();  }

finally { session.Close();  }

}

}

}

正如你所看到的,关于NHiberante重要的事情是如此简单。继续并且查询你的数据库,验证一下User表里的新记录。 现在重要的事情就是你去操心业务对象并在进行处理的时候告诉NHibernate就可以了。

让我们来告诉你,当你有一个UserID的时候如何获取对象(举 例说,登陆你的网站的时候)。仅仅一句话就可以打开Session,传入key就可以了

// open another session to retrieve the just inserted user
session = factory.OpenSession();
User joeCool = (User)session.Load(typeof(User), “joe_cool”);

你所获取的User对象还在生存周期内!改变它的属性,并通过Flush()持久化到数据库。

// set Joe Cool’s Last Login property
joeCool.LastLogon = DateTime.Now;

// flush the changes from the Session to the Database
session.Flush();

你所要做的就是通过NHibernate来进行你需要的改变,并调用Session的Flush()方法提 交。验证一下数据库,查查用户ID为”joe_cool”的记录中”LastLogon”的更改。

还有更好的,你可以以System.Collections.IList的方式来获取从表中的对象。如下
IList userList = session.CreateCriteria(typeof(User)).List();
foreach(User user in userList)
{
System.Diagnostics.Debug.WriteLine(user.Id + ” last logged in at ” + user.LastLogon);
}

这个查询将会返回所有表记录。往往你需要做更多的控制,比如说获取从March 14, 2004 10:00 PM 以后登陆的用户,如下:

IList recentUsers = session.CreateCriteria(typeof(User))
.Add(Expression.Expression.Gt(”LastLogon”, new DateTime(2004, 03, 14, 20, 0, 0)))
.List();

foreach(User user in recentUsers)
{
System.Diagnostics.Debug.WriteLine(user.Id + ” last logged in at ” + user.LastLogon);
}

文档里还有一堆健壮的查询方式让你调用,这里仅仅让你对NHibernate 所提供的强有力的工具有一定的了解。

最后调用Session对象的Close()方法,释放NHibernate所使用的ADO.Net连接资源
// tell NHibernate to close this Session
session.Close();

Tagged as: No Comments
25May/10

NHibernate搭配Access的问题(完整)

Posted by Nick Xu

近日想学习一下NHibernate,以用在最新的一套大型系统上。想想不如先做一个小网站来练练手,不过之前还是得先看看NHibernate的 使用方法。搜索了几篇入门文章,包括官方网站的First Step,然后就开始写了。本想使用Access数据库,结果是一直失败,一开始是连Configuration都无法生成,后来又是写入数据库错误。后 来改成使用Sql server2000,结果很顺序的就通过了。再改成Access,又没有了头绪。最后经历了千辛万苦啊,终于算是通过了。总结如下:

使用Access数据库的组件在官方下载的NHibernate-contrib文件里,有个NHibernate.JetDriver.dll, 这个文件也是必须的。其实,在app.config文件里加入NHibernate属性的语句是:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
? <configSections>
??? <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
? </configSections>
? <nhibernate>
??? <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
??? <add key="hibernate.dialect" value="NHibernate.JetDriver.JetDialect, NHibernate.JetDriver"/>
??? <add key="hibernate.connection.driver_class" value="NHibernate.JetDriver.JetDriver, NHibernate.JetDriver"/>
??? <add key="hibernate.connection.connection_string" value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=hibtest.mdb"/>
? </nhibernate>
</configuration>

那个dialect和driver_class的值里面前面是类名称,后面是命名空间名称,中间用逗号分开,不可缺少。以前查到的一些资料里都没有 这一步,所以一直失败。

最 后还有一个问题,用户表里面的LastLogon是个日期时间字段,类里面字段的属性也设成日期时间,插入的时候报错,把类里面的字段改成string就 可以了,可能是因为插入的时候没有加引号的问题。还有密码字段如果字段名为Password,同样插入出错,这个应该是字段名跟关键字冲突的问题,看来作 者没有考虑到把字段名括起来的问题。但是我一直没有办法显示出最终要执行的sql语句,所以也一直没有办法确定错误原因。不过有了以上内容,应该足够写完 整个程序了。

测试了一下性能,点击按钮到返回,要2秒多,明显有点慢,做了一个10000个新数据,然后一次写入,用了20秒才写完,有点不可接受了。回头试一 下直接用OleDb写入的速度比较一下吧。

对于昨天的问题,继续研究了一下。经过一阵搜索,受到了另一位仁兄的启发。现在的速度慢,跟多一次DLL调用应该也有不小的关系,为什么不把 NHibernate.JetDriver合并进主模块呢?打开NHibernate的源代码,试着编译了一下,报错,说是签名失败,缺少一个sn文件。 后来找到这个文件是需要自己生成的,使用VS自带的一个工具sn.exe -k NHibernate.snx就可以了,把这个文件放到src目录下,编译通过。

在NHibernate的项目里添加目录 JetDriver,然后把NHibernate-Contrib里面的NHibernate.JetDriver目录里面的源代码一个个添加进来,编 译,通过了。试着调用一下,用昨天那个程序,重新添加引用,成功了,减少了一个DLL文件。

顺便研究了一下JetDriver的源代码,在JetDialect.cs文件里找到了一段:
??public override char CloseQuote
??{
???get { return ''; }
??}

初 步以为这个应该是自动添加在字段名上的修饰,于是把中间改成中括号,结果失败,查看了一下NHibernate输出的SQL,没有起作用,放弃。在 JetDbCommand.cs文件里找到一段CheckParameters,如果字段属性是日期时间字段,那么会自动转换成字符串。既然如此,为什么 我插入日期时间字段的时候还是有问题呢?它的ToString是有格式的,ToString( "dd-MMM-yyyy HH:mm:ss"),把这个格式添加到我自己的程序的转换过程里,结果也失败了,原来如此,这个转换后的格式再往Access里面写的时候没有办法正确 的转换回去了,于是修改这个格式,ToString( "dd/MM/yyyy HH:mm:ss" ),重新编译NHibernate.dll,重新编译测试程序,把字段属性和映射属性都改成日期,成功了。

至于Password字段名字冲 突的问题,虽然让数据库里面的字段不要叫Password可以解决问题,但是毕竟比较丑陋,结果试了一下在映射文件里面的column属性里加上中括号修 饰,也通过了,OK,问题不完美解决。如果想要的话,可以在映射文件的所有字段名上加上中括号,当然,其实只有Username和Password是必须 的。还是没有找到程序中哪里可以修改,自动为字段名添加修饰,难道没有考虑到这个功能?

经过两天的苦战,得出一个结论,NHibernate不适合Access开发,还是放弃吧。

前面很容易的搞定了Driver的问题以后,自以为离成功已经很近了,但是没有想到,真正的困难还在后面。

通 常数据库程序中最常用的一个关系就是一对多的关系,这也是NHibernate推荐使用的关系类型,于是很简单的建了一个数据库,一个Groups表,包 括GroupID和GroupName,一个Items表,包括ItemID和ItemName还有GroupID。通常这个关系可以代表数据库系统中绝 大部分关系类型。比如用户和组,产品和类别,等等。

按照上一次写的Config文件和hbm.xml文件,很快写好了整个系统的框架,不过 对于一对多的关系如何处理还没有思路,看了看别人的文章,都是一个set做One-to-more,另一个做More-to-one,在Group类里定 义了一个Items的变量,变量类型一开始使用IDictionary,在Item类里定义了一个Group类型的变量,代表它所对应的Group,(一 开始定义成了Int型的GroupID,后来发现这是个严重的错误,总是提示无法转换成正确的类型)。结果单独创建了一个Group变量再保存的时候都一 直报错,说数据类型转换错误,后来查了一些英文资料,好像在C#里没有办法把IDictionary转换成NHibernate使用的Set类型,要用 ICollection,但是把Items改成这个类型以后,发现这个类型本身没有Add方法,那怎么把它的Item加进来呢?百思不得其解啊。似乎没有 一篇文章提到这个解决方案。

晚上再努力查找,终于看到一丝线索,NHibernate内部使用的是 Iesi.Collections.ISet接口类型,它派生出了好几种类型,对应不同功能的列表。ISet这个东西C#本身是不具备的,这是这个第三方 库模仿Java做出来的。于是在Group里面加上这个引用,然后把Items改成这个类型,哇,终于通过了,可以添加Group了。开心。

接下来Load一个Group出来,创建一个Item,把这个Group变量赋给Item的Group属性。保存Item,成功了!

但 是看来看去总觉得不是个味,好像那个一对多关系白定义了。因为看人家的说法,应该是定义一个Group,然后定义一个Item,把这个Item添加到 Group的Items列表里,然后只要保存Group,那么这个Item也会自动保存,结果我试来试去不成功,提示一个错误,插入或更新出错,期望1 行,结果0行,可能是别的用户更改或删除的数据等等。

因为一开始做的是一个Web应用程序,结果在配置文件里设成show_sql为 true以后,还是没有办法看到生成的Sql语句,于是新建一个exe项目,代码一样,运行一下,果然看到Sql语句了。奇怪的是,系统生成了两条语句, 一个是插入新的Group,然后调用了一个Select @@identity,然后使用一个Update语句来更新Items表,结果生成的Where语句是ItemID=0。就算这个0是个错误,那也应该生 成Insert语句啊,为什么是Update呢?想了一下,总算是知道这个0是什么意思了,前面的那个Select @@identity对于Access无效,没有返回那个刚刚插入的id,所以就成了0。

这个问题恐怕不是我能解决的了,没有了这个功能,使用NHibernate还有什么意义?如果无法提取关联对象,还要自己负责维护这个关系的话,恐 怕还要出现一些条件语句在系统里,那就真的不如不用了。

等会试一下用Sql Server是不是没有这个问题,还有再试一下提取对象是否能够自动关联起来。

遇到了传说中的人品问题。

今 天终于搞定了NHibernate的一对多问题,不知道是因为它的版本升级了还是真的我的人品问题,反正按照网上看来的代码没有一个可以正确执行的。最终 发现还需要多一步操作,就是对于每个Item都需要一步Session.Save,而不是像网上所说的那样只要Save(Group)就可以自动Save Item。

完整的工作流程:建立 Configuration,Load XML文件(或者AddAssembly),生成SessionFactory, 生成Session,生成Transaction,新建Group,设定属性,Session.Save(Group),(或者Load Group),新建Item,设定属性,特别要指定它的Group属性,跟组对应起来,Session.Save(Item),如果有多个Item,重复 这个过程,然后Transaction.Commit,数据就已经保存进数据库了,Session.Close。

读 取的过程:前面相同,生成Session以后(Factory.OpenSession),Group g=(Group)Session.Load(typeof(Group),GroupID),这样就得到了这个ID所对应的Group对象,它的 Items属性里包含了所有的下级Item,目前我用的是HashedSet来保存的,所以可以使用foreach(Item it in g.Items),然后就可以使用所有的it的属性了。

在Sql Server里调试成功以后,切换到Access数据库,居然发现一切问题都不存在了,完全跟Sql Server等效,难道那个Select @@identity对于Access也同样有效?有待查证。不过既然过了这一关,剩下的问题就都很简单了,只不过字段多一些而已。但是根据它的这个生成 对象的方式来看的话,如果有三个以上的表使用级联的一对多关系,比如一级分类,二级分类,三级分类,然后才是Item,那么在调用生成一级分类对象的时 候,它应该是会搜索所有的数据表,生成所有的级联对象的,这是个很严重的浪费,而且如果要是用在论坛里面,要想显示首页所有的板块,那么它就会同时在数据 库里提取出所有的帖子,这个好像不太现实,继续研究中……

1.测试了两级一对多的关系,写入数据的确变的相当方便了,省却了大量重复的SQL代码的工作。但是当使用Session.Load加一个父对象的 时候,它的子对象,以及这个子对象的所有子对象都会被加载,对于数据库或者网络都是个问题。

解 决方案:给这个一对多的关系在配置文件hbm.xml的set段加上一个属性,lazy="true",这样就变成延迟载入,调入父对象的时候不会自动加 载它的子集,只有当你要使用这个子集的时候(foreach),才会及时生成这个子集,这样就避免了浪费无用的查询时间。但是这样有一个要求,就是 session必须保持活动,不能被Close,否则就无法再去调入新的查询内容了。

2.调入父对象的时候,子对象自动调入,如果用foreach来查询子对象,那么输出的顺序是不固定的,无论在父类中使用ListSet还是 HashedSet,结果都是不固定的。

解 决方案:还没有看到真正解决这个问题的方法,只能手动加载子类,使用Session.Find加HQL语言,可以达到任意控制权限。在一篇文章里看到使用 list代替set,然后在list的里面可以添加一个index属性,标明排序的字段,实体类里面使用IList,但是我没有成功。回头再试。

3. 在使用级联删除的时候,加载一个Group,Session.Delete(Group),然后查看执行的Sql语句,在Items表里居然每一行 Item执行了一次Delete,也就是说它是按照Where ItemID=?来删除的,这样1000条记录就执行了1000次,为什么不使用Where GroupID=?呢,只要一句就搞定了,而且对于这种删除,数据库本身一般都是做了优化的。

4.突然想不起来了,占位,想起来再补。

25May/10

NHIBERNATE中 ISession.Delete方法 使用过程中遇到的问题.

Posted by Nick Xu

以下两段删除代码中,如果删除失败(例如主外键约束),在当前SESSION中,该实体已经被删除。

但是在数据库中依然存在的,而此时如果使用SESSION.GET方法来获取刚才删除失败的实体,将返回NULL值。

若要查询刚才删除失败的实体,那么需要打开一个新的SESSION。

代码一:

///<summary>
///清除数据库中 Users表中的所有数据,提供一个干净的测试数据环境
///</summary>
[Test]
public void ClearData() {
//ISession对象提供一个到后台数据库的连接,参数由App.config指定
//ITransaction对象提供一个可以被NHibernate管理的事 务。
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();

//使用查询删除所有数据
session.Delete(”Select From User Where 1=1″);

transaction.Commit();

session.Close();

}

代码二:

///<summary>
///删除指定的对象
///</summary>
[Test]
public void DeleteTest() {
ISession session = factory.OpenSession();
ITransaction transaction = session.BeginTransaction();

User aUser;

try {
//当userId指定的数据在数据库中不存在时会抛出异常
aUser = session.Load(typeof(User), userId) as User;

if (aUser!=null) { session.Delete(aUser); }

transaction.Commit();
}

catch { transaction.Rollback();  }

finally { session.Close();  }

}

   
site
site