使用CriteriaBuilder结合Hibernate StatelessSession构建查询时字符串参数占位符未正确替换的问题求助
首先,问题的核心原因在于:scrollQuery.getQueryString()返回的是带有占位符的原始HQL,Hibernate不会自动将绑定的参数值替换到HQL字符串中——你看到Integer参数被替换的情况,大概率是因为这些Integer是常量值(比如枚举的getValue()结果、固定数值),Hibernate在生成HQL时直接将常量嵌入,而String类型的变量参数则会保留占位符(:paramN)。
StatelessSession虽然要求createQuery传入String类型的HQL,但它同样支持参数绑定,不需要手动替换HQL中的占位符(反而手动替换会带来SQL注入风险)。下面是两种可靠的解决方案:
方案一:从原Query提取参数并绑定到StatelessSession的Query
直接从你已经构建好的scrollQuery中提取所有参数的名称和值,然后逐个绑定到StatelessSession创建的Query上:
StatelessSession session = ((Session) EntityManager.getDelegate()).getSessionFactory().openStatelessSession(); // ... 你的Criteria构建代码保持不变 ... TypedQuery<Concepts> query = EntityManager.createQuery(queryConcepts); org.hibernate.Query scrollQuery = query.unwrap(org.hibernate.Query.class); // 创建StatelessSession的Query org.hibernate.Query statelessQuery = session.createQuery(scrollQuery.getQueryString()) .setReadOnly(true) .setFetchSize(maxCachedRecords) .setCacheable(false); // 提取并绑定所有参数 ParameterMetadata paramMetadata = scrollQuery.getParameterMetadata(); for (Parameter<?> param : paramMetadata.getParameters()) { if (param.getName() != null) { // 绑定命名参数 statelessQuery.setParameter(param.getName(), scrollQuery.getParameterValue(param)); } else { // 绑定位置参数(如果有的话) statelessQuery.setParameter(param.getPosition(), scrollQuery.getParameterValue(param)); } } // 执行scroll ScrollableResults scrollconcepts = statelessQuery.scroll(ScrollMode.FORWARD_ONLY);
这种方式完全复用了你原本绑定的参数,既避免了手动拼接HQL的风险,又能保证所有类型的参数(String、Integer等)都能正确传递。
方案二:直接使用StatelessSession的API构建查询(可选)
如果你愿意重构部分代码,可以直接用StatelessSession的HQL字符串来构建查询,同时手动绑定参数:
StatelessSession session = ((Session) EntityManager.getDelegate()).getSessionFactory().openStatelessSession(); String hql = "SELECT a FROM Concepts a, ExportTemp b " + "WHERE a.xptoName = b.xptoName " + "AND b.keyName = :key " + "AND b.id = :exportId"; ScrollableResults scrollconcepts = session.createQuery(hql) .setReadOnly(true) .setFetchSize(maxCachedRecords) .setCacheable(false) .setParameter("key", ExportTmpType.CONCEPTS.getValue()) .setParameter("exportId", tecAccountingExport.getId()) .scroll(ScrollMode.FORWARD_ONLY);
这种方式更直接,但需要你手动编写HQL,适合逻辑不太复杂的查询场景。
关键提醒
永远不要手动将参数值拼接到HQL字符串中(比如用字符串替换:param1),这会导致SQL注入漏洞。参数绑定是Hibernate提供的安全且可靠的参数传递方式,无论使用Stateful还是StatelessSession都应该优先使用。
内容的提问来源于stack exchange,提问作者Hugo Marinho

