`
java-mans
  • 浏览: 11390988 次
文章分类
社区版块
存档分类
最新评论

面向对象的NHibernate数据查询语言-HQL

 
阅读更多
对象查询 vs Sql查询
在传统的.Net企业应用的开发是以数据集为核心来进行的,数据集中的数据获取和操作都是通过标准的SQL 语言来实现的,比如通过设定SqlCommand 等组件的CommandText 为相应的Sql 来查询数据的,查询结果通常是强类型或弱类型的DataSet。但是回顾一下我们前面使用的NHibernate来获取业务域对象数据时,我们并没有使用任何的Sql语句,而是使用类似于session.CreateCriteria(typeof(publisher)).List();方法来获取数据的,返回的结果是IList或者IDictionary等保存在集合中的对象实例。
NHibernate中的查询都是面向对象的查询,通过CreateCriteria返回的ICriteria接口来获取对象的面向对象的查询方法叫条件查询,这种查询使用起来比较简单,能够满足基本的查询需要,但是如果用来实现复杂查询的时候,用起来就会比较烦琐。此外,NHibernate提供了一种强大的查询语言(Hibernate Query Language, 以下简称HQL),它的语法非常类似于Sql,但同Sql不同,它是完全面向对象的,具备继承、多态和关联等特性。此外,Java版本的Hibernate还支持通过特定的Sql语句返回面向对象的查询结果,在本文创作时使用的NHibernate 0.2版本中还未实现这一特性。下表是HQL查询语言同标准的Sql语言之间的对比:
HQL查询语言
SQL查询语言
面向对象的查询语言,“面向对象的SQL”,
强类型的查询语言
面向静态数据集的查询语言,非面向对象的
数据库平台无关的
数据库平台相关,不同平台的SQL 语言用法不同
只能用于对数据进行查询操作,不能用于数据增、删、改。
除了查询,可以通过Insert,Update , Delete
对数据进行修改
模型映射
接下来,我们来演示如果使用NHibernate实现基本的数据查询。出于简单的考虑,我们使用Sql Server默认安装的pubs示例数据库(对应于一个出版信息系统的信息模型),这样可以减少建表和测试数据录入的工作。从pubs库中,选择employee和pulishers表作为数据查询对象,两者的关系是一对多的关系,如下图示意:
其中pubishers表保存所有的出版公司信息,每个出版实体具有Id, 名称(pub_name),所在城市、州、国家的属性。Employee表保存出版公司雇员的信息,每个雇员实体具有Id(emp_id),姓(fname), 中间缩写(minit),名(lname),对应的公司标识(pub_id),雇佣时间、职位等等属性信息。
要想让NHibernate能够查询这些实体信息,首先需要根据DbSchema建立映射文件及其对应的类定义。新建一个项目,定义如下的类:
/// <summary>
/// publisher对应于出版商类
/// </summary>
public class publisher
{
public publisher()
{
}
//出版商的ID
private string _PubId;
public string PubId
{
get {return _PubId;}
set {_PubId=value;}
}
//出版商名称
private string _PubName;
public string PubName
{
get {return _PubName;}
set {_PubName=value;}
}
//城市
private string _City;
public string City
{
get{return _City;}
set{_City=value;}
}
//所在州
private string _State;
public string State
{
get {return _State;}
set {_State=value;}
}
//国家
private string _Country;
public string Country
{
get {return _Country;}
set {_Country=value;}
}
//雇员列表
private IDictionary _Employee;
public IDictionary Employee
{
get { return _Employee; }
set { _Employee = value; }
}
}
/// <summary>
/// employee对应于雇员类
/// </summary>
public class employee
{
public employee()
{
}
//雇员号
private string _EmpId;
public string EmpId
{
get { return _EmpId; }
set { _EmpId = value; }
}
//
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set { _FirstName = value; }
}
//中间名
private string _MiddleName;
public string MiddleName
{
get { return _MiddleName; }
set { _MiddleName = value; }
}
//
private string _LastName;
public string LastName
{
get { return _LastName; }
set { _LastName = value; }
}
//雇佣时间
private DateTime _HireDate;
public System.DateTime HireDate
{
get { return _HireDate; }
set { _HireDate = value; }
}
//对应的出版商
private publisher _pub;
public NHQuery.publisher pub
{
get { return _pub; }
set { _pub = value; }
}
}
建立的映射文件pubs.hbm.xml内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
<class name="NHQuery.publisher, NHQuery" table="publishers">
<id name="PubId" column="pub_id" type="String" length="4">
<generator class="assigned" />
</id>
<set name="Employee" table="employee" cascade="all" inverse="true">
<key column="pub_id" />
<one-to-many class="NHQuery.employee, NHQuery" />
</set>
<property name="PubName" column="pub_name" type="String" length="40"/>
<property name="City" column="city" type="String" length="20"/>
<property name="State" column="state" type="String" length="2"/>
<property name="Country" column="country" type="String" length="30"/>
</class>
<class name="NHQuery.employee, NHQuery" table="employee">
<id name="EmpId" column="emp_id" type="String" length="9">
<generator class="assigned" />
</id>
<property name="FirstName" column="fname" type="String" length="20"/>
<property name="MiddleName" column="minit" type="String" length="1"/>
<property name="LastName" column="lname" type="String" length="30"/>
<many-to-one name="pub" column="pub_id" class="NHQuery.publisher, NHQuery"/>
<property name="HireDate" column="hire_date" type="DateTime"/>
</class>
</hibernate-mapping>
表示层设计
界面示意图如下:
界面包括两部分,一部分用于显示条件查询的结果,一部分显示HQL查询结果,结果用DataGrid来展示,对于HQL查询来说,提供了一个输入框,用来输入HQL查询语句。
业务逻辑的实现
条件查询
假设我们现在要获取所有国家为USA的出版商,则可以通过调用CreateCriteria方法获得ICriteria接口,然后添加一个表达式对象Expression,示例如下:
ICriteria criteria=session.CreateCriteria(typeof(publisher));
criteria.Add(Expression.Eq("Country","USA"));
上面的语句对应的NHibernate运行时生成的Sql语句如下:
declare @P1 int
set @P1=3
exec sp_prepexec @P1 output, N'@p0 nvarchar(4000)', N'SELECT publishe0_.pub_id as pub_id, publishe0_.pub_name as pub_name, publishe0_.city as city, publishe0_.state as state, publishe0_.country as country FROM publishers publishe0_ WHERE (publishe0_.country = @p0)', @p0 = N'USA'
select @P1
可以注意到,NHibernate生成的Sql语句是非常智能的,性能是尽可能优化的,比如特别生成了一个针对出版商国家属性的参数化查询,而不是一条简单的Select * from publishers where country=’USA’。生成参数化查询的好处就是很多大型数据库会把参数化查询在编译后缓存起来,如果后面反复调用的话,则数据库不再重新编译,可以直接调用,对于查询性能有很大的优化。
NHibernate的条件查询表达式类Expression除了支持使用Eq实现等于条件查询外,还支持gt(大于条件),ge(大于等于),lt(小于),le(小于等于),Between(对应于Sql的Between),like(对应于Sql 的Like表达式), in(对应于Sql的in表达式),and/or(对应于Sql 的and/or逻辑运算)等等常见的查询条件,要想查询同时满足多个查询条件的数据,只要多次调用Add方法添加多个查询对象即可。
最为灵活的是,NHibernate的查询对象还提供了原生Sql语法的支持,比如下面的语句就把所有pub_name以A字母打头的数据查询出来。
criteria.Add(Expression.Sql("pub_name like 'A%'"));
除了支持类似于Sql中where的查询条件外,NHibernate的条件查询还支持对应于Sql的Order的排序功能,比如我们要想按照出版商的PubName属性按降序输出结果时,可以调用AddOrder方法来实现,代码如下:
criteria.AddOrder(Order.Desc("PubName"));
另外,条件查询还提供了简单的分页支持,我们可以利用分页支持限定返回记录的范围。比如要想从第二条记录开始,返回最多3条记录的话,可以用下面代码实现:
criteria.SetFirstResult(2);
criteria.SetMaxResults(3);
不过需要注意的一点,通过跟踪我们会发现,调用上面两个方法对查询结果范围做限制并不会改变NHibernate运行时所产生的Sql语句,因此可以看出条件查询对于分页的支持比较简单,没有对性能做优化。因此需要审慎的使用。
HQL查询
HQL查询是通过session的CreateQuery方法返回的IQuery接口来实现的,代码示意如下:
private void btnHql_Click(object sender, System.EventArgs e)
{
//Hql查询
try
{
ISession session=factory.OpenSession();
IQuery query=session.CreateQuery(this.rtbHql.Text.Trim());
this.dgHql.DataSource=null;
this.dgHql.DataSource=query.List();
session.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
可以在界面上的文本输入框中输入HQL语句,运行后检验HQL的查询结果。比如,要想用HQL获取所有国家为USA的出版商,可以输入下面的HQL语句:
from publisher where Country='USA'
要注意的是HQL语句中查询目标的类名和属性名称是区分大小写的,比如publisher不能写成Publisher,但是目前NHibernate的实现有点问题,在某些情况下对象的属性值是不区别大小写的,需要注意。
同样的,HQL也支持排序的语法,如果要按照出版社的名称排序输出结果的话,可以输入如下的查询语句:
from publisher as pub order by pub.PubName
HQL支持对象别名,上面语句中的pub就是publisher对象的别名。此外,HQL还支持group by , having子句,子查询等特性,这使得HQL也适合用于生成各种复杂报表。
HQL统计函数
很多时候,我们可能并不关心数据对象本身,可能更关心对一组相关对象的统计结果,HQL同样支持类似于Sql的Count, Sum, Avg等聚集函数的简单统计查询功能,比如想要查询国家为USA的出版社的数量,可以输入下面的查询语句:
select count(*) from publisher where Country='USA'
统计结果的展示同普通的查询不同,返回的不是一个集合,因此需要通过IQuery的Enumerable接口通过转型获取,代码如下:
private void btnValue_Click(object sender, System.EventArgs e)
{
try{
ISession session=factory.OpenSession();
IQuery query=session.CreateQuery(this.rtbHql.Text.Trim());
IEnumerator itor=query.Enumerable().GetEnumerator();
itor.MoveNext();
MessageBox.Show(itor.Current.ToString());
session.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
HQL关联查询
上面举的HQL的例子都是些单表的查询,实际应用中碰到的更多的查询是关联查询,HQL支持内连接、左联接、右连接和全连接,比如要想通过查询获取关联的雇员的名字及其工作的出版社的名称,采用Sql语句来实现的话,需要输入下面的语句:
select emp.fname, emp.minit, emp.lname, pub.pub_name from employee as emp, publishers as pub where emp.pub_id=pub.pub_id
其对应的HQL语句示意如下:
select emp.FirstName, emp.MiddleName, emp.LastName, pub.PubName from employee as emp inner join emp.pub as pub
注意在HQL语句中,我们无须写Where子句来定义关联,这是因为HQL会自动分析到我们在hbm.xml中定义的1对多的关联,自动建立关联。运行上面的HQL,你会发现DataGrid显示的网格会显示一个怪异的列表,见下图示意:
这显然不是我们想要的效果,Debug分析一下IQuery.List()方法的返回结果,你会发现只有当HQL返回publisher或者employee对象时,List()方法会返回publisher或者employee对象的ArrayList集合,但是如果只选择publisher等对象的几个属性时,返回的ArrayList中的元素是属性值数组,绑定到DataGird上的效果就不对了,那么如何解决这个问题呢?
幸好HQL提供了根据返回的属性值,构造一个新对象集合的功能,假设有一个EmpJoinPub类,它的构造函数可以接受FirstName, MiddleName, LastName,PubName作为构造参数,则我们可以把HQL改写成下面的形式:
select new EmpJoinPub(emp.FirstName, emp.MiddleName, emp.LastName, pub.PubName) from employee as emp inner join emp.pub as pub
这回HQL会返回一个EmpJoinPub对象集合,这样绑定到DataGrid的效果就是我们想要的了。下面就是EmpJoinPub类的定义:
public class EmpJoinPub
{
public EmpJoinPub(string FirstName, string MiddleName, string LastName, string PubName)
{
this._EmpName=string.Format("{0}.{1}.{2}", FirstName, MiddleName, LastName);
this._Publisher=PubName;
}
//出版商名称
private string _Publisher;
public string Publisher
{
get { return _Publisher; }
set { _Publisher = value; }
}
//雇员名称
private string _EmpName;
public string EmpName
{
get { return _EmpName; }
set { _EmpName = value; }
}
}
为了要让NHibernate知道到什么地方去找这个EmpJoinPub类,我们需要将EmpJoinPub类也导入到hbm.xml 文件定义中,在pubs.hbm.xml中添加下面的语句就可以了:
<import class="NHQuery.EmpJoinPub, NHQuery"/>
看了这边文章我加了好几次 <import> 标签总是加不上!!!!!!
分享到:
评论

相关推荐

    大、小断层矿井小波SVM融合智能故障预测matlab代码.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    垂直SeekBar(拖动条).zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    libADLMIDI1-1.5.0-bp153.1.1.x86-64.rpm

    libADLMIDI1-1.5.0-bp153.1.1.x86_64.rpm 是用于在 x86_64 架构的设备上安装的 RPM 包,具体功能如下: 名称:libADLMIDI1 版本:1.5.0 摘要:带有 OPL3 (YMF262) 模拟器的软件 MIDI 合成器库 许可证:GPL-3.0-only 和 LGPL-3.0-only 该库提供了一个基于 ADLMIDI 的软件 MIDI 合成器,它模拟了 OPL3 音源芯片(FM 合成)。它可以通过使用 ADLMIDI 库来实现多平台的 MIDI 播放和 OPL3 模拟。 该 RPM 包适用于 x86_64 架构,用于在相关设备上安装 libADLMIDI1 库文件。库文件包括: /usr/lib64/libADLMIDI.so.1 和 /usr/lib64/libADLMIDI.so.1.5.0:库文件 /usr/share/doc/packages/libADLMIDI1/AUTHORS、/usr/share/doc/packages/libADLMIDI1/README.md 等文档文件:文档文件

    基于qt+C++实现u盘插拔检测.+源码(毕业设计&课程设计&项目开发)

    基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于qt+C++实现u盘插拔检测.+源码,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~

    Quectel_Product_Brochure_CN_V7.9.pdf

    Quectel_Product_Brochure_CN_V7.9.pdf

    更换软件主题(apk方式).zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    chepai-reg-main (2).zip

    phpstudy

    Python 入门详细教程-1天学会 Python.docx

    python入门

    二维码扫描的实现.zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    移动机器人机械臂的设计开题报告.doc

    移动机器人机械臂的设计开题报告.doc

    基于QT+C++开发的智能平台访客系统+源码

    用法链接:https://menghui666.blog.csdn.net/article/details/137977678?spm=1001.2014.3001.5502 基于QT+C++开发的智能平台访客系统+源码,包含主界面、系统设置、警情查询、调试帮助、用户退出功能。 基于QT+C++开发的智能平台访客系统+源码,包含主界面、系统设置、警情查询、调试帮助、用户退出功能。 基于QT+C++开发的智能平台访客系统+源码,包含主界面、系统设置、警情查询、调试帮助、用户退出功能。

    三菱机械臂校点说明.pptx

    三菱机械臂校点说明.pptx

    按字母索引滑动.zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    激光推送客户端demo.zip

    android 源码学习. 资料部分来源于合法的互联网渠道收集和整理,供大家学习参考与交流。本人不对所涉及的版权问题或内容负法律责任。如有侵权,请通知本人删除。感谢CSDN官方提供大家交流的平台

    c语言入门,小白进军C语言.zip

    C语言诞生于美国的贝尔实验室,由丹尼斯·里奇(Dennis MacAlistair Ritchie)以肯尼斯·蓝·汤普森(Kenneth Lane Thompson)设计的B语言为基础发展而来,在它的主体设计完成后,汤普森和里奇用它完全重写了UNIX,且随着UNIX的发展,c语言也得到了不断的完善。为了利于C语言的全面推广,许多专家学者和硬件厂商联合组成了C语言标准委员会,并在之后的1989年,诞生了第一个完备的C标准,简称“C89”,也就是“ANSI C”,截至2020年,最新的C语言标准为2018年6月发布的“C18”。 [5] C语言之所以命名为C,是因为C语言源自Ken Thompson发明的B语言,而B语言则源自BCPL语言。 1967年,剑桥大学的Martin Richards对CPL语言进行了简化,于是产生了BCPL(Basic Combined Programming Language)语言。

    Python入门到精通.zip

    python入门 单元测试和测试用例 Python标准库中的模块unittest提供了代码测试工具。 单元测试用于核实函数的某个防霾呢没有问题; 测试用例是一组单元测试,这些单元测试仪器一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种收入,包含所有针对这些情形的测试。 全覆盖式测试用例包含一整套单元测试,涵盖了各种可能的函数使用方式。 对于大型项目,要实现全覆盖可能很难。通常,最初只要对针对代码的重要行为编写测试即可,等项目给广泛使用时再考虑全覆盖。 可通过的测试 创建测试用例的语法需要一段时间才能习惯,但测试用例创建后,再添加针对函数的单元测试就很简单了。要为函数编写测试用例,可先导入模块unittest以及要测试的函数,在创建一个继承unittest.TestCase的类,并编写一系列方法对函数行为的不同方面进行测试。 下面test_name_function.py一个只包含一个方法的测试用例,它检查函数get_formatted_name()在给定名和姓时能否正确的工作。

    基于matlabbenders分解算法.zip

    基于matlabbenders分解算法.zip

    dsp工程设计讲座.ppt

    dsp工程设计讲座.ppt

    Adams空间复杂机械臂动力学仿真研究.doc

    Adams空间复杂机械臂动力学仿真研究.doc

    基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip

    【资源说明】 基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip基于Android+OpenCV的车牌识别系统源码+使用文档+全部资料(优秀项目).zip 【备注】 1、该项目是个人高分毕业设计项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(如软件工程、计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

Global site tag (gtag.js) - Google Analytics