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

Spring Boot JPA查询中出现未指定关联实体的问题求助

Spring Data JPA懒加载集合意外加载问题

开发Spring Data JPA应用时遇到一个奇怪的问题:原本认为懒加载(FetchType.LAZY)的集合类型实体仅在实际访问或通过fetch join查询时才会被加载,但调试时发现了一个未在查询的select、from或where子句中指定的ProjectMember实体。

实体类代码

Project实体

@Entity
@Table(name = "projects")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Project extends BaseTimeEntity {

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

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

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
    private final List<ProjectGroup> groups = new ArrayList<>();

    @OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
    private final List<ProjectMember> projectMembers = new ArrayList<>();

    @Builder
    public Project(
        String name
    ) {
        this.name = name;
    }

    public void addGroup(ProjectGroup group) {
        this.groups.add(group);
        group.setProject(this);
    }

    public void removeGroup(ProjectGroup group) {
        this.groups.remove(group);
        group.setProject(null);
    }

    public void addProjectMember(ProjectMember projectMember) {
        this.projectMembers.add(projectMember);
        projectMember.setProject(this);
    }

    public void removeProjectMember(ProjectMember projectMember) {
        this.projectMembers.remove(projectMember);
        projectMember.setProject(null);
    }
}

ProjectGroup实体

@Entity
@Table(name = "project_groups")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ProjectGroup extends BaseTimeEntity {

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

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "project_id", foreignKey = @ForeignKey(name = "FK_PROJECT_GROUP_PROJECT"))
    private Project project;

    @Builder
    public ProjectGroup(String name) {
        this.name = name;
    }

    public void setProject(Project project) {
        this.project = project;
    }
}

ProjectMember实体

@Entity
@Table(name = "project_members")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ProjectMember extends BaseTimeEntity {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "project_id", foreignKey = @ForeignKey(name = "FK_PROJECT_MEMBER_PROJECT"))
    private Project project;

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

    @Column(name = "is_deleted")
    private boolean isDeleted = false;

    @Builder
    public ProjectMember(
        String nickname,
        boolean isDeleted
    ) {
        this.nickname = nickname;
        this.isDeleted = isDeleted;
    }

    public void setProject(Project project) {
        this.project = project;
    }
}

所有集合类型实体都设置为FetchType.LAZY(默认值)。

Repository与Service代码

ProjectRepository

public interface ProjectRepository extends JpaRepository<Project, Long> {

    @Query("""
        select distinct p
        from Project p
            left join fetch p.groups
        where p.id = :id
    """)
    Optional<Project> findWithProjectGroupsById(@Param("id") Long id);

}

ProjectQueryServiceImpl

@Service
@Transactional
@RequiredArgsConstructor
public class ProjectQueryServiceImpl implements ProjectQueryService {

    private final ProjectRepository projectRepository;

    @Override
    public ProjectResponseDto fetchWithMembersAndGroups(Long id) {
        return projectRepository.findWithProjectGroupsById(id)
            .map(ProjectResponseDto::new)
            .orElseThrow(EntityNotFoundException::new);
    }
}

Service仅通过fetch join查询Project及其关联的groups实体,查询中完全没有涉及ProjectMember实体。

生成的SQL

select
    distinct project0_.id as id1_2_0_,
    groups1_.id as id1_0_1_,
    project0_.created_at as created_2_2_0_,
    project0_.modified_at as modified3_2_0_,
    project0_.name as name4_2_0_,
    groups1_.created_at as created_2_0_1_,
    groups1_.modified_at as modified3_0_1_,
    groups1_.name as name4_0_1_,
    groups1_.project_id as project_5_0_1_,
    groups1_.project_id as project_5_0_0__,
    groups1_.id as id1_0_0__ 
from
    projects project0_ 
left outer join
    project_groups groups1_ 
        on project0_.id=groups1_.project_id 
where
    project0_.id=?

问题现象

在该查询执行后设置断点调试时,发现未指定的ProjectMember实体已被加载(调试截图显示该集合包含数据)。

有人知道这是为什么吗?尝试查找原因但仍未找到。可通过示例代码仓库复现,运行测试方法并在PostResponseDto中设置断点,执行Repository方法后暂停测试即可复现。


内容的提问来源于stack exchange,提问作者I have 10 fingers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 05:14:51