JPA查询返回实体中transacionId字段始终为null问题排查
核心原因
是的,transacionId字段始终为null的核心原因就是它不属于Module实体对应的modulos表列。JPA实体的属性默认仅映射对应数据库表的列,当你用List<Module>作为原生查询的返回类型时,JPA只会自动将查询结果中属于modulos表的字段映射到Module实体属性,额外来自其他表的字段(比如这里的C.id_transaccion)不会自动绑定到实体的同名属性上,哪怕实体里定义了该属性。
另外你的查询语句存在语法错误:SELECT子句的顺序颠倒了,正确的写法应该是先写SELECT关键字,再列出要查询的字段,原语句里C.id_transaccion as transacionId, SELECT A.*的写法会导致SQL执行失败,必须修正为:
SELECT A.*, C.id_transaccion as transacionId FROM (modulos A RIGHT JOIN modulos2 B ON A.uu_id_modulo = B.uu_id_modulo) LEFT JOIN operacion C ON B.id_operacion = C.uu_id_operacion WHERE uu_id = :uuId
解决方案
针对这种需要返回实体+额外字段的场景,有几种可行的处理方式:
1. 使用@SqlResultSetMapping映射结果
在Module实体上定义结果集映射,明确指定如何将查询结果中的额外字段映射到实体属性:
@Entity @AllArgsConstructor @NoArgsConstructor @Builder @Data @SqlResultSetMapping( name = "ModuleWithTransacionIdMapping", entities = @EntityResult( entityClass = Module.class, fields = { @FieldResult(name = "uuIdModule", column = "uu_id_modulo"), @FieldResult(name = "transacionId", column = "transacionId") } ) ) public class Module { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer uuIdModule; private Integer transacionId; }
然后在查询注解中指定使用该映射:
@Query(value = "SELECT A.uu_id_modulo, C.id_transaccion as transacionId, A.* FROM (modulos A RIGHT JOIN modulos2 B ON A.uu_id_modulo = B.uu_id_modulo) LEFT JOIN operacion C ON B.id_operacion = C.uu_id_operacion WHERE uu_id = :uuId", nativeQuery = true, resultSetMapping = "ModuleWithTransacionIdMapping") List<Module> queryModules(@Param("uuId") Integer uuId);
注意:查询语句中需要明确列出
uu_id_modulo(对应实体的uuIdModule)和transacionId,避免字段顺序导致映射错误。
2. 使用自定义DTO(推荐)
创建一个专门的DTO类来承载查询结果,避免污染实体类(毕竟transacionId不属于实体对应的表):
@Data @AllArgsConstructor public class ModuleDto { private Integer uuIdModule; // 其他Module实体的属性... private Integer transacionId; }
然后修改查询方法的返回类型为该DTO,确保查询字段顺序与DTO构造函数参数一致:
@Query(value = "SELECT A.uu_id_modulo, C.id_transaccion as transacionId FROM (modulos A RIGHT JOIN modulos2 B ON A.uu_id_modulo = B.uu_id_modulo) LEFT JOIN operacion C ON B.id_operacion = C.uu_id_operacion WHERE uu_id = :uuId", nativeQuery = true) List<ModuleDto> queryModules(@Param("uuId") Integer uuId);
这种方式更符合单一职责原则,实体类只负责对应数据库表的映射,DTO负责承载查询结果。
3. 使用Spring Data投影接口
定义一个投影接口,包含需要返回的所有属性的getter方法:
public interface ModuleProjection { Integer getUuIdModule(); Integer getTransacionId(); // 其他需要的属性getter... }
然后修改查询方法的返回类型为该接口:
@Query(value = "SELECT A.uu_id_modulo as uuIdModule, C.id_transaccion as transacionId FROM (modulos A RIGHT JOIN modulos2 B ON A.uu_id_modulo = B.uu_id_modulo) LEFT JOIN operacion C ON B.id_operacion = C.uu_id_operacion WHERE uu_id = :uuId", nativeQuery = true) List<ModuleProjection> queryModules(@Param("uuId") Integer uuId);
Spring Data会自动动态生成实现类,将查询结果映射到接口的getter方法对应的属性上。
内容的提问来源于stack exchange,提问作者Talenel

