1. 程式人生 > >使用Hibernate SQLQuery執行原生SQL

使用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(){@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方法的返回結果為改變的資料庫記錄的行數。

相關推薦

使用Hibernate SQLQuery執行原生SQL

Hibernate對原生 SQL查詢的支援和控制是通過SQLQuery介面實現的。通過Session介面,我們能夠很方便的建立一個SQLQuery(SQLQuery是一個 介面,在Hibernate4.2.2之前,預設返回的是SQLQuery的實現類——SQLQueryImpl物件,在下文中出現的SQLQ

Hibernate執行原生SQL

nts rest tar current dex string new ash star 1、查詢指定字段 public List<Object[]> getUseList( Integer index, Integer offset, String

hibernate,將原生SQL執行的結果轉換為Map

核心:設定處理查詢結果的策略query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP),表示將結果轉換為Map型別預設結果是[“張三”,20],加了Transformers.ALIAS_TO_ENTITY_MAP之

hibernate中直接執行原生sql遇到型別轉換問題

今天做了個用hibernate直接執行原生sql的查詢,遇到char型別的資料庫欄位查出來只能截斷成一位,後經高人指導,要用自定義的方言,如下: public class OracleDialect extends Oracle10gDialect{  public Orac

Hibernate執行原生sql時,將資料庫的char(n)型別轉換成了character型別的解決方案

在使用Hibernate的原生態SQL對Oracle進行查詢時,碰到查詢char型別的時候始終返回的是一個字元,開始認為應該是Hibernate在做對映的把資料型別給對映成char(1),在經過查詢網上的一些資料,得知產生這個問題的主要原因確實是Hibernate再查詢Or

django-sqlite執行原生sql

sqlit cursor object from get ans color fetchall djang from django.db import connection question_obj = models.Questions.objects.get

Thinkphp3.2.3:執行原生SQL語句

【查詢語句】query方法 示例:查詢blog_article表中的文章標題title欄位 //構造sql語句 $sql = "select `title` from blog_article";

tp3.2 執行原生sql

 from: http://zixuephp.net/article-253.html Thinkphp 3.2.3 在某些時候需要執行原生sql語句,會更方便些,執行原生sql語句的兩個方法        獲取結果集query(查詢語句),返回結果集陣列:php$m = M

Laravel 執行原生SQL時,Group by必須包含select欄位的問題

正常啟動無問題,就是執行sql時提示group by中的欄位必須包含select欄位中的條目,後經百度以及查閱只需修改database.php中的內容即可解決: 'strict' => true

jpa執行原生sql union bug解決

@Query(value = "SELECT * FROM count_entity where id=-1111 union (SELECT * FROM count_entity where video_name =

坑之 Jpa 執行原生SQL 返回對映為物件

當使用jpa執行查詢時,因為業務上的需求,執行復雜的sql操作。 但是query.getResultList()返回的是一個List。也就是說每行的資料被作為一個物件陣列返回。 Query produ

關於hibernate如何把原生sql查出的結果轉化為物件

原生SQL查詢執行的控制是通過SQLQuery介面進行的,通過執行Session.createSQLQuery()獲取這個介面。下面來描述如何使用這個API進行查詢。 標量查詢(Scalar queries) 最基本的SQL查詢就是獲得一個標量(數值)的列表

關於使用JdbcTemplate封裝的方法執行原生sql語句的常用寫法

1、使用JdbcTemplate的execute()方法執行sql語句示例:jdbcTemplate.execute("CREATE TABLE USER (user_id integer, user_name varchar(100))"); 2、如果是更新或插入可以使用

Laravel中執行原生SQL語句,使用paginate分頁

1、執行原生sqlpublic function getList($data){ //獲取前端傳過來的引數 $user = $data['userId']; $office = $data['officeId']; $key = $data['oneK

JPA執行原生SQL返回指定物件

//生成EntityManger protected EntityManager em; //執行原生SQL Query nativeQuery = em.createNativeQuery(Strin

sql 在sqlplus下能正確執行,但是hibernate 原生sql執行報列名無效錯誤

這是我原來的sql: String sql="select s.* from(select t.*,rownum n from (select id,msg_Title,msg_Sdate from Sys_Msg where status=? and msg_sdate&

Hibernate SQLQuery查詢返回空List,在mysql命令下直接執行sql語句可以正常得到記錄

遇到的問題如標題所述。 用Hibernate的SQLQuery來list記錄, 背景: 1、前一天還能正常返回資料,今天就不可以用了,程式碼絕對沒有變動過。 2、是多表查詢,且在where條件中還使用了select查詢(就這樣套了很多層)。 3、以為是多表聯查有問題,百度之

hibernate原生sql封裝,報錯信息:could not find setter for rownum_

not .com 解決 做了 hiberna could 解決方法 ber bsp 今天用hibernate的時候,用了一個原生態sql做了一個分頁查詢,結果就報錯了。。。 找到解決方法了:http://shmily2038.iteye.com/blog/17049

hibernate執行SQL語句返回的list集合中的內容是Object[]物件,而不是entity物件

現狀:在entity類中存在資料庫表不存在的欄位,這些欄位只是做展示,不做在資料庫表中進行儲存。但是在sql語句查詢的時候,出現了:返回的list集合中的內容是Object[]物件,而不是entity物件 程式碼:pojo程式碼: // 主鍵 private String id;

hibernate的baseDao和原生SQL

1、 BaseDAO 需求: 按名字分頁查詢對應書籍資訊 package com.zking.eight.dao; import java.util.Collection; import java.util.List; import java.util.Map