`

使用Hibernate SQLQuery执行原生SQL

 
阅读更多
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(newResultTransformer(){@OverridepublicObject transformTuple(Object[] values,String[] columns){Map<String,Object> map =newLinkedHashMap<String,Object>(1);int i =0;for(String column : columns){ map.put(column, values[i++]);}return map;}@OverridepublicList 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方法的返回结果为改变的数据库记录的行数。


转载地址: http://www.yshjava.cn/post/543.html

分享到:
评论

相关推荐

    Hibernate SQLQuery执行原生SQL.docx

    我们能够很方便的创建一个SQLQuery(SQLQuery是一个接口,在Hibernate4.2.2之前,默认返回的是SQLQuery的实现类——SQLQueryImpl对象,在下文中出现的SQLQuery如非注明,都是指该子类)对象来进行原生SQL查询:。...

    HIBERNATE_QUERY

    Hibernate支持强大且易于使用的面向对象查询语言(HQL)。 如果希望通过编程的方式创建查询,Hibernate... 你也可以用原生SQL(native SQL)描述Hibernate查询,Hibernate额外提供了将结果集(result set)转化为对象的支持。

    SQL查询构建工具Querydsl.zip

    可跟 Hibernate 和 JPA 等框架结合使用。 基本查询: JPAQuery query = new JPAQuery(entityManager); List persons = query.from(person)  .where(  person.firstName.eq("John"),  person.lastName....

    Hibernate+中文文档

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    hibernate3.2中文文档(chm格式)

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    HibernateAPI中文版.chm

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    最全Hibernate 参考文档

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    Hibernate中文详细学习文档

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    Hibernate 中文 html 帮助文档

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    Hibernate_3.2.0_符合Java习惯的关系数据库持久化

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    hibernate 体系结构与配置 参考文档(html)

    使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    hibernate 教程

    原生SQL查询 13.1. 创建一个基于SQL的Query 13.2. 别名和属性引用 13.3. 为SQL查询命名 14. 性能提升(Improving performance) 14.1. 理解集合的性能 14.1.1. 分类 14.1.2. Lists, maps 和...

    Jpa 原生SQL分页查询“一个别名引发的一场血案”

    问题描述: 备注:刚开始SQL 没有加任何AS ... WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM 第二页 明显不一样的处理逻辑 select med.* fro

    Hibernate教程

    11.4.4. 使用原生SQL的查询 11.5. 修改持久对象 11.6. 修改脱管(Detached)对象 11.7. 自动状态检测 11.8. 删除持久对象 11.9. 在两个不同数据库间复制对象 11.10. Session刷出(flush) 11.11. 传播性持久化...

    Hibernate3+中文参考文档

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    hibernate3.04中文文档.chm

    11.4.4. 使用原生SQL的查询 11.5. 修改持久对象 11.6. 修改脱管(Detached)对象 11.7. 自动状态检测 11.8. 删除持久对象 11.9. 在两个不同数据库间复制对象 11.10. Session刷出(flush) 11.11. 传播性持久化...

    Hibernate参考文档

    10.4.4. 使用原生SQL的查询 10.5. 修改持久对象 10.6. 修改脱管(Detached)对象 10.7. 自动状态检测 10.8. 删除持久对象 10.9. 在两个不同数据库间复制对象 10.10. Session刷出(flush) 10.11. 传播性持久化...

    hibernate

    原生SQL查询 13.1. 创建一个基于SQL的Query 13.2. 别名和属性引用 13.3. 为SQL查询命名 14. 性能提升(Improving performance) 14.1. 理解集合的性能 14.1.1. 分类 14.1.2. Lists, maps 和...

    hibernate 框架详解

    使用原生SQL的查询 11.5. 修改持久对象 11.6. 修改脱管(Detached)对象 11.7. 自动状态检测 11.8. 删除持久对象 11.9. 在两个不同数据库间复制对象 11.10. Session刷出(flush) 11.11. 传播性持久化...

    hibernate_reference中文文档.pdf

    3.4.1. SQL 方言 ...................................................... 42 3.4.2. 外连接抓取(Outer Join Fetching) .............................. 43 3.4.3. 二进制流(Binary Streams) ....................

Global site tag (gtag.js) - Google Analytics