當 HQL 或 Criteria 無法滿足複雜查詢需求(如多表連接、特定數據庫函數、優化慢查詢)時,Hibernate 提供的原生 SQL 查詢成為強有力的補充。本文系統總結 SQLQuery 接口的核心用法,覆蓋標量查詢、實體查詢、關聯處理、命名查詢及存儲過程調用。
Session 對象session.createSQLQuery(sql) 獲取 SQLQuery 對象addScalar()(標量)或 addEntity()(實體)映射結果setParameter() 綁定值list() 或 uniqueResult() 執行查詢返回 Object[] 列表,每個元素對應一行數據的字段值。默認使用 ResultSetMetaData 推斷類型,性能稍弱。推薦显式指定字段與類型:
List list = session.createSQLQuery("SELECT * FROM person_info")
.addScalar("person_id", StandardBasicTypes.INTEGER)
.addScalar("name", StandardBasicTypes.STRING)
.addScalar("age", StandardBasicTypes.INTEGER)
.list();
for (Object[] row : list) {
System.out.println("ID: " + row[0] + ", Name: " + row[1] + ", Age: " + row[2]);
}
addScalar 雙重作用:① 限定返回列(未聲明的列不會出現在結果中)② 显式指定類型,避免 MetaData 開銷。
前提:SQL 必須查出映射實體的所有字段(含關聯外鍵)。
// 單表實體查詢
List<Person> persons = session.createSQLQuery("SELECT * FROM person_info")
.addEntity(Person.class)
.list();
// 多表連接查詢多個實體(需別名)
String sql = "SELECT p.*, e.* FROM person_inf p INNER JOIN event_inf e ON p.person_id = e.person_id";
List list = session.createSQLQuery(sql)
.addEntity("p", Person.class)
.addEntity("e", Event.class)
.list();
for (Object[] row : list) {
Person p = (Person) row[0];
Event e = (Event) row[1];
System.out.println(p.getName() + " - " + e.getTitle());
}
解決 N+1 問題,通過 addJoin() 將關聯集合一併查詢並初始化:
String sql = "SELECT p.*, e.* FROM person_inf p, event_inf e WHERE e.person_id = p.person_id";
List list = session.createSQLQuery(sql)
.addEntity("p", Person.class)
.addJoin("e", "p.myEvents") // 關聯路徑:Person.myEvents
.list();
// 返回的 Person 對象,myEvents 集合已完全初始化,無需額外查詢
在映射文件(.hbm.xml)或註解中定義:
<sql-query name="personEventQuery">
<return alias="p" class="Person"/>
<return alias="e" class="Event"/>
<return-scalar column="p.age" type="int"/>
SELECT p.*, e.* FROM person_inf p, event_inf e
WHERE p.person_id = e.person_id AND p.age = :age
</sql-query>
代碼中直接調用:
List list = session.getNamedQuery("personEventQuery")
.setInteger("age", 30)
.list();
可通過 <resultset> 復用結果映射配置。
要求:函數必須返回結果集;存儲過程第一個參數為 OUT 類型的結果集游標。
<sql-query name="callSelectPerson" callable="true">
<return class="Person">
<return-property name="name" column="name"/>
<return-property name="age" column="age"/>
<return-property name="id" column="person_id"/>
</return>
{CALL select_person()} -- 或 EXEC select_person
</sql-query>
Java 調用同普通命名查詢:session.getNamedQuery("callSelectPerson").list()。
addScalar/addEntity 避免 MetaData 開銷