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

Spring Boot中Lombok @Data注解致关联表查询为空问题求助

问题描述

我用Spring Boot + MySQL开发API时碰到个奇怪问题:关联表EtapeRecette.java用Lombok的@Data注解生成getter/setter,编译没问题,但调用RecetteRepository.findAll()返回的etapes数组是空的;换成手动写getter/setter或者用@Getter+@Setter注解就完全正常。想知道@Data失效的原因是什么?

附上三个实体类代码:

Recette.java

package org.microferme.charme.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.sql.Timestamp;
import java.util.Date;
import java.util.Set;

@Data
@NoArgsConstructor
@Entity
@Table(name="Recette")
public class Recette {

    @Id
    private String id;

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

    @Column(name = "nb_personne")
    private int nbPersonne;

    @Column(name = "prix_par_personne")
    private float prixParPersonne;

    @OneToMany(mappedBy = "recette", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonIgnoreProperties("recette")
    private Set<MediaRecette> medias;

    @OneToMany(mappedBy = "recette", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonIgnoreProperties("recette")
    private Set<StockUtile> ingredients;

    @OneToMany(mappedBy = "recette", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonIgnoreProperties("recette")
    private Set<EtapeRecette> etapes;

    public Recette(String nom, int nbPersonne, float prixParPersonne) {
        //Si l'id n'est pas spécifié on prend la timestamp actuelle
        Date date = new Date();
        this.id = String.valueOf(new Timestamp(date.getTime()).getTime());

        this.nom = nom;
        this.nbPersonne = nbPersonne;
        this.prixParPersonne = prixParPersonne;
    }

    public void addMedia(MediaRecette media) {
        this.medias.add(media);
    }

    public void addIngredient(StockUtile ingredient) {
        this.ingredients.add(ingredient);
    }

    public void addEtape(EtapeRecette etapeRecette) {
        this.etapes.add(etapeRecette);
    }

}

Etape.java

package org.microferme.charme.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.Set;

@Data
@Entity
@Table(name = "Etape")
public class Etape {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "ordre")
    private float ordre;

    @Column(name = "duree")
    private float duree;

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

    @ManyToOne
    private Ustensile ustensile;

    public Etape() {
    }

    public Etape(float ordre, float duree, String description, Ustensile ustensile) {
        this.ordre = ordre;
        this.duree = duree;
        this.description = description;
        this.ustensile = ustensile;
    }
}

EtapeRecette.java(Recette与Etape的关联表)

package org.microferme.charme.model;

import jakarta.persistence.*;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.microferme.charme.model.embeddable.EtapeRecetteId;


@Entity
@Data
@Table(name = "EtapeRecette")
public class EtapeRecette {

    @EmbeddedId
    private EtapeRecetteId id;

    @ManyToOne(cascade = CascadeType.ALL)
    @MapsId("recetteId")
    private Recette recette;

    @ManyToOne(cascade = CascadeType.ALL)
    @MapsId("etapeId")
    private Etape etape;

    public EtapeRecette() {
    }

    public EtapeRecette(Recette recette, Etape etape) {
        this.id = new EtapeRecetteId(recette.getId(), etape.getId());
        this.recette = recette;
        this.etape = etape;
    }

}
原因分析

问题出在@Data注解的附加功能上:它除了生成getter/setter,还默认生成基于所有字段的equals()、hashCode()和toString()方法。

对于EtapeRecette这种带嵌入式ID和双向JPA关联的实体,自动生成的equals()和hashCode()会依赖recette和etape这两个关联对象。而JPA加载关联集合(比如Recette的etapes)时,会用这些方法来管理集合元素:

  • 当JPA使用懒加载时,recette或etape可能是未初始化的代理对象,此时调用它们的属性计算equals/hashCode会触发意外的加载行为,导致集合无法正确填充。
  • 双向关联的循环引用也会让equals/hashCode的逻辑出现异常,最终表现为返回的etapes集合为空。

而@Getter+@Setter只生成存取方法,不会自动生成equals/hashCode,JPA就能正常处理关联集合的加载。

解决方案

有两种可行的解决方式:

  • 直接替换@Data为@Getter+@Setter+@NoArgsConstructor(保持原有构造逻辑),只生成必要的存取方法,避免equals/hashCode带来的问题。
  • 保留@Data,但通过@EqualsAndHashCode注解指定只基于嵌入式ID生成equals/hashCode,避免依赖关联对象:
@Entity
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Table(name = "EtapeRecette")
public class EtapeRecette {

    @EmbeddedId
    @EqualsAndHashCode.Include
    private EtapeRecetteId id;

    // 其余代码不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:45:04