Spring Data JPA中@Query查询返回嵌套Object[]而非预期Object[]的原因咨询
嗨,我来帮你捋清楚这个问题~
从你贴的代码和输出结果来看,你预期findDetailedProduct方法返回一个包含商品字段的Object[],但实际得到的是一个长度为1的外层Object[],里面的元素才是真正装着字段值的内层Object[]——这就是为什么你打印product.length得到1,且product[0]的类型是[Ljava.lang.Object;(也就是Object[]的类名)。
问题原因
这是Spring Data JPA处理自定义投影查询的默认行为:当你用@Query指定查询多个字段,并且把方法返回类型声明为Object[]时,框架会把查询结果的每一行数据封装成一个Object[],然后把所有行的数组统一放到一个外层Object[]里返回。哪怕你的查询只命中一行数据,外层数组也会包含这一个内层数组,于是就出现了你看到的嵌套数组情况。
解决办法
这里给你几个更优雅的处理方案,按推荐程度排序:
1. 直接返回DTO对象(最推荐)
既然你最终要转换成DetailedProductResponse,不如直接在查询里构造这个对象,省去手动转换的步骤。
首先确保DetailedProductResponse有对应字段的构造函数:
public class DetailedProductResponse { // 你的字段定义 public DetailedProductResponse(Integer id, String name, String description, BigDecimal price, BigDecimal discount, Integer quantity, String imagePublicKey, String category, Boolean isVegetarian, LocalDate expiryDate) { // 字段赋值逻辑 } }
然后修改Repository的查询方法:
@Query("select new com.yourpackage.DetailedProductResponse(p.id, p.name, p.description, p.price, p.discount, p.quantity, p.imagePublicKey, p.category, p.isVegetarian, p.expiryDate) from Product p where p.id = :productId") public DetailedProductResponse findDetailedProduct(Integer productId);
这样调用时直接拿到目标对象,不需要再处理数组:
@Override public DetailedProductResponse getDetailedProduct(Integer productId) { return productRepository.findDetailedProduct(productId); }
2. 修改返回类型为Optional<Object[]>
如果还是想用数组的方式,可以把方法返回类型改成Optional<Object[]>,明确表示这是单行结果:
@Query("select id, name, description, price, discount, quantity, imagePublicKey, category, isVegetarian, expiryDate from Product where id = :productId") public Optional<Object[]> findDetailedProduct(Integer productId);
调用时直接取出内层数组:
@Override public DetailedProductResponse getDetailedProduct(Integer productId) { Object[] product = productRepository.findDetailedProduct(productId) .orElseThrow(() -> new RuntimeException("商品不存在")); return ConversionUtility.convertToDetailedProduct(product); }
3. 手动取外层数组的第一个元素(不推荐,仅作兼容)
如果不想修改Repository的方法定义,也可以在调用时手动取出嵌套的内层数组:
@Override public DetailedProductResponse getDetailedProduct(Integer productId) { Object[] result = productRepository.findDetailedProduct(productId); // 注意这里要转型,且需确保查询结果不为空,否则会抛出数组越界异常 Object[] product = (Object[]) result[0]; return ConversionUtility.convertToDetailedProduct(product); }
这个方法不够优雅,且存在空指针风险,所以不建议使用。
备注:内容来源于stack exchange,提问作者Neeraj Kumar

