EntityNotFoundException in Hibernate – Hibernate中的EntityNotFoundException

最后修改: 2021年 8月 1日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll talk about the EntityNotFoundException from the javax.persistence package. We’ll cover the cases when this exception can occur, and after that, we’ll write tests for those cases.

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

2. When Is the EntityNotFoundException Thrown?

2.什么时候抛出EntityNotFoundException

Oracle documentation for this exception defines three situations in which the persistence provider can throw the EntityNotFoundException:

Oracle关于这个异常的文档定义了持久化提供者可以抛出EntityNotFoundException的三种情况。

  • EntityManager.getReference on an entity that does not exist
  • EntityManager.refresh on an object that does not exist in the database
  • EntityManager.lock with pessimistic locking on an entity that does not exist in the database

Apart from these three use cases, there is one more case that is a bit more ambiguous. This exception can also occur when working with @ManyToOne relationships and lazy loading.

除了这三种用例,还有一种情况比较模糊。在处理@ManyToOne关系和懒惰加载时也会出现这种异常。

When we use @ManyToOne annotation, then the referenced entity must exist. This is usually ensured with database integrity using foreign keys. If we don’t use foreign keys in our relational model or our database is inconsistent, we can see EntityNotFoundException when fetching entities. We’ll illustrate this in the following section with an example.

当我们使用@ManyToOneannotation时,那么被引用的实体必须存在。这通常是通过使用外键的数据库完整性来保证的。如果我们在关系模型中不使用外键,或者我们的数据库不一致,我们在获取实体时就会看到EntityNotFoundException。我们将在下一节用一个例子来说明这个问题。

3. EntityNotFoundException in Practice

3.EntityNotFoundException的实践

First, let’s cover one simpler use case. In the previous section, we mentioned the getReference method. We use this method to fetch a proxy of a specific entity. This proxy only has the primary key field initialized. When we call a getter on this proxy entity persistence provider initializes the rest of the fields. If the entity doesn’t exist in the database, then we get EntityNotFoundException:

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

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

User entity is elementary. It only has two fields and no relationships. We create a proxy entity with the primary key value 1L on the first line in the test. After that, we call getter on that proxy entity. The persistence provider tries to fetch entity by primary key, and since the record doesn’t exist, an EntityNotFoundException is thrown.

User实体是初级的。它只有两个字段,没有关系。我们在测试的第一行创建一个主键值为1L的代理实体。之后,我们在该代理实体上调用getter。持久性提供者试图通过主键来获取实体,由于记录不存在,所以抛出了一个EntityNotFoundException

For the next example, we’ll use different domain entities. We’ll create Item and Category entities with a bi-directional relationship between them:

对于下一个例子,我们将使用不同的域实体。我们将创建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
}

Note that we are using lazy fetch on @ManyToOne annotation. Also, we use @ForeignKey to remove the constraint from the database:

注意,我们在@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();
}

In this test, we fetch the Item entity by id. Method find will return an Item object without Category fully initialized since we use FetchType.LAZY (only the id is set, similar to the previous example). When we call getter on Category object persistence provider will try to fetch the object from the database, and we’ll get an exception since the record doesn’t exist.

在这个测试中,我们通过id来获取Item实体。方法find将返回一个没有Category完全初始化的Item对象,因为我们使用了FetchType.LAZY(只有id被设置,与前面的例子类似)。当我们对Category对象调用getter时,持久化提供者将尝试从数据库中获取该对象,我们将得到一个异常,因为该记录不存在。

@ManyToOne relationship assumes that referenced entity exists. Foreign keys and database integrity ensure that these entities exist. If this is not the case, there is a workaround to ignore the missing entity.

@ManyToOne关系假定被引用的实体存在。外键和数据库完整性确保这些实体存在。如果不是这种情况,有一个变通方法可以忽略缺少的实体。

Combining @NotFound(action = NotFoundAction.IGNORE) with @ManyToOne annotation will stop the persistence provider from throwing the EntityNotFoundException, but we’ll have to handle missing entity by hand to avoid NullPointerException.

@NotFound(action = NotFoundAction.IGNORE)@ManyToOneannotation结合起来将阻止持久化提供者抛出EntityNotFoundException但是我们必须手工处理缺失的实体以避免NullPointerException

4. Conclusion

4.总结

In this article, we covered in which situations can EntityNotFoundException occur and how we can handle it. First, we went over official documentation and covered usual use cases. After that, we cover more complex cases and how to fix this issue.

在这篇文章中,我们介绍了在哪些情况下会出现EntityNotFoundException以及如何处理它。首先,我们翻阅了官方文档,涵盖了通常的使用情况。之后,我们介绍了更复杂的情况以及如何解决这个问题。

As usual, we can find the code from this article over on GitHub.

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