一、简介

在本教程中,我们将讨论 jakarta.persistence 包中的 EntityNotFoundException 。我们将介绍可能发生此异常的情况,然后,我们将为这些情况编写测试。

2.什么时候抛出 EntityNotFoundException

此异常的 Oracle 文档定义了持久性提供程序可以抛出 EntityNotFoundException 的 三种情况:

  • EntityManager.getReference 不存在的实体
  • EntityManager.refresh 数据库中不存在的对象
  • EntityManager.lock 对数据库中不存在的实体进行悲观锁定

除了这三个用例之外,还有一种情况比较模糊。 使用 @ManyToOne 关系和延迟加载 时也会发生此异常。

当我们使用 @ManyToOne 注解时,引用的实体必须存在。这通常通过使用外键的数据库完整性来确保。如果我们在关系模型中不使用外键或者我们的数据库不一致,则在获取实体时我们会看到 EntityNotFoundException 。我们将在下一节中通过示例来说明这一点。

3. EntityNotFoundException 实践

首先,让我们介绍一个更简单的用例。在上一节中,我们提到了 getReference 方法。我们使用此方法来获取特定实体的代理。该代理仅初始化了主键字段。当我们在此代理实体上调用 getter 时,持久性提供程序会初始化其余字段。如果数据库中不存在实体,那么我们会得到 EntityNotFoundException

@Test(expected = EntityNotFoundException.class)
public void givenNonExistingUserId_whenGetReferenceIsUsed_thenExceptionIsThrown() {
    User user = entityManager.getReference(User.class, 1L);
    user.getName();
}

用户 实体是基本的。它只有两个字段,没有关系。我们在测试的第一行创建一个主键值为 1L 的代理实体。之后,我们对该代理实体调用 getter。持久性提供程序尝试通过主键获取实体,并且由于记录不存在,因此引发 EntityNotFoundException

对于下一个示例,我们将使用不同的域实体。我们将创建 ItemCategory 实体,它们之间具有双向关系:

@Entity
public class Item implements Serializable {
    @Id
    @Column(unique = true, nullable = false)
    private long id;
    private String name;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
    private Category category;
    // getters and setters
}
@Entity
public class Category implements Serializable {
    @Id
    @Column(unique = true, nullable = false)
    private long id;
    private String name;
    @OneToMany
    @JoinColumn(name = "category_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
    private List<Item> items = new ArrayList<>();
    // getters and setters
}

请注意,我们在 @ManyToOne 注释上使用延迟获取。另外,我们使用 @ForeignKey 从数据库中删除约束:

@Test(expected = EntityNotFoundException.class)
public void givenItem_whenManyToOneEntityIsMissing_thenExceptionIsThrown() {
    entityManager.createNativeQuery("Insert into Item (category_id, name, id) values (1, 'test', 1)").executeUpdate();
    entityManager.flush();
    Item item = entityManager.find(Item.class, 1L);
    item.getCategory().getName();
}

在此测试中,我们通过 id 获取 Item 实体。由于我们使用 FetchType.LAZY (仅设置了 id ,与前面的示例类似), find 方法将返回一个没有完全初始化 Category 的 Item 对象。当我们在 类别 对象上调用 getter 时,持久性提供程序将尝试从数据库中获取对象,并且由于记录不存在,我们将收到异常。

@ManyToOne 关系假设引用的实体存在。外键和数据库完整性确保这些实体的存在。如果不是这种情况,有一种解决方法可以忽略丢失的实体。

@NotFound(action = NotFoundAction.IGNORE)@ManyToOne 注释相结合将阻止持久性提供程序抛出 EntityNotFoundException 但我们必须手动处理丢失的实体以避免 NullPointerException

4。结论

在本文中,我们介绍了在哪些情况下会发生 EntityNotFoundException 以及如何处理它。首先,我们查看了官方文档并介绍了常见的用例。之后,我们将介绍更复杂的情况以及如何解决此问题。

像往常一样,我们可以 在 GitHub 上找到本文的代码。