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

JPA原生查询遭Snyk报SQL注入,PreparedStatement返回数据异常求助

JPA原生查询改参数化查询后无数据返回的解决办法

问题背景

Snyk检测到原有JPA原生查询存在SQL注入风险,改用EntityManager创建带参数的NativeQuery实现后,查询无数据返回,但原@Query注解的查询可以正常返回结果。

原JPA查询代码:

@Query(value = "SELECT dsm.*, rc.order FROM StoreInfo.response_configuration rc INNER JOIN StoreInfo.data_source_mapping dsm ON rc.data_source_mapping_id = dsm.id WHERE rc.request_source_id = (SELECT ID FROM StoreInfo.request_source where name = :source and active = true)", nativeQuery = true)
public List<DataSourceMappingEntity> getMappings(@Param("source") String source);

参数化实现代码:

private List<DataSourceMappingEntity> getMappingsUsingPreparedStatement(String source) {
    String query = "SELECT dsm.*, rc.order " +
            "FROM StoreInfo.response_configuration rc " +
            "INNER JOIN StoreInfo.data_source_mapping dsm ON rc.data_source_mapping_id = dsm.id " +
            "WHERE rc.request_source_id = (" +
            "    SELECT ID FROM StoreInfo.request_source WHERE name = ? AND active = true" +
            ")";
    Query sqlQuery = entityManager.createNativeQuery(query, DataSourceMappingEntity.class);
    List results = sqlQuery.setParameter(1, source).getResultList();
    
    return results;
}

排查与解决方案

1. 检查SQL关键字转义

order是SQL保留关键字,部分数据库(如MySQL、PostgreSQL)需要对其转义才能正确识别。原@Query注解可能由JPA自动处理了转义,但手动编写的NativeQuery需要显式处理:

  • MySQL:用反引号包裹关键字:rc.order``
  • PostgreSQL/Oracle:用双引号包裹关键字:rc."order"

修改后的查询示例(MySQL):

String query = "SELECT dsm.*, rc.`order` " +
        "FROM StoreInfo.response_configuration rc " +
        "INNER JOIN StoreInfo.data_source_mapping dsm ON rc.data_source_mapping_id = dsm.id " +
        "WHERE rc.request_source_id = (" +
        "    SELECT ID FROM StoreInfo.request_source WHERE name = ? AND active = true" +
        ")";

2. 验证实体映射匹配

createNativeQuery(query, DataSourceMappingEntity.class)要求查询返回的列与实体字段完全匹配(包括列名、数据类型)。原查询返回了dsm.*和rc.order,但如果DataSourceMappingEntity中没有对应rc.order的字段,或者字段关联的表不正确,会导致映射失败,返回空列表。

解决方式:

  • 只查询实体对应表的字段:如果不需要rc.order,直接修改查询为SELECT dsm.*
  • 添加对应字段到实体:如果需要rc.order,在DataSourceMappingEntity中添加字段并指定关联表:
    @Column(name = "order", table = "response_configuration")
    private Integer order;
    
  • 手动映射结果:先返回Object[]数组,再手动转换为实体,确认数据是否存在:
    List<Object[]> rawResults = sqlQuery.setParameter(1, source).getResultList();
    List<DataSourceMappingEntity> mappings = new ArrayList<>();
    for (Object[] row : rawResults) {
        DataSourceMappingEntity entity = new DataSourceMappingEntity();
        // 根据实际列顺序设置字段,例如:
        entity.setId((Long) row[0]);
        entity.setSomeField(row[1].toString());
        entity.setOrder((Integer) row[...]); // 对应rc.order的列索引
        mappings.add(entity);
    }
    return mappings;
    

3. 对比执行的SQL语句

启用JPA的SQL日志,查看参数化查询实际执行的SQL与原@Query执行的SQL是否一致,确认参数是否正确绑定:
在application.properties中添加配置:

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

通过日志对比:

  • 参数是否被正确替换
  • 表名、字段名是否有拼写错误
  • 子查询逻辑是否一致

4. 确认参数传递正确性

打印传入的source参数值,验证是否与原查询调用时的参数一致,避免因参数值差异导致无数据返回。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 23:44:53