请教Hibernate中CriteriaQuery可以使用原生sql作为排序条件么

 我来答
day忘不掉的痛
推荐于2016-03-25 · 知道合伙人数码行家
day忘不掉的痛
知道合伙人数码行家
采纳数:62646 获赞数:223939
本人担任公司网络部总经理多年,有充足的网络经验、互联网相关知识和资讯。

向TA提问 私信TA
展开全部
  Hibernate对原生SQL查询的支持和控制是通过SQLQuery接口实现的,这种方式弥补了HQL、Criterion查询的不足,在操作和使用上往往更加的自由和灵活,如果使用得当,数据库操作的效率还会得到不同程度的提升。

  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转换器,则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");

  大多数情况下,上面的别名注入方式可以满足需要,但在使用更加复杂的映射,比如复合属性、通过标识符构造继承树,以及集合类等等情况下,则需要更加复杂的别名注入方式。
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式