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

Spring Data JPA findAll()查询子实体仅返回重复首条数据问题

问题:Spring Data JPA查询父实体时子实体重复且仅显示第一条数据

我定义了如下实体类,所有子实体(Address、BankAccounts)的数据已正确保存到数据库中。但使用父实体的findAll()方法查询子实体数据时,仅返回子表的第一条记录且重复显示,数据库中存在正确的多条数据。使用的Spring Data JPA版本为:implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.0.7.RELEASE'

实体类代码

@Entity
@Table(name = "Employee")
@AllArgsConstructor
@NoArgsConstructor
data class EmployeeEntity(

    .....

    @OneToMany(cascade = arrayOf(CascadeType.ALL))
    @JoinColumn(name = "id", referencedColumnName = "id")
    val addresses: List<Address>,

    @OneToMany(cascade = arrayOf(CascadeType.ALL))
    @JoinColumn(name = "id", referencedColumnName = "id")
    val bankaccounts: List<BankAccounts>

)

实际查询结果

[
    {
        .........
        "addresses": [
            {
                "address": "address 1"
            },
            {
                "address": "address 1"
            }
        ],
        "bankaccounts": [
            {
                "account": "account 1"
            },
            {
                "account": "account 1"
            }
        ]
    }
]

预期查询结果

[
    {
        .........
        "addresses": [
            {
                "address": "address 1"
            },
            {
                "address": "address 2"
            }
        ],
        "bankaccounts": [
            {
              "account": "account 1"
            },
            {
              "account": "account 2"
            }
        ]
    }
]

仓库接口

@Repository
interface EmployeeRepository : JpaRepository<EmployeeEntity, String?> {

}

问题原因及解决办法

核心原因

  1. 关联映射配置错误:两个@OneToMany注解的@JoinColumn都用了name = "id", referencedColumnName = "id",意味着子表用自身的主键id关联父表的id,完全不符合外键关联逻辑,导致JPA无法正确识别子实体归属。
  2. 笛卡尔积导致重复:同时查询多个集合关联时,JPA执行笛卡尔积查询,再加上映射错误,最终返回重复的第一条子实体数据。
  3. Kotlin数据类特性问题:data class自动生成的equals/hashCode包含集合字段,JPA处理懒加载或查询结果时,可能误判对象重复,重复添加相同实例。

解决步骤

1. 修正关联映射配置

给子表添加指向父实体的外键字段(如employee_id),并修改@JoinColumn配置:

@Entity
@Table(name = "Employee")
@AllArgsConstructor
@NoArgsConstructor
data class EmployeeEntity(

    @Id
    val id: String, // 父实体主键

    @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
    @JoinColumn(name = "employee_id", referencedColumnName = "id") // 子表外键列 -> 父表主键
    val addresses: List<Address> = emptyList(),

    @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
    @JoinColumn(name = "employee_id", referencedColumnName = "id")
    val bankaccounts: List<BankAccounts> = emptyList()

)

子实体示例(Address):

@Entity
@Table(name = "Address")
data class Address(
    @Id
    val id: String,
    val address: String,
    @Column(name = "employee_id")
    val employeeId: String // 关联父实体的id
)

2. 避免笛卡尔积问题

使用@Fetch(FetchMode.SUBSELECT)让JPA通过子查询分别加载每个集合,避免笛卡尔积:

@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
@JoinColumn(name = "employee_id", referencedColumnName = "id")
@Fetch(FetchMode.SUBSELECT)
val addresses: List<Address> = emptyList(),

@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
@JoinColumn(name = "employee_id", referencedColumnName = "id")
@Fetch(FetchMode.SUBSELECT)
val bankaccounts: List<BankAccounts> = emptyList()

3. 修正Kotlin数据类问题(可选但推荐)

手动重写equals/hashCode,仅基于主键字段判断,避免集合字段影响:

@Entity
@Table(name = "Employee")
@AllArgsConstructor
@NoArgsConstructor
class EmployeeEntity(

    @Id
    val id: String,

    @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
    @JoinColumn(name = "employee_id", referencedColumnName = "id")
    val addresses: List<Address> = emptyList(),

    @OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
    @JoinColumn(name = "employee_id", referencedColumnName = "id")
    val bankaccounts: List<BankAccounts> = emptyList()

) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as EmployeeEntity
        return id == other.id
    }

    override fun hashCode(): Int {
        return id.hashCode()
    }
}

4. 验证数据库数据

检查子表(Address、BankAccounts)的employee_id字段是否正确关联到对应父实体的id,确保数据关联关系无误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:27:50