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
相关产品推荐
相关产品推荐

