HQL (Hibernate Query Language) 是面向物件的查詢語言,操作的是實體與屬性而非資料表欄位。
| 子句 | 範例 | 說明 |
|---|---|---|
| FROM | from Post / from Post p | 查詢實體,可起別名 |
| SELECT | select p.Title, p.Author from Post p | 投影查詢,回傳陣列或 Tuple |
| 集合查詢 | select elements(blog.Posts) from Blog blog | elements() 展開集合 |
| 聚合函數 | select count(*) from Blog / select max(p.Price) from Product p | 支援 count, sum, avg, min, max, count(distinct ...) |
| WHERE | from Blog b where b.Author = 'Terry' | 支援參數綁定 ? 或命名參數 :name |
適用於回傳實體集合或標量陣列,支援位置參數 ?:
public static Post[] GetPostsByCategory(string category) {
var query = new SimpleQuery(typeof(Post),
"from Post post where post.Category = ?", category);
return (Post[])ActiveRecordBase.ExecuteQuery(query);
}
public static int[] GetPostIdsByDateRange(DateTime start, DateTime end) {
var query = new SimpleQuery(typeof(Post), typeof(int),
"select post.Id from Post post where post.Created between ? and ?", start, end);
return (int[])ActiveRecordBase.ExecuteQuery(query);
}
關鍵點:第二個建構子參數 typeof(int) 指定回傳標量型別,否則預設回傳實體型別。
專為 count、sum 等聚合查詢設計,直接回傳單一值:
public static int GetPostCountByAuthor(string author) {
var query = new ScalarQuery(typeof(Blog),
"select count(elements(blog.Posts)) from Blog blog where blog.Author = ?", author);
return (int)ActiveRecordBase.ExecuteQuery(query);
}
繼承 ActiveRecordBaseQuery 覆寫 Execute(ISession session),適合複雜參數邏輯、分頁、動態 HQL:
public class BlogQuery : ActiveRecordBaseQuery {
public string AuthorName { get; set; }
public int MaxResults { get; set; } = 10;
public BlogQuery() : base(typeof(Blog)) { }
public override object Execute(ISession session) {
var hql = "from Blog blog";
if (!string.IsNullOrEmpty(AuthorName)) hql += " where blog.Author = :author";
var q = session.CreateQuery(hql);
if (!string.IsNullOrEmpty(AuthorName)) q.SetString("author", AuthorName);
q.SetMaxResults(MaxResults);
return base.GetResultsArray(typeof(Blog), q.List(), null, false);
}
}
// 使用
public static Blog[] GetBlogsByAuthor(string author, int max = 10) {
var q = new BlogQuery { AuthorName = author, MaxResults = max };
return (Blog[])ActiveRecordBase.ExecuteQuery(q);
}
無需額外類別,直接傳遞委派處理 ISession:
public static Blog[] GetBlogsByAuthorCallback(string author) {
return (Blog[])ActiveRecordBase.Execute(typeof(Blog),
new NHibernateDelegate(GetBlogsCallback), author);
}
private static object GetBlogsCallback(ISession session, object instance) {
var author = (string)instance;
var query = session.CreateQuery("from Blog blog where blog.Author = :author");
query.SetString("author", author);
var results = query.List();
var blogs = new Blog[results.Count];
results.CopyTo(blogs, 0);
return blogs;
}
| 場景 | 推薦方式 | 理由 |
|---|---|---|
| 簡單實體集合查詢 | SimpleQuery | 語法簡潔,自動映射實體 |
| 聚合/單一值查詢 | ScalarQuery | 專為標量優化,回傳型別明確 |
| 動態 HQL、分頁、複雜參數 | 自定義查詢類別 | 可複用、可測試、支援完整 Session API |
| 一次性邏輯、不想增加類別 | Callback 委派 | 輕量、內聯,適合簡單臨時查詢 |
?:SimpleQuery 預設,順序對應,簡單但易因順序錯誤導致 Bug。:name:自定義查詢/Callback 中使用 q.SetString("name", val),可讀性高、順序無關、支援重複參數。ActiveRecord 提供四種 HQL 查詢進階方式,從簡單到複雜層層遞進。掌握各自適用場景,配合命名參數與自定義查詢類別,能優雅解決 95% 以上的複雜查詢需求,同時保持程式碼可測試與可維護。