摘要:在
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 );
}
}
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);
}
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);
}
}
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; }
}
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);
}
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
中验证输入数据的有效性。
参考资料
Castle
的官方网站http://www.castleproject.org