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

如何实现JPA双向关联,查询供应商时返回关联产品?

问题:查询Supplier时无法返回关联的Product列表

问题场景

现有两个JPA实体类Product和Supplier配置了双向关联:

Product类代码

@Entity
public class Product
{
    @Id
    private Long productID;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JsonManagedReference
    @JoinColumn(name = "fk_supplier")
    private Supplier supplier;

    private String description;

    public Supplier getSupplier()
    {
        return supplier;
    }
}

Supplier类代码

@Entity
public class Supplier
{
    @Id
    private Long supplierID;

    @OneToMany(mappedBy = "supplier")
    @JsonBackReference
    private Set<Product> products = new HashSet<>();

    private String name;

    public Set<Product> getProducts()
    {
        return products;
    }
}

当前查询Product时能正常返回关联的Supplier信息,示例返回:

{
    "productID": 1,
    "supplier": {
        "supplierID": 1,
        "name": "Acer"
    },
    "description": "Gaming chair"
}

但查询Supplier时,返回结果缺少关联的Products列表,示例返回:

{
    "supplierID": 1,
    "name": "Acer"
}

原因分析

  1. JSON序列化限制:@JsonBackReference注解的作用是序列化时忽略当前属性,避免双向关联引发的循环引用问题,因此Supplier的products集合不会被Jackson序列化输出。
  2. JPA懒加载策略:@OneToMany注解默认FetchType为LAZY(懒加载),查询Supplier时不会主动加载关联的Product集合,若在Session关闭后访问该集合还会抛出懒加载异常。

解决方案

方案一:调整JSON注解与JPA加载策略

修改Supplier类的注解,用@JsonIgnoreProperties替代@JsonBackReference处理循环引用,同时设置FetchType.EAGER主动加载关联集合:

@Entity
public class Supplier
{
    @Id
    private Long supplierID;

    // 设置主动加载,并忽略Product中的supplier属性避免循环序列化
    @OneToMany(mappedBy = "supplier", fetch = FetchType.EAGER)
    @JsonIgnoreProperties("supplier")
    private Set<Product> products = new HashSet<>();

    private String name;

    public Set<Product> getProducts()
    {
        return products;
    }

    // 推荐添加维护双向关联的方法
    public void addProduct(Product product) {
        products.add(product);
        product.setSupplier(this);
    }
}

同时可移除Product类中的@JsonManagedReference(@JsonIgnoreProperties已处理循环引用),并补充setter方法方便维护关联:

@Entity
public class Product
{
    @Id
    private Long productID;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "fk_supplier")
    private Supplier supplier;

    private String description;

    public Supplier getSupplier()
    {
        return supplier;
    }

    public void setSupplier(Supplier supplier) {
        this.supplier = supplier;
    }
}

方案二:使用JOIN FETCH查询(性能更优,推荐)

若不想修改默认懒加载策略(EAGER在大数据场景下可能影响性能),可通过JPQL的JOIN FETCH在查询时主动加载关联集合:

  1. 定义SupplierRepository接口:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface SupplierRepository extends JpaRepository<Supplier, Long> {
    @Query("SELECT s FROM Supplier s JOIN FETCH s.products WHERE s.supplierID = :id")
    Supplier findByIdWithProducts(@Param("id") Long id);
}
  1. 修改Supplier类的JSON注解:
@Entity
public class Supplier
{
    @Id
    private Long supplierID;

    @OneToMany(mappedBy = "supplier")
    @JsonIgnoreProperties("supplier")
    private Set<Product> products = new HashSet<>();

    private String name;

    public Set<Product> getProducts()
    {
        return products;
    }
}

调用findByIdWithProducts方法查询时,会一次性加载Supplier及其关联的Product集合,Jackson也能正常序列化输出products列表。

内容的提问来源于stack exchange,提问作者DwnBrd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 07:05:16