HQL (Hibernate Query Language) 是面向對象的查詢語言,語法類 SQL 但操作實體與屬性。本文系統梳理 HQL 核心查詢模式:實體查詢與 N+1 陷阱、對象導航查詢、原生 SQL 查詢、內/外連接查詢、外置命名查詢,並給出演示代碼與優化建議。
List<Student> list = session.createQuery("from Student").list(); // 推薦:list() 一次性加載
// 或
Iterator<Student> iter = session.createQuery("from Student").iterate(); // 慎用!
iterate() 返回迭代器,優先從一級緩存 (Session) 讀取。// 1. 先用 list() 將數據加載到 Session 緩存
session.createQuery("from Student").list();
// 2. 再用 iterate() 從緩存獲取,不再發 N 條 SQL
Iterator<Student> iter = session.createQuery("from Student").iterate();
while (iter.hasNext()) { /* 處理 */ }
最佳實踐: 直接用 list() 或 scroll() (流式處理大數據),除非明確需要緩存優化的迭代場景。
利用實體關聯直接在 HQL 中「點」過去,避免手寫連接,簡化語句並利用映射元數據優化。
// 查詢班級名包含 "2" 的所有學生
List<Student> list = session.createQuery(
"from Student s where s.classes.name like '%2%'"
).list();
s.classes.name 自動轉換為內連接 t_student JOIN t_classes ON ... WHERE classes.name LIKE ...。繞過 HQL 直接執行 SQL,適合複雜報表、存儲過程、特定數據庫函數。
List<Object[]> rows = session.createSQLQuery("select * from t_student").list();
for (Object[] row : rows) {
System.out.println(row[0] + ", " + row[1]); // 按索引獲取列
}
// 映射到實體 (需列名匹配或加 Scalar)
List<Student> students = session.createSQLQuery("select * from t_student")
.addEntity(Student.class).list();
// 參數綁定
session.createSQLQuery("select * from t_student where id = ?").setParameter(0, 1).uniqueResult();
// 顯式
List<Object[]> list = session.createQuery(
"select s.name, c.name from Student s join s.classes c"
).list();
// 隱式 (導航查詢自動轉內連接)
List<Student> list = session.createQuery(
"from Student s where s.classes.name = '一班'"
).list();
List<Object[]> list = session.createQuery(
"select s.name, c.name from Student s left join s.classes c"
).list();
// 包含無班級的學生 (c.name 為 null)
List<Object[]> list = session.createQuery(
"select s.name, c.name from Student s right join s.classes c"
).list();
// 包含無學生的班級
將 HQL/SQL 定義在映射文件中,代碼中按名稱調用,實現 SQL 與代碼分離、集中維護、熱更新(需框架支持)。
<hibernate-mapping>
<class name="Student" table="t_student">...</class>
<query name="findStudentById">
<![CDATA[ from Student s where s.id < ? ]]>
</query>
</hibernate-mapping>
Query query = session.getNamedQuery("findStudentById");
query.setParameter(0, 10);
List<Student> list = query.list();
list()、scroll()、stream() (Hibernate 5.2+)。query.setFirstResult(offset).setMaxResults(pageSize),底層轉換為數據庫分頁語法。select s.name, c.name from ...,減少數據傳輸與實體構建開銷。query.setReadOnly(true) 避免快照創建,提升性能。@Cacheable、<cache usage="read-write"/> 緩存頻讀少寫實體。HQL 的核心優勢在於面向對象:操作實體與屬性而非表與列。掌握 list() vs iterate()、導航查詢、連接查詢、命名查詢 五大模式,並堅守「投影、分頁、只讀、緩存」四大優化原則,即可編寫高性能、可維護的數據訪問層代碼。