antlr.NoViableAltException异常求助:Hibernate动态查询WHERE子句问题
兄弟,你这段代码里的问题其实挺典型的——直接把HQL字符串拼接和Hibernate Criteria API混着用了,这就是WHERE子句完全没生效的核心原因!我帮你拆解下具体问题,再给你两个靠谱的修复方案:
核心问题分析
1. 致命错误:混用HQL字符串与Criteria API
你一边在手动拼接HQL的query字符串,一边往criteria集合里加Criteria查询条件,最后还把criteria用String.join(" OR ", criteria)拼到HQL里——这完全是错误的用法!
Criteria API是Hibernate提供的独立于HQL的面向对象查询方式,你不能把Criteria对象直接当成HQL片段拼接,这么做只会让你的WHERE子句里塞满Criteria对象的toString()内容(类似org.hibernate.criterion.SimpleExpression@xxxx),根本不是数据库能解析的有效条件。
2. HQL拼接的语法漏洞
- 你原HQL里
Select字段的结尾和from之间没有空格!拼接后会变成workplaceNamefrom Employee emp,这本身就是语法错误,数据库直接解析失败。 - 如果
checkAllNotEmpty(data)为true,但所有参数都为空(比如用户没输入任何查询条件),你会拼接出WHERE ORDER BY的错误语法,同样会导致查询失败。
3. 参数绑定与条件脱节
你后面用q.setParameter()绑定了一堆参数,但这些参数对应的占位符(比如:id、:deptID)根本没出现在你拼接的HQL里——因为你根本没把正确的HQL条件片段加进去,参数绑定自然也完全无效。
修复方案(二选一即可)
方案一:纯HQL字符串拼接(适合熟悉SQL语法的场景)
用StringBuilder拼接HQL,手动添加条件片段,确保占位符和参数绑定对应:
try{ // 注意开头Select字段后加空格,避免语法错误 StringBuilder query = new StringBuilder("Select emp.employeeID,emp.firstName,emp.middleName,emp.lastName,pos.positionName,dept.deptName,work.workplaceName "); query.append("from Employee emp "); query.append("INNER JOIN Department dept ON emp.departmentID = dept.deptID "); query.append("INNER JOIN Position pos ON emp.positionID = pos.positionID "); query.append("INNER JOIN Workplace work ON emp.workplaceID = work.workplaceID "); List<String> hqlConditions = new ArrayList<>(); if(checkAllNotEmpty(data)) { // 员工ID条件 if(data.getEmployeeID() != null && !data.getEmployeeID().equals("")) { hqlConditions.add("emp.employeeID like :id"); input_empID = true; } // 员工姓名条件(根据你的搜索逻辑拼接) if(data.getEmployeeName()!= null && !data.getEmployeeName().equals("")){ if(searchbyOne){ hqlConditions.add("(emp.firstName like :inputName OR emp.lastName like :inputName)"); } else if(searchbyFandL){ hqlConditions.add("(emp.firstName like :firstLastName AND emp.lastName like :firstLastName)"); } else if(searchbyCompName){ hqlConditions.add("concat(emp.firstName, ' ', emp.lastName) like :completeName"); } input_empName = true; } // 部门条件 if(data.getDepartmentID()!=0) { hqlConditions.add("emp.departmentID = :deptID"); selected_dept = true; } // 职位条件 if(data.getPositionID()!=0) { hqlConditions.add("emp.positionID = :posID"); selected_pos = true; } // 工作地点条件 if(data.getWorkplaceID()!=0) { hqlConditions.add("emp.workplaceID = :workID"); selected_work = true; } // 只有当有条件时才拼接WHERE if(!hqlConditions.isEmpty()){ query.append("WHERE ").append(String.join(" OR ", hqlConditions)).append(" "); } } query.append("ORDER BY emp.joinDate DESC"); System.out.println("QUERY: " + query); Query q = session.createQuery(query.toString()); // 绑定参数 if(input_empID) { q.setParameter("id", "%" + data.getEmployeeID() + "%"); } if(input_empName) { if(searchbyOne) q.setParameter("inputName", "%" + data.getEmployeeName() + "%"); if(searchbyFandL) q.setParameter("firstLastName", "%" +firstLastName+ "%"); if(searchbyCompName) q.setParameter("completeName", "%" +completeName+ "%"); } if(selected_dept) { q.setParameter("deptID", data.getDepartmentID()); } if(selected_pos) { q.setParameter("posID", data.getPositionID()); } if(selected_work) { q.setParameter("workID", data.getWorkplaceID()); } // 注意这里返回的是Object[]数组,因为你查的是多个字段 employees = (List<Object[]>) q.list(); }catch(Exception e){ e.printStackTrace(); }finally{ session.close(); } return employees;
方案二:纯Criteria API(更安全,避免SQL注入)
用Hibernate的Criteria API构建查询,完全不用手动拼接字符串,能自动处理语法和参数绑定:
try{ CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class); Root<Employee> emp = cq.from(Employee.class); // 关联查询 Join<Employee, Department> dept = emp.join("departmentID", JoinType.INNER); Join<Employee, Position> pos = emp.join("positionID", JoinType.INNER); Join<Employee, Workplace> work = emp.join("workplaceID", JoinType.INNER); // 选择需要返回的字段 cq.select(cb.array( emp.get("employeeID"), emp.get("firstName"), emp.get("middleName"), emp.get("lastName"), pos.get("positionName"), dept.get("deptName"), work.get("workplaceName") )); List<Predicate> predicates = new ArrayList<>(); if(checkAllNotEmpty(data)) { // 员工ID条件 if(data.getEmployeeID() != null && !data.getEmployeeID().equals("")) { predicates.add(cb.like(emp.get("employeeID"), "%" + data.getEmployeeID() + "%")); } // 员工姓名条件 if(data.getEmployeeName()!= null && !data.getEmployeeName().equals("")){ String namePattern = "%" + data.getEmployeeName() + "%"; if(searchbyOne){ predicates.add(cb.or( cb.like(emp.get("firstName"), namePattern), cb.like(emp.get("lastName"), namePattern) )); } else if(searchbyFandL){ predicates.add(cb.and( cb.like(emp.get("firstName"), namePattern), cb.like(emp.get("lastName"), namePattern) )); } else if(searchbyCompName){ predicates.add(cb.like( cb.concat(cb.concat(emp.get("firstName"), " "), emp.get("lastName")), "%" + completeName + "%" )); } } // 部门/职位/工作地点条件 if(data.getDepartmentID()!=0) { predicates.add(cb.equal(emp.get("departmentID"), data.getDepartmentID())); } if(data.getPositionID()!=0) { predicates.add(cb.equal(emp.get("positionID"), data.getPositionID())); } if(data.getWorkplaceID()!=0) { predicates.add(cb.equal(emp.get("workplaceID"), data.getWorkplaceID())); } } // 添加WHERE条件(OR连接所有谓词) if(!predicates.isEmpty()){ cq.where(cb.or(predicates.toArray(new Predicate[0]))); } // 排序 cq.orderBy(cb.desc(emp.get("joinDate"))); Query<Object[]> q = session.createQuery(cq); employees = q.getResultList(); }catch(Exception e){ e.printStackTrace(); }finally{ session.close(); } return employees;
最后提醒
- 千万不要混用HQL和Criteria API,选一种方式坚持用就好
- 用字符串拼接HQL时一定要注意空格,避免低级语法错误
- 优先用Criteria API,它能帮你自动处理语法、避免SQL注入,还能适配不同数据库的语法差异
内容的提问来源于stack exchange,提问作者Snow
相关产品推荐
相关产品推荐

