You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL查询在Java中报unexpected token: ON语法错误排查

PostgreSQL + Hibernate createSQLQuery 报错 unexpected token: ON

问题场景

有一条PostgreSQL查询语句,用distinct on获取table1.profile_id并按table3.creation_date排序,在DBeaver客户端能正常执行,但嵌入Java代码通过Hibernate的createSQLQuery执行时,抛出java.sql.SQLSyntaxErrorException: unexpected token: ON错误。把Java生成的SQL复制回数据库客户端却又能正常运行。

原始查询语句

select profileId from (
    select distinct on (p.profile_id) p.profile_id as profileId, pr.creation_date as creationDate
    from table1 p
             
             join table2 o on (p.profile_id = o.offeree_profile_id) 
             join table3 pr on (o.offer_id = pr.offer_id ) 
    where 
     
             o.offer_status_id = 'ACCEPTED' 
             
    and (pr.status != 'TERMINATED') 
             and o.offeror_profile_id in 
    
    (select p.profile_id from table4 u 
    join table1 p on (p.user_id = u.user_id) 
    where customer_id = 'A'
    AND o.offeror_profile_id != o.offeree_profile_id)
    ) sub
order by creationDate desc limit 6

Java代码实现

String queryString = "select profileId from (" +
    "select distinct on (p.profile_id) p.profile_id as profileId, pr.creation_date as creationDate " +
    "from table1 p " +
    "join table2  o on (p.profile_id = o.offeree_profile_id ) " +
    "join table3 pr on (o.offer_id = pr.offer_id ) " +
    "where " +
    "o.offer_status_id = 'ACCEPTED' " +
    "and (pr.status != 'TERMINATED') " +
    "and o.offeror_profile_id in " +
    "(select p.profile_id from table4 u " +
    "join table1 p on (p.user_id = u.user_id) " +
    "where customer_id = :CustomerId " +
    "and o.offeror_profile_id != o.offeree_profile_id)" +
    ") sub " +
    "order by creationDate desc limit 6";


final Query profileIdQuery = getCurrentSession().createSQLQuery(queryString).setString("CustomerId", CustomerId);

List<BigInteger> profileIdList = profileIdQuery.list();

抛出错误信息

Caused by: java.sql.SQLSyntaxErrorException: unexpected token: ON

解决办法

1. 确保Hibernate使用PostgreSQL方言

检查Hibernate配置文件,确认hibernate.dialect设置为对应版本的PostgreSQL方言:

# 示例:PostgreSQL 10+ 适用
hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect

若方言设置错误,Hibernate会使用通用SQL解析器,无法识别PostgreSQL特有的distinct on语法。

2. 替换distinct on为窗口函数写法

改用row_number()窗口函数实现相同逻辑,避免Hibernate对distinct on的解析问题,改写后的SQL如下:

select profileId from (
    select p.profile_id as profileId, pr.creation_date as creationDate,
           row_number() over (partition by p.profile_id order by pr.creation_date) as rn
    from table1 p
    join table2 o on (p.profile_id = o.offeree_profile_id) 
    join table3 pr on (o.offer_id = pr.offer_id ) 
    where 
        o.offer_status_id = 'ACCEPTED' 
        and (pr.status != 'TERMINATED') 
        and o.offeror_profile_id in 
        (select p.profile_id from table4 u 
         join table1 p on (p.user_id = u.user_id) 
         where customer_id = :CustomerId
         AND o.offeror_profile_id != o.offeree_profile_id)
) sub
where rn = 1
order by creationDate desc limit 6

该写法与distinct on效果一致:按p.profile_id分区,每个分区取第一条数据,且Hibernate对窗口函数的兼容性更好,不会触发语法解析错误。

3. 绕过Hibernate解析,直接用JDBC执行

若上述方法仍无效,可直接获取JDBC连接执行原生SQL:

String sql = "你的改写后SQL";
PreparedStatement stmt = getCurrentSession().doReturningWork(connection -> 
    connection.prepareStatement(sql)
);
stmt.setString("CustomerId", CustomerId);
ResultSet rs = stmt.executeQuery();
List<BigInteger> profileIdList = new ArrayList<>();
while(rs.next()) {
    profileIdList.add(rs.getBigDecimal("profileId").toBigInteger());
}

内容的提问来源于stack exchange,提问作者blueSky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:31:03