使用HibernateSQLQuery执行原生SQL
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
使用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(new ResultTransformer() {
@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 转换器,将每行返回结果的数据按照结果列的顺序装入中。
当然,也可以直接指定默认的MAP 转换规
贝y:query.setResultTransformer(
ers.ALIAS_TO_ENTITY_MAP);
这里介绍一个工具类:
则Hibernate Object 数组
Transform
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 获取其类型。
、/. I、, G > :
注意
,字段不区分大小写,同时不能够指定不存在的字段
!
关于从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 字段别名,字段别名的占位符是表别名+ . + 属性名。
注意
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 {}
复合属性
{[aliasname].[componentname].[propertyname]} CURRENCY as {item.amount.currency}, V ALUE 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 {}
集合元素的所有属性{[aliasname].element.*}
{coll.element.*}
集合的所有属性{[aliasname].*} {coll.*}。