首先,把PlantItem对象的数据结构做一些调整,然后再进行本节中的测试。
在NHibernate考察系列 04一节中测试结果,象PlantItem这种复合主键对象,使用一个语意上的ID比较合适,这里我们就按照这种方式修改过来。因为domain对ID属性没有任何依赖,不用于对象间的关联,因此使用一个整数类型就可以了。为TBLPLANTITEM表添加一个int的ID字段,设置成identity(最好为ID字段添加一个非聚集索引)。类和映射文件大致如下:
public int ID
{
get { return _id; }
set { _id = value; }
}
private int _id;
public virtual Plant Plant
{
get { return _plant; }
set { _plant = value; }
}
private Plant _plant;
public virtual Item Item
{
get { return _item; }
set { _item = value; }
}
private Item _item;
用下面的测试代码加入测试数据:
Company company = new Company("1000", "test company 1", "", new HashedSet());
session.Save(company);
Plant plant1 = new Plant("1105", "test plant 1", company);
session.Save(plant1);
Plant plant2 = new Plant("1202", "test plant 2", company);
session.Save(plant2);
Item item1 = new Item("FK1.1023.78AF", "2.5# LCD", " CS", new decimal(85.7));
session.Save(item1);
Item item2 = new Item("191.1023.78AF", "test item 2", " CS", new decimal(12));
session.Save(item2);
Item item3 = new Item("023.0000.1233", "test item 3", " CS", new decimal(25.7));
session.Save(item3);
Item item4 = new Item("FK1.2314.ZF31", "test item 4", " CS", new decimal(1.789));
session.Save(item4);
Item item5 = new Item("1000 0000 0070", "test item 5", "EA", new decimal(19));
session.Save(item5);
Item item6 = new Item("ANTXX00230GD", "test item 6", "EA", new decimal(19));
session.Save(item6);
//创建PlantItem对象
session.Save(new PlantItem(plant1, item1, " CS", ItemCategoryEnum.P, PurchaseCategoryEnum.JIT, StockOptionEnum.ERP));
session.Save(new PlantItem(plant1, item2, " CS", ItemCategoryEnum.P, PurchaseCategoryEnum.PO, StockOptionEnum.None));
session.Save(new PlantItem(plant1, item3, " CS", ItemCategoryEnum.P, PurchaseCategoryEnum.PO, StockOptionEnum.ERP));
session.Save(new PlantItem(plant1, item4, " CS", ItemCategoryEnum.M, PurchaseCategoryEnum.JIT, StockOptionEnum.Hub));
session.Save(new PlantItem(plant1, item5, " CS1", ItemCategoryEnum.M, PurchaseCategoryEnum.JIT, StockOptionEnum.None));
session.Save(new PlantItem(plant1, item6, "EA", ItemCategoryEnum.M, PurchaseCategoryEnum.PO, StockOptionEnum.None));
session.Save(new PlantItem(plant2, item2, " CS", ItemCategoryEnum.M, PurchaseCategoryEnum.JIT, StockOptionEnum.ERP));
session.Save(new PlantItem(plant2, item3, "PCS", ItemCategoryEnum.M, PurchaseCategoryEnum.PO, StockOptionEnum.Hub));
session.Save(new PlantItem(plant2, item4, "PCS", ItemCategoryEnum.P, PurchaseCategoryEnum.PO, StockOptionEnum.Hub));
session.Save(new PlantItem(plant2, item5, "PCS", ItemCategoryEnum.M, PurchaseCategoryEnum.PO, StockOptionEnum.Hub));
1. Criteria 条件式查询
ICriteria criteria = session.CreateCriteria(typeof(PlantItem));
criteria.Add(Expression.Eq("PurchaseCategory", PurchaseCategoryEnum.PO))
.AddOrder(Order.Desc("Item"))
.SetFirstResult(2).SetMaxResults(2);
IList list = criteria.List();
for (int i = 0; i :Time这一条件生成SQL?((CREATE_DATE=@date AND CREATE_TIME>@time) OR CREATE_DATE>@date)?在不同的运用场景下情况会更复杂。
这里对上面这种情形的用户自定义类型带来相当的限制,因此慎用。
详细的hql语法参考NHibernate的文档。
个人对条件式查询、hql的看法:
1. 实现多数据库兼容。
2. 对持久化存取知识的封装。可能比较直观的想法是既然做到了多数据库兼容,数据库知识的封装作用已经不大。一方面,对象属性与持久化存储之间有差异,使用hql使得这种差异被封装在映射文件或者自定义的映射类型中。另一方面,在粗粒度对象设计的情况下,实体跟表之间基本一一对应,在对象模型层面写hql跟在table层面写SQL的确没有太多差异。但如果使用细粒度对象设计,对象模型与table之间的差异就会相当明显,domain既要处理映射行为,控制数据存取,又要处理复杂的对象关系,情况就复杂化了。分离一个DAL出来,成本代价也是比较高。
3. 性能问题。
这个问题就有点复杂化了。使用ORM之后,在应用与数据库之间建立一个隔离带,性能问题不会消失。解决性能问题有两种倾向,第一种是类似iBATIS做法,把存取数据用的SQL做分离,集中管理,方便数据库、SQL层面做优化。另一种做法是结合domain的架构设计来解决性能问题。
NHibernate的应用产生性能问题,一方面是不合理的对象关系,在数据加载方面造成浪费、开销。另一方面是不合理的条件式查询、hql的使用。确保良好的对象模型设计,根据设计思想,在条件式查询、hql的使用上制定约束、规范变得很重要。
3. Named Query
Named Query是将hql从代码中分离出来,以命名的形式放入配置文件中。这跟iBATIS的方式有点类似,便于对hql的集中管理和维护。
在PlantItem.hbm.xml配置文件的hibernate-mapping元素下添加下面的Named Query hql语句:
from NH12.MyExample.Domain.PlantItem as pi
where pi.Item.ItemDescription like :ItemDesc
order by pi.Plant, pi.Item
]]>
程序中使用:
IList list = session.GetNamedQuery("NH12.MyExample.Domain.PlantItemQuery")
.SetString("ItemDesc", "test item 5%")
.List();
4. Native SQL
Native SQL提供直接对数据库访问的机会。
string sql=@"
select pi.PLANT_ID as PlantID,pi.ITEM_ID as ItemID,i.ITEM_DESCRIPTION as ItemDescription
from TBLPLANTITEM pi
inner join TBLITEM i on i.ITEM_ID=pi.ITEM_ID
order by pi.PLANT_ID, i.ITEM_DESCRIPTION
";
ISQLQuery query = session.CreateSQLQuery(sql)
.AddScalar("PlantID", NHibernateUtil.String)
.AddScalar("ItemID", NHibernateUtil.String)
.AddScalar("ItemDescription", NHibernateUtil.String);
IList list = query.List(); 返回的是一个object数组的列表。
用下面的方法返回实体对象列表:
string sql = @"select * from TBLPLANTITEM";
ISQLQuery query = session.CreateSQLQuery(sql).AddEntity(typeof(PlantItem));
IList list = query.List();
用下面的方法可以一次返回多个对象列表:
string sql = @"select {pi.*}, {i.*} from TBLPLANTITEM pi, TBLITEM i where pi.ITEM_ID=i.ITEM_ID";
ISQLQuery query = session.CreateSQLQuery(sql)
.AddEntity("pi", typeof(PlantItem))
.AddEntity("i", typeof(Item));
IList list = query.List();
for (int i = 0; i |