摘要:在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類是如何聲明的:
[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 );
}
}
可以看到唯一與我們前面聲明的Blog類不同的地方就在於下面這句話:
[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(),重新修改我們的測試代碼之後:
[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);
}
}
現在測試可以正常通過了,大家在使用延遲載入的時候不要忘了new SessionScope()。
二.使用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中驗證輸入數據的有效性。