使用Hibernate SQLQuery(转)
原文地址:http://itindex.net/detail/51776-hibernate-sqlquery-sql,重新排了一下版
Hibernate对原生SQL查询的支持和控制是通过SQLQuery接口实现的。通过Session接口,我们能够很方便的创建一个SQLQuery(SQLQuery是一个接口,在Hibernate4.2.2之前,默认返回的是SQLQuery的实现类——SQLQueryImpl对象,在下文中出现的SQLQuery如非注明,都是指该子类)对象来进行原生SQL查询:
session.createSQLQuery(String sql);
SQLQuery实现了Query接口,因此你可以使用Query接口中提供的API来获取数据。
最简单的示例
session.createSQLQuery("select * from note").list();//获取所有查询结果
session.createSQLQuery("select * from note where id = 1").uniqueResult();//仅获取第一条结果
使用预处理SQL
预处理SQL的好处自然不必多说,除了众所周知的能够防止SQL注入攻击外,还能够在一定程度上提高SQL的查询效率。SQLQuery提供了众多的接口来分别设置不同类型的参数,诸如setBigDecimal、setBinary、setDouble等,详参SQLQuery的JavaDoc,此处不再赘述。这里仅重点说一下通用的SQL参数设置接口setParameter。
如下代码示范了如何使用SQLQuery执行预处理SQL:
SQLQuery query = session.createSQLQuery("select * from note where id = ?");
//设置第一个参数的值为12,即查询ID=12的note
query.setParameter(0,12);
List list = query.list();
这里需要注明一点,无论是通过不同类型参数的设置接口来设置SQL参数,还是通过setParameter来设置参数,下标都是从0开始的,而不是从1开始的!
使用自定义的结果转换器处理查询结果
SQLQuery 接口预留了setResultTransformer接口以实现使用用户自定义的ResultTransformer结果集转换器处理查询结果。 ResultTransformer接口非常简单,只有两个方法,分别用来转换单行数据和所有结果数据。经过自定义ResultTransformer生成的实体,并未加入Session,因此是非受管实体。
如下代码,示范了如何将单行数据装入LinkedHashMap对象中:
query.setResultTransformer(newResultTransformer(){
@Override
public Object transformTuple(Object[] values,String[] columns){
Map<String,Object> map =new LinkedHashMap<String,Object>(1);
int i =0;
for(String column : columns){
map.put(column, values[i++]);
}
return map;
}
@Override
public List transformList(List list){
return list;
}
}
);
如果不设置自定义的ResultTransformer转换器,则Hibernate将每行返回结果的数据按照结果列的顺序装入Object数组中。
这里介绍一个工具类: Transformers,它提供了一些常用的转换器,能够帮助我们快速转换结果集,如Transformers.aliasToBean(Note.class)能够将查询结果依别名注入到Note实体中。
使用标量
使用SQLQuery执行原生SQL时,Hibernate会使用ResultSetMetadata来判定返回的标量值的实际顺序和类型。如果要避免过多的使用ResultSetMetadata,或者只是为了更加明确的指名返回值,可以使用addScalar()。
session.createSQLQuery("select * from note where id = 1")
.addScalar("id",LongType.INSTANCE)
.addScalar("name",StringType.INSTANCE)
.addScalar("createtime",DateType.INSTANCE);
这个查询指定了SQL查询字符串,要返回的字段和类型.它仍然会返回Object数组,但是此时不再使用ResultSetMetdata,而是明确的将id,name和 createtime按照Long, String和Date类型从resultset中取出。同时,也指明了就算query是使用*来查询的,可能获得超过列出的这三个字段,也仅仅会返回这三个字段。
对全部或者部分的标量值不设置类型信息也是可以的:
session.createSQLQuery("select * from note where id = 1").addScalar("id").addScalar("name").addScalar("createtime",DateType.INSTANCE);
没有被指定类型的字段将仍然使用ResultSetMetdata获取其类型。 注意,字段不区分大小写,同时不能够指定不存在的字段!
关于从ResultSetMetaData返回的java.sql.Types是如何映射到Hibernate类型,是由方言(Dialect)控制的。假 若某个指定的类型没有被映射,或者不是你所预期的类型,你可以通过Dialet的registerHibernateType调用自行定义。
如果仅指定了一个scalar,那么...
Date createTime =(Date)session.createSQLQuery("select * from note where id = 1").addScalar("createtime",DateType.INSTANCE).uniqueResult();
如果我们的SQL语句使用了聚合函数,如count、max、min、avg等,且返回结果仅一个字段,那么Hibernate提供的这种提取标量结果的方式就非常便捷了。
实体查询
上面的查询都是返回标量值的,也就是从resultset中返回的“裸”数据。下面展示如何通过addEntity()让原生查询返回实体对象。
session.createSQLQuery("select * from note where id = 1").addEntity(Note.class);
session.createSQLQuery("select id,name,createtime from note where id = 1").addEntity(Note.class);
这个查询指定SQL查询字符串,要返回的实体。假设Note被映射为拥有id,name和createtime三个字段的类,以上的两个查询都返回一个List,每个元素都是一个Note实体。
假若实体在映射时有一个many-to-one的关联指向另外一个实体,在查询时必须也返回那个实体,否则会导致发生一个"column not found"的数据库错误。这些附加的字段可以使用*标注来自动返回,但我们希望还是明确指明,看下面这个具有指向Dog的many-to-one的例 子:
session.createSQLQuery("select id,note,createtime,author from note where id = ?").addEntity(Note.class);
author字段即为Note实体和Author实体的关联字段,只需在查询时得到该字段的值,Hibernate即可使用该值找到对应的关联实体。如上例中,note.getAuthor()即可返回当前Note所属的Author对象。
处理关联和集合类
通过提前抓取将Author连接获得,而避免初始化proxy带来的额外开销也是可能的。这是通过addJoin()方法进行的,这个方法可以让你将关联或集合连接进来。
session.createSQLQuery("select {note.*}, {author.*} from note note, user author where note.author = author.id")
.addEntity("note",Note.class)
.addJoin("author","note.author");
上面的例子是多对一的关联查询,反过来做一对多的关联查询也是可以的。如下的例子中,author.notes表示该用户发表的所有日记(Note),Set集合类型:
session.createSQLQuery("select {author.*},{note.*} from note note, user author where author.id = ? and note.author = author.id")
.addEntity("author",User.class)
.addJoin("note","author.notes");
注意:join查询会在每行返回多个实体对象,处理时需要注意。
别名和属性引用
假若SQL查询连接了多个表,同一个字段名可能在多个表中出现多次,这会导致SQL错误。不过在我们可以通过使用占位符来完美地解决这一问题。
其实在上例中已经用到了占位符:
session.createSQLQuery("select {note.*}, {author.*} from note note, user author where note.author = author.id")
.addEntity("note",Note.class)
.addJoin("author","note.author");
这个查询指明SQL查询语句,其中包含占位附来让Hibernate注入字段别名,查询并返回的实体。
上面使用的{note.*}和{author.*}标记是作为“所有属性”的简写形式出现的,当然你也可以明确地罗列出字段名。但如下的范例代码中我们让Hibernate来为每个属性注入SQL字段别名,字段别名的占位符是表别名 + . + 属性名。
注意:属性名区分大小写,而且不能够在where子句中使用占位符。
SQLQuery query = session.createSQLQuery(
"select note.id as {note.id},note as {note.note},createtime as {note.createTime},author as {note.author}, {author.*}
from note, user author
where note.id = ? and note.author = author.id");
query.addEntity("note",Note.class);
query.addJoin("author","note.author");
大多数情况下,上面的别名注入方式可以满足需要,但在使用更加复杂的映射,比如复合属性、通过标识符构造继承树,以及集合类等等情况下,则需要更加复杂的别名注入方式。
下表列出了使用别名注射参数的不同方式:
别名注入(alias injection names) 描述 | 语法 | 示例 |
简单属性 | {[aliasname].[propertyname] | A_NAME as {item.name} |
复合属性 | {[aliasname].[componentname].[propertyname]} | CURRENCY as {item.amount.currency}, VALUE as {item.amount.value} |
实体辨别器 | {[aliasname].class} | DISC as {item.class} |
实体的所有属性 | {[aliasname].*} | {item.*} |
集合键(collection key) | {[aliasname].key} | ORGID as {coll.key} |
集合id | {[aliasname].id} | EMPID as {coll.id} |
集合元素 | {[aliasname].element} | XID as {coll.element} |
集合元素的属性 | {[aliasname].element.[propertyname]} | NAME as {coll.element.name} |
集合元素的所有属性 | {[aliasname].element.*} | {coll.element.*} |
集合的所有属性 | {[aliasname].*} | {coll.*} |
在hbm文件中描述结果集映射信息,并在查询中使用
对于一些复杂的结果集映射,往往需要像MyBatis那样在文件中手动配置好,然后在程序中使用。幸运的是Hibernate也提供了类似的功能,你可以使用自己配置的结果集映射来处理返回的结果集数据:
SQLQuery query = session.createSQLQuery("select note.id as {note.id},note as {note.note},createtime as {note.createTime},author as {note.author}, {author.*} from note, user author where note.id = ? and note.author = author.id");//使用在hbm文件中配置的自定义结果集映射
query.setResultSetMapping("noteAnduthor");
query.list();
执行更新操作
使用SQLQuery执行数据库更新操作比较容易,除了像查询时那样需要指定SQL语句(如有需要还需设置SQL参数)外,仅需调用executeUpdate()方法,即可提交更新操作。代码如下所示:
session.createSQLQuery("update createtime = ? from note where note.id = ?");
query.setDate(0,newDate());
query.setLong(1,1L);
query.executeUpdate();
executeUpdate方法的返回结果为改变的数据库记录的行数。
使用Hibernate SQLQuery(转)的更多相关文章
- Hibernate SQLQuery简单实用,做链接查询
工单里面可能有0个告警,一个或多个告警,当工单中没有告警的时候也需要将工单显示出来,所以就需要使用工单和告警的做链接查询,下面是具体实例 表: CREATE TABLE `alarm` ( `id` ...
- 'org.hibernate.SQLQuery' is deprecated
'org.hibernate.SQLQuery' is deprecated 在Hibernate5.2之后,SQLQuery已经被摒弃,改用NativeQuery代替了. 在Hibernate中使用 ...
- Hibernate - SQLQuery
使用SQLQuery 对原生SQL查询执行的控制是通过SQLQuery接口进行的,通过执行Session.createSQLQuery()获取这个接口.下面来描述如何使用这个API进行查询. 标量查询 ...
- Hibernate SQLQuery 原生SQL 查询及返回结果集处理-1
第一篇:官方文档的处理方法,摘自官方 在迁移原先用JDBC/SQL实现的系统,难免需要采用hibernat native sql支持. 1.使用SQLQuery hibernate对原生SQL查询执行 ...
- Hibernate SQLQuery 原生SQL 查询及返回结果集处理-2
1. 返回List, .setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP);将结果转为Map,存放到list中,即list中为若干 ...
- 【Hibernate】---Query、Criteria、SQLQuery
一.核心配置文件 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-con ...
- Java三大框架之——Hibernate
什么是Hibernate? Hibernate是基于ORM(O:对象,R:关系,M:映射)映射的持久层框架,是一个封装JDBC的轻量级框架,主要实现了对数据库的CUPD操作. 注:CRUD是指在做计算 ...
- Hibernate的增删改查
一.搭建Hibernate开发环境,这里就不说了,直接说环境搭好后的事情. 二.项目的目录结构
- [开源项目]Hibernate基本使用
开源项目(1)Hibernate基本使用 Hibernate介绍 Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象 ...
随机推荐
- ecshop编辑器FCKeditor修改成KindEditor编辑批量上传图片
ecshop一直使用的编辑器是fck,这个不用多说,相信很多朋友用的很悲剧吧,特别是图片不能批量上传图片. 今天小编就分享一下怎么换掉fck,放上实用的kindeditor,最新ecshop版 ...
- android 项目中如何引入第三方jar包
http://www.360doc.com/content/13/0828/08/11482448_310390794.shtml
- iframe使用方法
--点击按钮会把地址里的页面显示在oframe里,对iframe可以设置宽和高<iframe src="demo_iframe.htm" name="iframe_ ...
- 拨打电话tel: 跳转到邮件mailto:(html)
拨打电话 <a href="tel://0571866000">0571-866000</a> 跳转到邮件 <a href="mailto: ...
- web前端学习部落22群开源分享 左边菜单导航
有大量web前端开发工具及学习资料,可以搜群[ web前端学习部落22群 ]进行下载,遇到学习问题也可以问群内专家以及课程老师哟 <!DOCTYPE html> <html lang ...
- hdu 1503 Advanced Fruits
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1503 思路:这是一道最长公共子序列的题目,当然还需要记录路径.把两个字符串的最长公共字串记录下来,在递 ...
- 与你相遇好幸运,使用gulp流程化Typescript后端开发
tsc --init { "compilerOptions": { "module": "commonjs", ...
- 四种常见的App弹窗设计,你有仔细注意观察吗?
弹窗又称为对话框,是App与用户进行交互的常见方式之一.弹窗分为模态弹窗和非模态弹窗两种,两者的区别在于需不需要用户对其进行回应.模态弹窗会打断用户的正常操作,要求用户必须对其进行回应,否则不能继续其 ...
- 解决Yii2 启用_csrf验证后POST数据仍提示“您提交的数据无法验证”
一 CSRF 概念 CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XS ...
- UVA 11481 Arrange the Numbers(组合数学 错位排序)
题意:长度为n的序列,前m位恰好k位正确排序,求方法数 前m位选k个数正确排,为cm[m][k],剩余m - k个空位,要错排,这m - k个数可能是前m个数中剩下的,也可能来自后面的n - m个数 ...