Quarkus Hibernate查询翻译器Bug:HQL与SQL逻辑不等价
Hibernate QueryTranslatorImpl生成SQL丢失分组括号导致逻辑不等价问题
在Quarkus应用调试中发现,Hibernate的QueryTranslatorImpl将HQL转换为SQL时,丢失了原HQL中的内部分组括号,导致SQL执行逻辑与HQL预期不一致。原HQL通过括号明确了每组(geo_feature_class = :classX AND geo_feature_code = :codeX)的逻辑关系,再将这些分组用OR连接,但生成的SQL去掉了内部分组括号。由于SQL中AND的优先级高于OR,最终查询逻辑会偏离预期。
原HQL查询:
from com.xxxxx.geo.GeoName where geo_country_code = :cc and geo_admin1_code = :admin1 and ((geo_feature_class = :class0 and geo_feature_code = :code0) or (geo_feature_class = :class1 and geo_feature_code = :code1) or (geo_feature_class = :class2 and geo_feature_code = :code2)) order by name
生成的SQL查询:
select geoname0_.geo_id as geo_id1_3_, geoname0_.geo_admin1_code as geo_admi2_3_, geoname0_.geo_admin2_code as geo_admi3_3_, geoname0_.geo_admin3_code as geo_admi4_3_, geoname0_.geo_admin4_code as geo_admi5_3_, geoname0_.geo_admin5_code as geo_admi6_3_, geoname0_.geo_alt_country_codes as geo_alt_7_3_, geoname0_.geo_alt_names as geo_alt_8_3_, geoname0_.geo_country_code as geo_coun9_3_, geoname0_.geo_dem as geo_dem10_3_, geoname0_.geo_elevation as geo_ele11_3_, geoname0_.geo_feature_class as geo_fea12_3_, geoname0_.geo_feature_code as geo_fea13_3_, geoname0_.geo_last_modified as geo_las14_3_, geoname0_.geo_latitude as geo_lat15_3_, geoname0_.geo_longitude as geo_lon16_3_, geoname0_.geo_name as geo_nam17_3_, geoname0_.geo_name_ascii as geo_nam18_3_, geoname0_.geo_population as geo_pop19_3_, geoname0_.geo_timezone as geo_tim20_3_ from geo.GeoNames geoname0_ where geo_country_code=? and geo_admin1_code=? and (geo_feature_class=? and geo_feature_code=? or geo_feature_class=? and geo_feature_code=? or geo_feature_class=? and geo_feature_code=?) order by geoname0_.geo_name
解决方法:
改用JPA Criteria API构建查询:
避免手动拼接HQL字符串,使用Criteria API可以让Hibernate正确解析逻辑分组,生成符合预期的SQL:CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<GeoName> query = cb.createQuery(GeoName.class); Root<GeoName> root = query.from(GeoName.class); // 基础过滤条件 Predicate baseFilter = cb.and( cb.equal(root.get("geo_country_code"), cc), cb.equal(root.get("geo_admin1_code"), admin1) ); // 定义每组(class+code)的AND条件 Predicate group0 = cb.and( cb.equal(root.get("geo_feature_class"), class0), cb.equal(root.get("geo_feature_code"), code0) ); Predicate group1 = cb.and( cb.equal(root.get("geo_feature_class"), class1), cb.equal(root.get("geo_feature_code"), code1) ); Predicate group2 = cb.and( cb.equal(root.get("geo_feature_class"), class2), cb.equal(root.get("geo_feature_code"), code2) ); // 组合OR条件 Predicate orFilter = cb.or(group0, group1, group2); // 组装最终查询并执行 query.where(cb.and(baseFilter, orFilter)) .orderBy(cb.asc(root.get("name"))); List<GeoName> results = entityManager.createQuery(query).getResultList();临时添加额外括号(Workaround):
如果必须使用字符串HQL,可以尝试在每组条件外层再嵌套一层括号,强制Hibernate保留分组结构:from com.xxxxx.geo.GeoName where geo_country_code = :cc and geo_admin1_code = :admin1 and (((geo_feature_class = :class0 and geo_feature_code = :code0)) or ((geo_feature_class = :class1 and geo_feature_code = :code1)) or ((geo_feature_class = :class2 and geo_feature_code = :code2))) order by name升级Hibernate/Quarkus版本:
该问题可能是特定版本的HQL解析器bug,建议升级到最新稳定版的Quarkus(及对应的Hibernate ORM依赖),确认是否已修复该解析问题。
内容的提问来源于stack exchange,提问作者R Soph
相关产品推荐
相关产品推荐

