Hibernate 查詢默認返回實體對象或 Object[],但實際開發常需只選部分字段、多表連接、聚合統計等場景,需靈活控制結果結構。本文系統講解 HQL 中的 `new List`、`new Map`、構造函數注入、`ResultTransformer` 等高級映射技巧。
mysql> SELECT * FROM p_dictionary; +-----------------+----+---------+------+--------+--------+ | dictionary_type | id | version | code | forbid | value | +-----------------+----+---------+------+--------+--------+ | COUNTY | 1 | 0 | 001 | | 四川 | | COUNTY | 2 | 0 | 002 | | 北京 | | COUNTY | 3 | 0 | 001 | NULL | 四川 | +-----------------+----+---------+------+--------+--------+
String hql = "SELECT NEW LIST(p.code, p.value) FROM Dictionary p"; List> list = session.createQuery(hql).list(); // 結果: [[001, 四川], [002, 北京], [001, 四川]]
String hql = "SELECT NEW MAP(p.code, p.value) FROM Dictionary p"; List
String hql = "SELECT NEW MAP(p.code AS code, p.value AS value) FROM Dictionary p"; List
String hql = "SELECT NEW MAP(p.code AS code, p.value) FROM Dictionary p";
// 結果: [{code=001, 1=四川}, {code=002, 1=北京}, {code=001, 1=四川}]
// DTO 需提供對應構造函數
public class CountyDTO {
private String code, value;
public CountyDTO(String code, String value) { this.code = code; this.value = value; }
// getter/setter
}
String hql = "SELECT NEW com.example.CountyDTO(p.code, p.value) FROM Dictionary p";
List list = session.createQuery(hql).list();
// 直接獲得類型安全的 DTO 列表,無需手動映射
優勢:編譯期檢查、類型安全、代碼可讀性高。要求:DTO 必須有全參構造函數,參數順序、類型與 SELECT 列嚴格對應。
Query q = session.createQuery("SELECT p.code AS code, p.value AS value FROM Dictionary p");
q.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
List
// 適用於原生 SQL 查詢,多表連接結果直接映射到包含多表字段的 DTO
String sql = "SELECT u.username AS userName, p.title, p.add_time AS addTime " +
"FROM user u JOIN post p ON u.id = p.user_id";
Query q = session.createSQLQuery(sql)
.setResultTransformer(Transformers.aliasToBean(PostVO.class));
// PostVO 需包含 userName, title, addTime 屬性及無參構造函數
List list = q.list();
原理:Hibernate 根據 SQL 結果集列名(別名)與 DTO 屬性名匹配,利用反射賦值。關鍵:SQL 別名必須與 DTO 屬性名完全一致(駝峰對應)。
public class VOResultTransformer implements ResultTransformer {
private final Class> targetClass;
public VOResultTransformer(Class> clazz) { this.targetClass = clazz; }
@Override public Object transformTuple(Object[] tuple, String[] aliases) {
try {
Object obj = targetClass.getDeclaredConstructor().newInstance();
for (int i = 0; i < aliases.length; i++) {
Field f = targetClass.getDeclaredField(aliases[i]);
f.setAccessible(true);
f.set(obj, tuple[i]);
}
return obj;
} catch (Exception e) { throw new RuntimeException(e); }
}
@Override public List transformList(List collection) { return collection; }
}
// 使用
q.setResultTransformer(new VOResultTransformer(PostVO.class));
list = session.createSQLQuery("SELECT code, value FROM p_dictionary")
.addScalar("code", StandardBasicTypes.STRING)
.addScalar("value", StandardBasicTypes.STRING)
.list();
掌握 Hibernate 多樣化結果映射,能讓數據訪問層代碼既簡潔又高效,完美適配各種業務查詢場景。