Hibernate的6种查询方式对比
Hibernate 有 6 种查询方式:HQL(面向对象的查询语言,类似 SQL)、SQL(原生 SQL)、命名查询(XML 配置 SQL/HQL)、Criteria(完全面向对象)、DetachedCriteria(分离查询条件)、Example(按实体示例查询)。推荐 HQL 日常查询、Criteria 动态条件、SQL 复杂场景。Example 代码复杂不推荐。
上一篇把 Hibernate 的入门配置走通了,这篇接着聊它的查询。
Hibernate 查数据的方式不止一种,前前后后有六种玩法。刚接触的时候容易懵:这么多种到底该用哪个?
我按平时的使用习惯和适用面给它们排了个序,每种配个最简实例,顺便说说各自的优缺点。看完你心里就有谱了。
六种查询,先排个序
按我自己的使用频率,大致是这个顺序:
- HQL 查询
- SQL 查询
- 命名查询
- 对象化查询(Criteria)
- 动态查询(DetachedCriteria)
- 例子查询(不推荐)
下面一个个过,代码都用上一篇那个 User 实体。
HQL 查询
HQL 是 Hibernate 自己的查询语言,长得像 SQL,但有个关键区别:它操作的是对象和属性,不是表和字段。
static void query(String name) {
Session s = null;
try {
s = HibernateUtil.getSession();
// from 后面跟的是对象 User,不是表名
// 冒号开头的是命名参数,可读性比 ? 好,推荐
String hql = "from User as user where user.username = :name";
Query query = s.createQuery(hql);
query.setString("name", name); // 给命名参数赋值
List<User> list = query.list();
for (User user : list) {
// 遍历结果
}
} finally {
if (s != null) s.close();
}
}
- 优点:最常用、最传统,结构类似 JDBC 容易理解,而且因为写的是对象,天然跨数据库。
- 缺点:是一门全新的查询语言,只在 Hibernate 里管用,换框架就白学。
日常八成查询用它就对了。
SQL 查询
就是直接写原生 SQL,没啥好绕的。
static void query(String name) {
Session s = null;
try {
s = HibernateUtil.getSession();
// createSQLQuery 走原生 SQL,addEntity 指定结果封装成哪个实体
// 注意:这里为演示用了拼接,真实代码要用参数绑定防注入
Query query = s.createSQLQuery("select * from user where name = :name")
.addEntity(User.class);
query.setString("name", name);
List<User> list = query.list();
} finally {
if (s != null) s.close();
}
}
- 优点:万能,不想学 HQL 的直接写 SQL,复杂查询(多表、特殊函数)也能硬上。
- 缺点:破坏了跨数据库能力,也不面向对象,等于绕开了 Hibernate 的大半好处。
多说一句安全:原底稿里是把变量直接拼进 SQL 字符串的,那样有注入风险。正确做法是用 :name 这种命名参数绑定,别字符串拼接。
命名查询
把查询语句写到配置文件里,代码里按名字调用。思路有点像 MyBatis 那种"SQL 和代码分离"。
代码这边:
static void query(String name) {
Session s = null;
try {
s = HibernateUtil.getSession();
// 按名字取出配置里定义好的查询
Query query = s.getNamedQuery("getUserByName");
query.setString("name", name);
List<User> list = query.list();
} finally {
if (s != null) s.close();
}
}
配置文件里定义,HQL 和 SQL 都支持:
<!-- 命名查询,写 HQL -->
<query name="getUserByName">
<![CDATA[from User where username = :name]]>
</query>
<!-- 命名查询,写原生 SQL -->
<sql-query name="getUserByNameSql">
<return alias="user" class="com.mebugs.daoBean.User"></return>
<![CDATA[select * from user where name = :name]]>
</sql-query>
- 优点:SQL 统一管理,集中维护,改语句不用动 Java 代码。
- 缺点:本质还是 HQL/SQL,不面向对象;语句散在配置里,调试时得两头翻。
对象化查询(Criteria)
这个就纯面向对象了,不用写任何查询语句字符串,全靠 API 拼条件。
static void query(String name) {
Session s = null;
try {
s = HibernateUtil.getSession();
Criteria c = s.createCriteria(User.class);
// Restrictions 提供各种条件:eq 等于、gt 大于、lt 小于、or 或 ...
c.add(Restrictions.eq("username", name));
List<User> list = c.list();
} finally {
if (s != null) s.close();
}
}
- 优点:完全面向对象,条件用 API 堆,可读性好,特别适合"条件个数不固定"的动态查询。
- 缺点:适用面不如 HQL 广,碰到复杂的连表、子查询会比较别扭。
提一句:Criteria 在 Hibernate 新版本(5.2 之后)已经被标记废弃,官方推荐用 JPA 的 CriteriaQuery 替代。所以新项目别再用这套老 Criteria 了,了解它的思路就行。
动态查询(DetachedCriteria)
它是 Criteria 的进阶版,把"拼条件"和"执行查询"拆开了。
条件可以在业务层先拼好,再丢给 DAO 层执行,DAO 层不用关心具体查什么字段。
// DAO 层:只负责执行,不关心条件细节
static List<User> query(DetachedCriteria dc) {
Session s = null;
try {
s = HibernateUtil.getSession();
// DetachedCriteria 绑定到当前 session 才能执行
Criteria c = dc.getExecutableCriteria(s);
return c.list();
} finally {
if (s != null) s.close();
}
}
// 业务层:在这里组装条件
DetachedCriteria dc = DetachedCriteria.forClass(User.class);
dc.add(Restrictions.eq("username", "mebugs"));
List<User> list = query(dc);
- 优点:面向对象,还把业务层和持久层解耦了,DAO 层不用为每种条件写一个方法。
- 缺点:和 Criteria 一样,适用面有限,同样面临被废弃的问题。
例子查询(不推荐)
最后这个,拿一个"样例对象"去查:把对象里非空的属性当成查询条件。
static void query(User example) {
Session s = null;
try {
s = HibernateUtil.getSession();
// Example.create 把 example 对象里非空字段拼成查询条件
List<User> list = s.createCriteria(User.class)
.add(Example.create(example))
.list();
} finally {
if (s != null) s.close();
}
}
从代码复杂度和可控性来看,我觉得它不值得推荐,了解一下就够了。
空值怎么处理、模糊匹配怎么配,这些细节绕来绕去,还不如直接写 Criteria 或 HQL 来得清楚。
怎么选
六种看着多,实际日常就记三个主力:
- 普通查询:用 HQL,最通用,面向对象又好读。
- 条件不固定的动态查询:用 Criteria(老项目),新项目换成 JPA CriteriaQuery。
- HQL 和 Criteria 都搞不定的复杂场景:退回 原生 SQL,记得参数绑定。
命名查询看团队习惯,想集中管理语句可以用;例子查询基本可以忽略。
小结
Hibernate 的六种查询,核心矛盾是"面向对象的方便"和"原生 SQL 的灵活"之间的权衡。
HQL 是中间最平衡的选择,日常首选;Criteria 系列胜在动态拼条件,但已经在被淘汰;原生 SQL 是复杂场景的兜底。
要提醒的是,这套 API 是 Hibernate 老版本的玩法,新项目大多走 JPA 规范(Spring Data JPA)了,Criteria、Example 这些能不碰就不碰。
但搞懂这六种的差异,对理解 ORM 查询"到底能怎么查、各自代价是什么"很有价值。知道有哪些牌,才知道该出哪张。
