Hibernate 標準 HQL/Criteria 雖物件化但複雜查詢受限;MyBatis XML 映射極致靈活卻失去 ORM 便利。本文介紹一種混合方案:以 FreeMarker 模板引擎解析 XML 定義的 SQL/HQL,緩存後由 Hibernate 執行,實現「註解/XML 雙模式、動態 SQL、集中管理」。
XML (User.query.xml)
→ 掃描解析器 (掃描 *.query.xml)
→ FreeMarker 渲染 (參數綁定、條件片段)
→ QueryFactory 緩存 (Key: 實體類 + 查詢名)
→ DBAdapter (Hibernate 原生 Query 執行)
→ DAO 層調用
Query.dtd定義 XML 結構:<query name="...">、<sql>/<hql>、<param>、<if>/<where> 等 FreeMarker 指令。
User.java@Entity @Table(name="t_user")
public class User {
@Id @GeneratedValue Long id;
String username; String email; Integer status;
// getter/setter
}
User.query.xml<queries entity="com.example.User">
<query name="findByCondition">
<hql>
FROM User u
<#if username??>WHERE u.username LIKE :username</#if>
<#if status??>AND u.status = :status</#if>
ORDER BY u.id DESC
</hql>
</query>
</queries>
QueryFactoryclasspath:*.query.xml。<query> 節點,預編譯 FreeMarker 模板。EntityClass#queryName 為 Key 緩存 Template 物件。HibernateSingleDBAdapterpublic class HibernateSingleDBAdapter implements DBAdapter {
private SessionFactory sessionFactory;
private QueryFactory queryFactory;
public List query(String queryName, Map params, Class entityClass) {
Template tpl = queryFactory.getTemplate(entityClass, queryName);
String renderedHql = FreeMarkerTemplateUtils.processTemplateIntoString(tpl, params);
Query query = sessionFactory.getCurrentSession().createQuery(renderedHql);
params.forEach(query::setParameter);
return query.list();
}
}
<bean id="queryFactory" class="com.example.db.QueryFactory">
<property name="files" value="classpath:**/*.query.xml"/>
</bean>
<bean id="dbAdapter" class="com.example.db.HibernateSingleDBAdapter">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="queryFactory" ref="queryFactory"/>
</bean>
public class UserDao extends AbstractDao {
public List findByCondition(String username, Integer status) {
Map params = new HashMap<>();
if (username != null) params.put("username", "%" + username + "%");
if (status != null) params.put("status", status);
return dbAdapter.query("findByCondition", params, User.class);
}
}
| 優點 | 缺點/風險 |
|---|---|
| 動態 SQL 能力接近 MyBatis | 引入 FreeMarker 依賴,啟動解析耗時 |
| SQL/HQL 集中管理,易審計優化 | 模板語法錯誤運行時才發現,需完善測試 |
| 保留 Hibernate 實體映射、緩存、攔截器 | 非標準方案,團隊學習成本、維護風險較高 |
| 支援註解/XML 雙模式共存 | 複雜關聯查詢模板易膨脹,建議配合 View/DTO |
此方案適合「既有 Hibernate 體系、需少量複雜動態 SQL、團隊熟悉 FreeMarker」的遷移過渡期。新專案建議直接選型成熟方案,避免重複造輪子。