You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot获取产品列表时productImages数组为空的解决方案咨询

问题:获取产品接口返回的productImages为空,不修改实体关联关系的解决办法

问题背景

测试「获取全部产品」接口时,返回结果中的productImages数组始终为空。相关代码如下:

Product实体类

public class Product implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@NotNull
@Size(max = 100)
@Column(name = "title", length = 100, nullable = false)
private String title;

@NotNull
@DecimalMin(value = "0")
@DecimalMax(value = "99999")
@Column(name = "day_price", precision = 21, scale = 2, nullable = false)
private BigDecimal dayPrice;

@Lob
@Column(name = "description", nullable = false)
private String description;

@NotNull
@Column(name = "position", nullable = false)
private String position;

@Min(value = 0L)
@Max(value = 100L)
@Column(name = "week_discount")
private Long weekDiscount;

@Min(value = 0L)
@Max(value = 100L)
@Column(name = "month_discount")
private Long monthDiscount;

@Min(value = 0L)
@Column(name = "refundable_deposit")
private Long refundableDeposit;

@Column(name = "enabled")
private Boolean enabled;

@Column(name = "deleted")
private Boolean deleted;

@Column(name = "created_at")
private Instant createdAt;

@Column(name = "updated_at")
private Instant updatedAt;

@Column(name = "deleted_at")
private LocalDate deletedAt;

@Enumerated(EnumType.STRING)
@Column(name = "currency")
private Currency currency;

@OneToMany(mappedBy = "product")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@org.springframework.data.annotation.Transient
@JsonIgnoreProperties(value = { "productBooking", "user", "invoice", "product" }, allowSetters = true)
private Set<Booking> bookings = new HashSet<>();

@OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@Fetch(value = FetchMode.SELECT) // don't remove this line
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@org.springframework.data.annotation.Transient
@JsonIgnoreProperties(value = { "product" }, allowSetters = true)
@BatchSize(size = 20)
private Set<ProductImages> productImages = new HashSet<>();

@OneToMany(mappedBy = "product")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@org.springframework.data.annotation.Transient
@JsonIgnoreProperties(value = { "user", "product" }, allowSetters = true)
private Set<ProductReview> productReviews = new HashSet<>();

@ManyToOne(optional = false)
@NotNull
private User owner;

@ManyToOne(optional = false)
@NotNull
@JsonIgnoreProperties(value = { "products", "productBookings", "productCategories", "parentId" }, allowSetters = true)
private ProductCategory productCategory;
}

ProductImages实体类

public class ProductImages implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@Lob
@Column(name = "image", nullable = false)
private byte[] image;

@NotNull
@Column(name = "image_content_type", nullable = false)
private String imageContentType;

@Column(name = "description")
private String description;

@ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "product_id", referencedColumnName = "id", nullable = false)
@NotNull
@JsonIgnoreProperties(value = { "bookings", "productImages", "productReviews", "owner", "productCategory" }, allowSetters = true)
private Product product;
}

Service层代码

@Transactional(readOnly = true)
public Page<Product> findAll(Pageable pageable) {
    return productRepository.findAll(pageable);
}

需在不修改实体间关联关系的前提下解决productImages为空的问题。


可行解决方案

1. 自定义Repository查询,主动关联查询ProductImages

在ProductRepository中添加带关联查询的自定义方法,通过JPQL主动加载productImages:

public interface ProductRepository extends JpaRepository<Product, Long> {
    @Query("SELECT DISTINCT p FROM Product p LEFT JOIN FETCH p.productImages WHERE p.deleted = false")
    @QueryHints(value = @QueryHint(name = org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false"))
    Page<Product> findAllWithProductImages(Pageable pageable);
}

然后修改Service层调用该方法:

@Transactional(readOnly = true)
public Page<Product> findAll(Pageable pageable) {
    return productRepository.findAllWithProductImages(pageable);
}

注:添加DISTINCT和HINT_PASS_DISTINCT_THROUGH是为了解决分页时的重复结果问题。

2. 使用EntityGraph指定加载关联属性

利用JPA的EntityGraph显式指定查询时加载productImages,无需修改实体FetchType:

public interface ProductRepository extends JpaRepository<Product, Long> {
    @EntityGraph(attributePaths = {"productImages"})
    Page<Product> findAll(Pageable pageable);
}

Spring Data JPA会自动生成关联查询SQL,主动加载productImages集合,Service层代码无需改动。

3. Service层手动初始化懒加载集合

在事务范围内,用Hibernate的initialize()方法手动触发懒加载集合的加载:

@Transactional(readOnly = true)
public Page<Product> findAll(Pageable pageable) {
    Page<Product> productPage = productRepository.findAll(pageable);
    productPage.getContent().forEach(product -> {
        Hibernate.initialize(product.getProductImages());
    });
    return productPage;
}

注:该方式会触发N+1查询,但你已在实体中配置@BatchSize(size = 20),可有效减少查询次数。


内容的提问来源于stack exchange,提问作者Charfeddine Mohamed Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 19:45:27