Spring Data Projection返回TupleBackedMap而非预期对象问题排查
问题
我定义了如下实体类:
@Entity @Data public class User { @Id private Long id; private String num; @OneToOne @JoinColumn(name = "group_id", referencedColumnName = "id") private Group group; } @Entity @Data public class Group { @Id private Long id; private String zone; }
我想通过Spring Data JPA的投影查询,获取所有用户的num和所属的zone,于是定义了这个投影接口:
public interface UserDtoProjection { String getNum(); String getZone(); }
查询方法写的是这样:
@Query(value = "SELECT u.num as num, g.zone as zone FROM \"user\" u join \"group\" g on u.group_id = g.id", nativeQuery = true) List<UserDtoProjection> findAllUserZones();
但调用这个方法后,返回的不是预期的UserDtoProjection对象,而是TupleBackedMap,这是怎么回事?
原因及解决办法
问题出在原生SQL查询(nativeQuery=true)默认不支持直接返回接口投影,Spring Data JPA处理原生查询时,默认会把结果封装成TupleBackedMap这种键值对容器,不会自动实例化你定义的投影接口。
给你两种解决思路:
思路一:改用JPQL查询配合接口投影
把原生SQL换成JPQL,Spring Data JPA就能自动识别投影接口并完成属性映射,还能利用实体类的关联关系省掉手动join:@Query("SELECT u.num as num, u.group.zone as zone FROM User u") List<UserDtoProjection> findAllUserZones();这种方式更贴合JPA的使用逻辑,代码也更简洁。
思路二:用类投影(DTO)替代接口投影(支持原生SQL)
如果必须用原生SQL,接口投影不好使,换成带构造函数的DTO类就行:
先写DTO类:@Data public class UserDto { private String num; private String zone; public UserDto(String num, String zone) { this.num = num; this.zone = zone; } }然后修改查询方法(注意要把
nativeQuery设为false,因为new DTO()是JPQL的语法,原生SQL不支持):@Query(value = "SELECT new com.yourpackage.UserDto(u.num, u.group.zone) FROM User u") List<UserDto> findAllUserZones();要是非得用原生SQL,就得通过
@SqlResultSetMapping来配置结果映射:
先在User实体类上添加映射配置:@Entity @SqlResultSetMapping( name = "UserDtoMapping", classes = @ConstructorResult( targetClass = UserDto.class, columns = { @ColumnResult(name = "num", type = String.class), @ColumnResult(name = "zone", type = String.class) } ) ) public class User { // ... 原有代码不变 }再修改查询方法指定映射:
@Query(value = "SELECT u.num as num, g.zone as zone FROM \"user\" u join \"group\" g on u.group_id = g.id", nativeQuery = true, resultSetMapping = "UserDtoMapping") List<UserDto> findAllUserZones();
内容的提问来源于stack exchange,提问作者Mr.Blev
相关产品推荐
相关产品推荐

