摘要:在 ActiveRecord 中把数据库表之间的关联关系采用对象间的聚合关系来表现,然而这却带来一系列的性能上的问题。就像我在 One-Many 中用到的例子 Blog ,使用 Blog.Find(1) 查找了一个 Blog 对象,也许我们只用到它,但事实它却把该 Blog 所关联的 Post 对象也读取出来放在了内存中,于是我们就需要有一种方法来实现只在需要 Post 对象的时候框架再自动读取。另外 ActiveRecord 只提供了 Find(id),FindAll() 这样两个静态的查询方法,在我们查询中还远远不够,这方面 ActiveRecord 为我们提供了 HQL 语言的支持,同时也可以通过设置 Where 子句来实现一些简单的查询。
主要内容
1 .实现延迟加载
2 .使用 Where 子句
一.实现延迟加载
要实现延迟加载,其实只要在 HasMany 特性中使用 Lazy=true 就可以了。来看我们的 Blog 类是如何声明的:
Blog 类不同的地方就在于下面这句话:
[ActiveRecord( " Blogs " )] public class Blog : ActiveRecordBase { private int _id; private String _name; private String _author; private IList _posts; [PrimaryKey(PrimaryKeyType.Identity, " blog_id " )] public int Id { get { return _id; } set { _id = value; } } [Property(" blog_name " )] public String Name { get { return _name; } set { _name = value; } } [Property(" blog_author " )] public String Author { get { return _author; } set { _author = value; } } [HasMany(typeof (Post), Table = " posts " , ColumnKey = " post_blogid " ,Lazy = true )] public IList Posts { get { return _posts; } set { _posts = value; } } public static void DeleteAll() { DeleteAll( typeof (Blog) ); } public static Blog[] FindAll() { return (Blog[]) FindAll( typeof (Blog) ); } public static Blog Find( int id) { return (Blog) FindByPrimaryKey( typeof (Blog), id ); } }
可以看到唯一与我们前面声明的
[HasMany(typeof (Post), Table= "posts" , ColumnKey= "post_blogid" ,Lazy= true )]
现在我们来写一个简单的测试代码:
[Test] public void TestLazyLoad() { // 找到Blog对象 Blog blog = Blog.Find( 8 ); string resultName = blog.Name; // 执行这句话的时候才载入Post对象 int resultCount = blog.Posts.Count; string expectedName = " Terrylee " ; int expectedCount = 2 ; Assert.IsNotNull(blog); Assert.AreEqual(expectedName,resultName); Assert.AreEqual(expectedCount,resultCount); }
测试,红灯!?报出了下面的错误:
ARDemo.OneManyFixture.TestLazyLoad : NHibernate.LazyInitializationException : Failed to lazily initialize a collection - no session
问题出在了我们得到 Blog 对象后,并没有把当前的 Session 保存下来, ActiveRecord 在实现延迟载入时找不到一个 NHibernate 的 ISession 。为了保存当前的 Session 我们必须使用 new SessionScope() ,重新修改我们的测试代码之后:
new SessionScope() 。
[Test] public void TestLazyLoad() { using ( new SessionScope()) { // 找到Blog对象 Blog blog = Blog.Find( 8 ); string resultName = blog.Name; // 执行这句话的时候才载入Post对象 int resultCount = blog.Posts.Count; string expectedName = " Tech Space " ; int expectedCount = 2 ; Assert.IsNotNull(blog); Assert.AreEqual(expectedName,resultName); Assert.AreEqual(expectedCount,resultCount); } }
现在测试可以正常通过了,大家在使用延迟载入的时候不要忘了
二.使用 Where 子句
在 ActiveRecord 中实现稍微复杂的一点的查询,我们就不能用使用 Find(id) , FindAll() 这两个静态的方法了,这时就需要使用 HQL 语句来实现,这个在后续文章中我会专门用一篇文章来介绍。我们也可以使用 ActiveRecord 来实现一些简单的查询,个人认为,这种查询把条件写在了特性里面,以一种硬编码的方式来实现,灵活性变得很差,使用的情况不是很多。看一个简单的例子,比如我们要查询某人发表的 Category 为“ Castle ”的 Posts ,可以在 Blog 实体类中添加一个 CastlePosts 的特性:
[HasMany( typeof (Post),Table = " posts " , ColumnKey = " post_blogid " ,Where = " post_categories='Castle' " )] public IList CastlePosts { get { return _posts; } set { _posts = value; } }
注意:在 Where 中写条件时所用的 Posts 表的字段名,而不是 Post 实体类对应的属性
编写一个简单的测试代码:
[Test] public void TestCondWhere() { // 找到Blog对象 Blog blog = Blog.Find( 8 ); // 获取Castle的Posts数量 int resultCount = blog.CastlePosts.Count; int expectedCount = 2 ; Assert.IsNotNull(blog); Assert.AreEqual(expectedCount,resultCount); }
这里我们调用 CastlePosts 得到的就是 Category 为 Castle 的 Posts 。关于延迟加载和使用 Where 子句就介绍到这儿了。下篇文章介绍如何在 RectiveRecord 中验证输入数据的有效性。