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

Spring Boot Jakarta @ManyToMany关联表未填充问题求助

多对多关联中间表未填充问题排查与解决

在Spring Boot Jakarta项目中,通过@ManyToMany与@CollectionTable配置Ordini和Prodotti的多对多关联,中间表为idsord。使用Postman提交包含关联Prodotti数据的Ordini请求时,主表插入成功,但中间表idsord无关联数据,返回结果中的listaProdotti为空数组,以下是问题根源及修复方案:

相关代码与请求体

Ordini实体类

@Entity
@Table(name = "ORDINI")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Ordini
{
    @Id
    @Column(name = "idord")
    @Size(min=5,max=25,message = "{Size.Ordini.idord.Validation}")
    private String idord;

    @Column(name = "idutente")
    @Size(min=5,max=40,message = "{Size.Ordini.idutente.Validation}")
    private String idutente;

    @Column(name = "idorganizzazione")
    @Size(min=5,max=35,message = "{Size.Ordini.idorganizzazione.Validation}")
    @NotBlank(message = "{NotBlank.Ordini.descrizione.Validation}")
    private String idorganizzazione;

    @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, mappedBy = "ordine")
    @CollectionTable(name = "idsord", joinColumns = @JoinColumn(name = "idord", referencedColumnName = "idord"))
    @Valid
    private Set<Prodotti> listaProdotti = new HashSet<>();

    @Column(name = "mailverificata")
    private boolean mailverificata;

    @Column(name = "pagamentoeseguito")
    private boolean pagamentoeseguito;

    @Temporal(TemporalType.DATE)
    @Column(name = "date")
    @NotNull(message = "{NotNull.Ordini.datacreazione.Validation}")
    private Date date;

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

    @Column(name = "linkpagamento")
    private String linkpagamento;
}

Prodotti实体类

@Entity
@Table(name = "PRODOTTI")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Prodotti
{
    @Id
    @Column(name = "codprod")
    @Size(min=5,max=20,message = "{Size.Prodotti.codprod.Validation}")
    private String codprod;

    @Column(name = "nomeprod")
    @Size(min=5,max=20,message = "{Size.Prodotti.nomeprod.Validation}")
    private String nomeprod;

    @Column(name = "descrizione")
    @Size(min=6,max=80,message = "{Size.Prodotti.descrizione.Validation}")
    @NotBlank(message = "{NotBlank.Prodotti.descrizione.Validation}")
    private String descrizione;

    @Column(name = "um")
    @NotBlank(message = "{NotBlank.Prodotti.um.Validation}")
    private String um;

    @Column(name = "codstat")
    private String codStat;

    @Column(name = "pz")
    @Max(value = 99, message = "{Max.Prodotti.pz.Validation}")
    @Min(value = 1, message = "{Min.Prodotti.pz.Validation}")
    private Integer pz;

    @Column(name = "pesonetto")
    @Max(value = 999, message = "{Max.Prodotti.pesonetto.Validation}")
    @DecimalMin(value = "0.01", message = "{Min.Prodotti.pesonetto.Validation}")
    private double pesonetto;

    @Column(name = "idstatoprod")
    @NotNull(message = "{NotNull.Prodotti.idstatoprod.Validation}")
    private String idstatoprod;

    @Column(name = "imgsrc")
    @NotNull(message = "{NotNull.Prodotti.imgSrc.Validation}")
    private String imgsrc;

    @ManyToOne
    @JoinColumn(name = "iva",  referencedColumnName = "idiva")
    @Valid
    private Iva iva;

    @ManyToOne
    @JoinColumn(name = "famassort", referencedColumnName = "id")
    @Valid
    private FamAssort famAssort;
    
    @ManyToMany
    @CollectionTable(name = "idsord", joinColumns = @JoinColumn(name = "codprod", referencedColumnName = "codprod"))
    private Set<Ordini> ordine  = new HashSet<>();
}

Postman请求体

{
        "idord": "000004",
        "idutente": "123stezza",
        "idorganizzazione": "mygroup",
        "listaProdotti": [
                  {
                    "codprod": "000201",
                    "nomeprod": "Muscoli",
                    "descrizione": "Muscoli seminati, coltivati e raccolti nel golfo di La Spezia.",
                    "um": "kg",
                    "codstat": null,
                    "pz": 2,
                    "pesonetto": 0.8,
                    "idstatoprod": "1",
                    "prezzo": 0.0,
                    "imgsrc": "img/prodotto1.jpg",
                    "famAssort": {
                        "id": 1,
                        "descrizione": "Molluschi"
                    },
                    "iva": {
                        "idIva": 3,
                        "descrizione": "IVA 22%",
                        "aliquota": 22
                    }}
        ],
        "mailverificata": true,
        "pagamentoeseguito": false,
        "date": "2025-02-24",
        "codStat": "pagato",
        "linkpagamento": ""
}

问题根源

  1. 双向多对多配置冲突:

    • Ordini类的@ManyToMany同时使用了mappedBy(标记为被动端)和@CollectionTable(主动端配置中间表),两者不能共存,导致Hibernate无法正确识别关联规则。
    • Prodotti类的@ManyToMany缺少inverseJoinColumns配置,无法明确中间表中关联Ordini的字段,关联关系定义不完整。
  2. 关联关系未双向维护:
    即使配置正确,保存Ordini时如果未将当前Ordini实例添加到关联Prodotti的ordine集合中,被动端不会触发中间表的插入操作。

  3. 懒加载导致返回为空:
    Ordini的listaProdotti使用FetchType.LAZY,默认不会主动加载关联数据,返回前端时若未主动触发加载,会呈现为空数组。

修复方案

1. 修正多对多关联配置

  • Ordini(被动端):移除@CollectionTable,仅保留mappedBy指定主动端:
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "ordine")
@Valid
private Set<Prodotti> listaProdotti = new HashSet<>();
  • Prodotti(主动端):使用@JoinTable完整定义中间表关联字段:
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
    name = "idsord",
    joinColumns = @JoinColumn(name = "codprod", referencedColumnName = "codprod"),
    inverseJoinColumns = @JoinColumn(name = "idord", referencedColumnName = "idord")
)
private Set<Ordini> ordine = new HashSet<>();

注意:主动端避免使用CascadeType.ALL,防止意外删除关联数据;按需选择PERSIST/MERGE即可。

2. 双向维护关联关系

在保存Ordini的业务逻辑中,手动维护双向关联:

public Ordini saveOrdine(Ordini ordine) {
    for (Prodotti prodotto : ordine.getListaProdotti()) {
        prodotto.getOrdine().add(ordine);
    }
    return ordineRepository.save(ordine);
}

3. 解决返回结果为空问题

  • 方案一:主动加载关联数据:在Repository层使用JOIN FETCH查询:
@Query("SELECT o FROM Ordini o JOIN FETCH o.listaProdotti WHERE o.idord = :idord")
Optional<Ordini> findByIdWithProdotti(@Param("idord") String idord);
  • 方案二:优化序列化配置:添加@JsonIgnoreProperties避免懒加载序列化问题:
@JsonIgnoreProperties("ordine")
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "ordine")
@Valid
private Set<Prodotti> listaProdotti = new HashSet<>();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 00:04:53