TL; DR
T findOne(ID id)
(nombre en la antigua API) / Optional<T> findById(ID id)
(nombre en la nueva API) se basa en EntityManager.find()
que realiza una carga ansiosa de la entidad .
T getOne(ID id)
se basa en EntityManager.getReference()
que realiza una entidad de carga diferida . Por lo tanto, para garantizar la carga efectiva de la entidad, se requiere invocar un método.
findOne()/findById()
es realmente más claro y fácil de usar que getOne()
.
Así que en el mismo la mayor parte de los casos, favorecen findOne()/findById()
más getOne()
.
Cambio de API
De al menos, la 2.0
versión, Spring-Data-Jpa
modificada findOne()
.
Anteriormente, se definía en la CrudRepository
interfaz como:
T findOne(ID primaryKey);
Ahora, el único findOne()
método que encontrará CrudRepository
es el que se define en la QueryByExampleExecutor
interfaz como:
<S extends T> Optional<S> findOne(Example<S> example);
Eso se implementa finalmente mediante SimpleJpaRepository
la implementación predeterminada de la CrudRepository
interfaz.
Este método es una búsqueda por ejemplo de búsqueda y no desea hacerlo como reemplazo.
De hecho, el método con el mismo comportamiento todavía está allí en la nueva API pero el nombre del método ha cambiado.
Fue renombrado de findOne()
a findById()
en la CrudRepository
interfaz:
Optional<T> findById(ID id);
Ahora devuelve un Optional
. Lo cual no es tan malo de prevenir NullPointerException
.
Entonces, la elección real es ahora entre Optional<T> findById(ID id)
y T getOne(ID id)
.
Dos métodos distintos que se basan en dos métodos distintos de recuperación de JPA EntityManager
1) El Optional<T> findById(ID id)
javadoc afirma que:
Recupera una entidad por su id.
A medida que analizamos la implementación, podemos ver que depende de EntityManager.find()
la recuperación:
public Optional<T> findById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
Class<T> domainType = getDomainClass();
if (metadata == null) {
return Optional.ofNullable(em.find(domainType, id));
}
LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
}
Y aquí em.find()
hay un EntityManager
método declarado como:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Sus estados javadoc:
Buscar por clave principal, utilizando las propiedades especificadas
Entonces, recuperar una entidad cargada parece esperado.
2) Mientras que los estados T getOne(ID id)
javadoc (el énfasis es mío):
Devuelve una referencia a la entidad con el identificador dado.
De hecho, la terminología de referencia es realmente la placa y JPA API no especifica ningún getOne()
método.
Entonces, lo mejor que puede hacer para comprender lo que hace el contenedor de Spring es investigar la implementación:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Aquí em.getReference()
hay un EntityManager
método declarado como:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
Y afortunadamente, el EntityManager
javadoc definió mejor su intención (el énfasis es mío):
Obtenga una instancia, cuyo estado se pueda obtener perezosamente . Si la instancia solicitada no existe en la base de datos, se emite la EntityNotFoundException cuando se accede por primera vez al estado de la instancia . (Se permite que el tiempo de ejecución del proveedor de persistencia arroje la EntityNotFoundException cuando se llama a getReference). La aplicación no debe esperar que el estado de la instancia esté disponible en la separación , a menos que la aplicación haya accedido mientras el administrador de la entidad estaba abierto.
Por lo tanto, invocar getOne()
puede devolver una entidad perezosamente obtenida.
Aquí, la recuperación perezosa no se refiere a las relaciones de la entidad, sino a la entidad misma.
Significa que si invocamos getOne()
y luego se cierra el contexto de persistencia, la entidad nunca se cargará y, por lo tanto, el resultado es realmente impredecible.
Por ejemplo, si el objeto proxy se serializa, puede obtener una null
referencia como resultado serializado o si se invoca un método en el objeto proxy, se LazyInitializationException
produce una excepción como la que se produce.
Entonces, en este tipo de situación, el lanzamiento de EntityNotFoundException
eso es la razón principal para usar getOne()
para manejar una instancia que no existe en la base de datos, ya que una situación de error nunca se puede realizar mientras la entidad no existe.
En cualquier caso, para garantizar su carga, debe manipular la entidad mientras se abre la sesión. Puede hacerlo invocando cualquier método en la entidad.
O un mejor uso alternativo en findById(ID id)
lugar de.
¿Por qué una API tan poco clara?
Para finalizar, dos preguntas para los desarrolladores de Spring-Data-JPA:
¿Por qué no tener una documentación más clara getOne()
? La carga diferida de la entidad realmente no es un detalle.
¿Por qué necesitas presentar getOne()
para envolver EM.getReference()
?
¿Por qué no simplemente apegarse al método envuelto getReference()
:? Este método EM es realmente muy particular mientras getOne()
transmite un procesamiento tan simple.