Spring Boot+Hibernate中如何将MySQL自定义查询结果映射到Java模型类
嘿,这个场景我太熟了!用原生SQL查多表数据后没法直接映射到自定义的FavoriteItem确实是个常见的小难题,给你三个实用的解决方案,你可以根据自己的需求选:
1. 用@SqlResultSetMapping + @ConstructorResult映射到DTO
这是最规范的原生SQL映射方式,适合需要完整FavoriteItem对象的场景:
首先,确保你的FavoriteItem是一个普通POJO(不需要加@Entity注解,因为它只是数据传输对象),并且有一个和查询字段顺序匹配的构造函数:
public class FavoriteItem { private Long itemId; private String itemName; private String sizeName; private BigDecimal prices; // 要和查询返回的字段顺序完全对应 public FavoriteItem(Long itemId, String itemName, String sizeName, BigDecimal prices) { this.itemId = itemId; this.itemName = itemName; this.sizeName = sizeName; this.prices = prices; } // 别忘了加getter/setter }
接下来,在任意一个你的实体类上(比如MtItem实体)定义结果集映射:
@Entity @Table(name = "mt_item") @SqlResultSetMapping( name = "FavoriteItemMapping", // 这个名字要和后面@Query里的对应 classes = @ConstructorResult( targetClass = FavoriteItem.class, columns = { @ColumnResult(name = "item_id", type = Long.class), @ColumnResult(name = "item_name", type = String.class), @ColumnResult(name = "size_name", type = String.class), @ColumnResult(name = "Prices", type = BigDecimal.class) } ) ) public class MtItem { // 你的实体字段和注解 }
最后修改你的Repository方法,指定resultSetMapping:
@Query(value = "Select i.item_id,i.item_name,s.size_name,sp.Prices from mt_item i join mt_sizeprice sp on i.item_id= sp.item_id join mt_size s on sp.size_id=s.size_id where i.merchant_fk= ?1 and is_featured=1", nativeQuery = true, resultSetMapping = "FavoriteItemMapping") List<FavoriteItem> get(long merchantId);
这样查询结果就会自动映射成List<FavoriteItem>啦!
2. 用投影接口(轻量方案)
如果不需要完整的FavoriteItem对象,只是要获取这些字段,投影接口是更简单的选择:
先定义一个投影接口,方法名要和查询字段的别名对应(注意驼峰命名):
public interface FavoriteItemProjection { Long getItemId(); // 对应查询里的item_id AS itemId String getItemName(); // 对应item_name AS itemName String getSizeName(); // 对应size_name AS sizeName BigDecimal getPrices(); // 对应Prices AS prices }
然后修改你的查询语句,给每个字段加别名,同时返回类型改成List<FavoriteItemProjection>:
@Query(value = "Select i.item_id AS itemId, i.item_name AS itemName, s.size_name AS sizeName, sp.Prices AS prices from mt_item i join mt_sizeprice sp on i.item_id= sp.item_id join mt_size s on sp.size_id=s.size_id where i.merchant_fk= ?1 and is_featured=1", nativeQuery = true) List<FavoriteItemProjection> get(long merchantId);
Spring会自动生成这个接口的代理实现类,你直接调用getter方法就能拿到数据,甚至可以把它转换成FavoriteItem对象:
List<FavoriteItemProjection> projections = yourRepository.get(merchantId); List<FavoriteItem> favoriteItems = projections.stream() .map(p -> new FavoriteItem(p.getItemId(), p.getItemName(), p.getSizeName(), p.getPrices())) .collect(Collectors.toList());
3. 手动映射(应急方案)
如果只是临时解决,不想改太多代码,也可以手动把Object[]数组转换成FavoriteItem:
先把Repository方法的返回类型改成List<Object[]>:
@Query(value = "Select i.item_id,i.item_name,s.size_name,sp.Prices from mt_item i join mt_sizeprice sp on i.item_id= sp.item_id join mt_size s on sp.size_id=s.size_id where i.merchant_fk= ?1 and is_featured=1",nativeQuery = true) List<Object[]> get(long merchantId);
然后在业务代码里手动遍历转换:
List<Object[]> rawResults = yourRepository.get(merchantId); List<FavoriteItem> favoriteItems = new ArrayList<>(); for (Object[] row : rawResults) { FavoriteItem item = new FavoriteItem(); item.setItemId((Long) row[0]); item.setItemName((String) row[1]); item.setSizeName((String) row[2]); item.setPrices((BigDecimal) row[3]); favoriteItems.add(item); }
不过这种方法要注意字段顺序,如果SQL查询的字段顺序变了,代码就会出错,所以只适合临时用哦!
内容的提问来源于stack exchange,提问作者Avinaya Acharya

