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

Spring Boot+JPA关联实体时抛出EntityNotFoundException问题排查

问题:关联Player与Punishment时抛出EntityNotFoundException

我正在开发一个处理足球相关处罚(如训练迟到)的Spring Boot应用,前端采用Vaadin,数据库用H2,持久层基于Spring Data JPA。单独插入Player和Punishment实体无异常,但通过PunishmentToPlayer表关联两者ID时,抛出javax.persistence.EntityNotFoundException,且数据库中Player和Punishment的实体数据是正确的。


相关实体类代码

Player实体

@Setter
@Getter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Player extends AbstractEntity{

    private String firstName;
    private String lastName;
}

Punishment实体

@Setter
@Getter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Punishment extends AbstractEntity{

    private String description;
    private Double amount;
}

PunishmentToPlayer实体

@Setter
@Getter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PunishmentToPlayer extends AbstractEntity{

    @OneToOne
    private Player player;

    @OneToOne
    private Punishment punishment;
}

数据初始化代码

@Component
public class DataHelper {

    public void addPlayer(PlayerService playerService){
        playerService.saveOrUpdate(buildPlayer("Player1_Name", "Player1_Lastname"));
        playerService.saveOrUpdate(buildPlayer("Player2_Name", "Player2_Lastname"));
        playerService.saveOrUpdate(buildPlayer("Player3_Name", "Player3_Lastname"));
        playerService.saveOrUpdate(buildPlayer("Player4_Name", "Player4_Lastname"));
    }

    public void addPunishments(PunishmentService punishmentService){
        punishmentService.saveOrUpdate(buildPunishment("Description 1", 20.00));
        punishmentService.saveOrUpdate(buildPunishment("Description 2", 15.00));
        punishmentService.saveOrUpdate(buildPunishment("Description 3", 10.00));
        punishmentService.saveOrUpdate(buildPunishment("Description 4", 5.00));
    }

    public void addConnections(PunishmentToPlayerService punishmentToPlayerService, PlayerService playerService, PunishmentService punishmentService){
        List<Player> players =  playerService.getAllPlayer();
        List<Punishment> punishments = punishmentService.getAllPunishments();

        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(0), punishments.get(0)));
        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(0), punishments.get(2)));
        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(1), punishments.get(1)));
        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(2), punishments.get(0)));
        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(2), punishments.get(3)));
        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(3), punishments.get(0)));
        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(3), punishments.get(3)));
        punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(players.get(3), punishments.get(3)));
    }

    private Player buildPlayer(String firstname, String lastname){
        return Player.builder()
                .firstName(firstname)
                .lastName(lastname)
                .build();
    }

    private Punishment buildPunishment(String description, double value){
        return Punishment.builder()
                .description(description)
                .amount(value)
                .build();
    }

    private PunishmentToPlayer buildPunishmentToPlayer(Player player, Punishment punishment){
        return PunishmentToPlayer.builder()
                .player(player)
                .punishment(punishment)
                .build();
    }
}

PunishmentToPlayerService代码

public void saveOrUpdate(PunishmentToPlayer punishmentToPlayer){
        punishmentToPlayerRepository.save(punishmentToPlayer);
}

异常堆栈信息

Caused by: org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find de.nils.Tus_Strafen.model.Player with id a62dde9d-2514-44d8-9d97-7d54afd69ef8; nested exception is javax.persistence.EntityNotFoundException: Unable to find de.nils.Tus_Strafen.model.Player with id a62dde9d-2514-44d8-9d97-7d54afd69ef8
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:379) ~[spring-orm-5.3.22.jar:5.3.22]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235) ~[spring-orm-5.3.22.jar:5.3.22]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551) ~[spring-orm-5.3.22.jar:5.3.22]
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.3.22.jar:5.3.22]
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) ~[spring-tx-5.3.22.jar:5.3.22]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152) ~[spring-tx-5.3.22.jar:5.3.22]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.22.jar:5.3.22]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174) ~[spring-data-jpa-2.7.2.jar:2.7.2]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.22.jar:5.3.22]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.22.jar:5.3.22]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.22.jar:5.3.22]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.22.jar:5.3.22]
    at com.sun.proxy.$Proxy123.save(Unknown Source) ~[na:na]
    at de.nils.Tus_Strafen.service.PunishmentToPlayerService.saveOrUpdate(PunishmentToPlayerService.java:48) ~[classes/:na]
    at de.nils.Tus_Strafen.DataHelper.addConnections(DataHelper.java:35) ~[classes/:na]
    at de.nils.Tus_Strafen.view.views.MainAppLayout.addData(MainAppLayout.java:81) ~[classes/:na]
    at de.nils.Tus_Strafen.view.views.MainAppLayout.<init>(MainAppLayout.java:41) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:64) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481) ~[na:na]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211) ~[spring-beans-5.3.22.jar:5.3.22]

原因分析及解决办法

1. 事务边界问题

添加Player、Punishment和关联数据的操作可能不在同一个事务中,导致关联时Player/Punishment的事务未提交,JPA无法找到对应实体。
解决:将addPlayer、addPunishments、addConnections的调用放在同一个带@Transactional注解的方法中,确保所有操作在同一事务内完成,保证数据持久化的一致性。

2. 实体状态问题

从getAllPlayer()和getAllPunishments()获取的实体可能处于游离状态(detached),JPA无法识别其为已持久化实体。
解决:关联前通过ID重新查询实体,将其转换为持久化状态:

Player managedPlayer = playerService.getPlayerById(players.get(0).getId());
Punishment managedPunishment = punishmentService.getPunishmentById(punishments.get(0).getId());
punishmentToPlayerService.saveOrUpdate(buildPunishmentToPlayer(managedPlayer, managedPunishment));

3. 关联注解逻辑错误

当前PunishmentToPlayer使用@OneToOne注解,但实际业务中一个球员可能有多个处罚、一个处罚可能对应多个球员,不符合业务逻辑,会导致JPA关联校验失败。
解决:修改为@ManyToOne注解:

@Setter
@Getter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PunishmentToPlayer extends AbstractEntity{

    @ManyToOne
    private Player player;

    @ManyToOne
    private Punishment punishment;
}

4. ID生成策略问题

检查AbstractEntity的ID生成逻辑,如果是手动生成ID但未正确持久化,可能导致查询到的实体ID与数据库实际ID不一致。
解决:确保AbstractEntity使用JPA标准ID生成策略,例如:

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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 05:07:03