Skip to content

Repository

Pavel Fomin edited this page Jan 2, 2020 · 3 revisions

Spring Data JPA repository

Spring Data JPA repository getOne() method

Starting with Spring Boot 2.0, the repository method findOne(id) was changed to getOne(id). I've noticed that when I converted my code from spring boot 1.5 to 2.0. However, what I did not notice was that not only the name of the method changed but its implementation changed very significantly. I've created a handful of the integration tests to show what is going on.

Spring boot 2.0+ getOne(id) implementation is based on EntityManager.getReference(getDomainClass(), id) but its java doc for is not very clear:

/**
 * Returns a reference to the entity with the given identifier.
 *
 * @param id must not be {@literal null}.
 * @return a reference to the entity with the given identifier.
 * @see EntityManager#getReference(Class, Object)
 * @throws javax.persistence.EntityNotFoundException if no entity exists for given {@code id}.
 */
Spring boot 2.0+ getOne(id) implementation based on em.getReference(getDomainClass(), id):/*
 * (non-Javadoc)
 * @see org.springframework.data.jpa.repository.JpaRepository#getOne(java.io.Serializable)
 */
@Override
public T getOne(ID id) {

 Assert.notNull(id, ID_MUST_NOT_BE_NULL);
 return em.getReference(getDomainClass(), id);
}

The exception is not thrown until the "state" of the entity is accessed. According to my tests that means calling any getter other than getId()!

Java doc for EntityManager method <T> T getReference(Class<T> entityClass, Object primaryKey) is a little better:

/**
 * Get an instance, whose state may be lazily fetched.
 * If the requested instance does not exist in the database,
 * the <code>EntityNotFoundException</code> is thrown when the instance
 * state is first accessed. (The persistence provider runtime is
 * permitted to throw the <code>EntityNotFoundException</code> when
 * <code>getReference</code> is called.)
 * The application should not expect that the instance state will
 * be available upon detachment, unless it was accessed by the
 * application while the entity manager was open.
 * @param entityClass entity class
 * @param primaryKey primary key
 * @return the found entity instance
 * @throws IllegalArgumentException if the first argument does
 * not denote an entity type or the second argument is
 * not a valid type for that entity�s primary key or
 * is null
 * @throws EntityNotFoundException if the entity state
 * cannot be accessed
 */
public <T> T getReference(Class<T> entityClass,
 Object primaryKey);

But it still would imply that getId() should throw EntityNotFoundException since I consider it to be part of the entity state.

Spring boot 1.5 findOne(id) implementation was based on EntityManager.find(id):

/*
 * (non-Javadoc)
 * @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable)
 */
public T findOne(ID id) {

 Assert.notNull(id, ID_MUST_NOT_BE_NULL);

 Class<T> domainType = getDomainClass();

 if (metadata == null) {
 return em.find(domainType, id);
 }

 LockModeType type = metadata.getLockModeType();

 Map<String, Object> hints = getQueryHints();

 return type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints);
}

At a bare minimum, be very careful when using entityManager.getReference() in your code. The proxy reference returned in not very easy to work with and might be error prone:

  • it's not null but not pointing to an existing entity
  • getId() would return id passed in as a primary key
  • access to any other getters will throw EntityNotFoundException
Clone this wiki locally