Persistence top

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE

1. Introduction

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.

2. When Is the EntityNotFoundException Thrown?

Oracle documentation for this exception defines three situations in which the persistence provider can throw the 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.

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.

3. EntityNotFoundException in Practice

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:

@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.

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

@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:

@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.

@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.

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.

4. Conclusion

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.

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

Persistence bottom
Get started with Spring Data JPA through the reference Learn Spring Data JPA course: >> CHECK OUT THE COURSE
Persistence footer banner
Comments are closed on this article!