Hibernate 關於執行sql查詢語句(轉)


原文  http://www.yshjava.cn/post/543.html

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");

大多數情況下,上面的別名注入方式可以滿足需要,但在使用更加復雜的映射,比如復合屬性、通過標識符構造繼承樹,以及集合類等等情況下,則需要更加復雜的別名注入方式。

下表列出了使用別名注射參數的不同方式:

 別名注入(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, new Date()); query.setLong(1, 1L); query.executeUpdate();

executeUpdate方法的返回結果為改變的數據庫記錄的行數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM