直接拼接SQL字符串会引发SQL注入且无法使用JPA参数绑定;@Query安全传参须二选一:索引参数(?1、?2)或具名参数(:paramName + @Param);nativeQuery=true时字段名须与实体属性名完全一致,推荐显式别名;@Modifying操作必须配@Transactional,否则抛TransactionRequiredException。

为什么不能直接拼接SQL字符串
直接用 StringBuilder 拼接用户输入(比如 "where user_id = " + userId)会引发 SQL 注入,且无法利用 JPA 的参数绑定机制。JPA 的 @Query 和 EntityManager 都支持占位符参数,但前提是**不手动拼接**。
@Query 中如何安全传参
两种安全方式必须二选一,不能混用:
- 索引参数:用
?1、?2对应方法参数顺序,适用于简单场景 - 具名参数:用
:paramName+@Param("paramName"),语义清晰、可复用、支持同名多次引用
错误示例:@Query("select * from user where name = '" + name + "'") —— 这是硬编码拼接,绝对禁止。
正确写法(具名参数):@Query("select u from User u where u.status = :status and u.createdAt > :since"),配合 @Param("status") Integer status 和 @Param("since") Date since。
nativeQuery = true 时的字段映射陷阱
启用 nativeQuery = true 后,SQL 走的是数据库原生协议,JPA 不再做对象映射转换,而是按列名或下标匹配结果。容易踩坑的地方有:
- 返回实体类时,查询字段名必须与实体属性名**完全一致**(区分大小写),否则字段为 null
- 推荐显式指定别名:
SELECT id AS id, user_name AS userName FROM user,避免依赖数据库字段命名风格 - 若只查部分字段,不要返回完整实体,改用
@Query(..., nativeQuery = true)+ 自定义 DTO 或Object[],再手动封装
例如:@Query(value = "SELECT id, user_name FROM user WHERE dept_id = ?1", nativeQuery = true) 返回 List<object></object>,而非 List<user></user>。
@Modifying 更新/删除必须配 @Transactional
任何带 @Modifying 的 @Query(UPDATE / DELETE / INSERT)都会抛出 TransactionRequiredException,除非满足以下任一条件:
- 方法所在类被
@Transactional注解修饰(推荐加在 service 层) - 该方法自己加了
@Transactional(注意传播行为,默认REQUIRED)
漏掉事务注解的典型错误信息:Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException。
额外提醒:@Modifying 方法默认不清除一级缓存,如需同步更新内存中实体状态,可加 clearAutomatically = true 参数。
真正难的不是写出能跑的 SQL,而是让参数不逃逸、字段不错位、事务不遗漏——这三处没处理好,再“正确”的 SQL 也会在生产环境突然失效。

















