Saltar al contenido

Cuando use los métodos getOne y findOne Spring Data JPA

Solución:

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 un entidad ansiosa de carga.

T getOne(ID id) se basa en EntityManager.getReference() que realiza un carga diferida de entidad. Entonces, para garantizar la carga efectiva de la entidad, se requiere invocar un método en ella.

findOne()/findById() es realmente más claro y fácil de usar que getOne().
Entonces, en la mayoría de los casos, favorezca findOne()/findById() sobre getOne().


Cambio de API

De al menos, el 2.0 versión, Spring-Data-Jpa modificado findOne().
Anteriormente, se definió en el CrudRepository interfaz como:

T findOne(ID primaryKey);

Ahora, el single findOne() método que encontrarás en CrudRepository es cual se define en el QueryByExampleExecutor interfaz como:

<S extends T> Optional<S> findOne(Example<S> example);

Eso es implementado finalmente por SimpleJpaRepository, la implementación predeterminada de la CrudRepository interfaz.
Este método es una consulta por búsqueda de ejemplo y no lo desea 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() para findById() en el CrudRepository interfaz :

Optional<T> findById(ID id); 

Ahora devuelve un Optional. Que no es tan malo prevenir NullPointerException.

Entonces, la elección real ahora está entre Optional<T> findById(ID id) y T getOne(ID id).


Dos métodos distintos que se basan en dos métodos de recuperación JPA EntityManager distintos

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 se basa en EntityManager.find() para hacer 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() es un EntityManager método declarado como:

public <T> T find(Class<T> entityClass, Object primaryKey,
                  Map<String, Object> properties);

Su javadoc dice:

Buscar por clave principal, usando las propiedades especificadas

Por lo tanto, parece esperado recuperar una entidad cargada.

2) Mientras que el T getOne(ID id) javadoc afirma (el énfasis es mío):

Devuelve un referencia a la entidad con el identificador dado.

De hecho, el referencia la terminología es realmente de tablero y la API de JPA no especifica ninguna getOne() método.
Entonces, lo mejor que puede hacer para comprender lo que hace el contenedor 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() es 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):

Obtener una instancia, cuyo estado se puede obtener de forma perezosa. Si la instancia solicitada no existe en la base de datos, se lanza EntityNotFoundException cuando se accede por primera vez al estado de la instancia. (El tiempo de ejecución del proveedor de persistencia puede lanzar EntityNotFoundException cuando se llama a getReference). La aplicación no debe esperar que el estado de la instancia esté disponible en el momento de la desconexión., a menos que la aplicación haya accedido mientras el administrador de la entidad estaba abierto.

Entonces, invocando getOne() puede devolver una entidad recuperada de forma perezosa.
Aquí, la búsqueda diferida 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, es posible que la entidad nunca se cargue y, por lo tanto, el resultado es realmente impredecible.
Por ejemplo, si el objeto proxy está serializado, podría obtener un null referencia como resultado serializado o si se invoca un método en el objeto proxy, una excepción como LazyInitializationException es aventado.
Entonces, en este tipo de situación, el lanzamiento de EntityNotFoundException esa es la razón principal para usar getOne() para manejar una instancia que no existe en la base de datos, ya que es posible que nunca se realice una situación de error mientras la entidad no exista.

En cualquier caso, para asegurar su carga hay que manipular la entidad mientras se abre la sesión. Puede hacerlo invocando cualquier método en la entidad.
O un mejor uso alternativo findById(ID id) en 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 para getOne() ? La carga diferida de entidades no es realmente un detalle.

  • por qué necesitas presentar getOne() envolver EM.getReference() ?
    ¿Por qué no seguir simplemente el método envuelto?getReference() ? Este método EM es realmente muy particular mientras getOne() transmite un procesamiento tan simple.

La diferencia básica es que getOne es vago cargado y findOne no es.

Considere el siguiente ejemplo:

public static String NON_EXISTING_ID = -1;
...
MyEntity getEnt = myEntityRepository.getOne(NON_EXISTING_ID);
MyEntity findEnt = myEntityRepository.findOne(NON_EXISTING_ID);

if(findEnt != null) {
     findEnt.getText(); // findEnt is null - this code is not executed
}

if(getEnt != null) {
     getEnt.getText(); // Throws exception - no data found, BUT getEnt is not null!!!
}

1. ¿Por qué falla el método getOne (id)?

Consulte esta sección en los documentos. Si anula la transacción que ya se ha realizado, es posible que esté causando el problema. Sin embargo, sin más información, este es difícil de responder.

2. ¿Cuándo debo usar el método getOne (id)?

Sin profundizar en los aspectos internos de Spring Data JPA, la diferencia parece estar en el mecanismo utilizado para recuperar la entidad.

Si miras el JavaDoc para getOne(ID) debajo Ver también:

See Also:
EntityManager.getReference(Class, Object)

parece que este método simplemente delega en la implementación del administrador de la entidad JPA.

Sin embargo, los documentos para findOne(ID) no menciones esto.

La pista también está en los nombres de los repositorios.
JpaRepository es específico de JPA y, por lo tanto, puede delegar llamadas al administrador de la entidad si es necesario.
CrudRepository es independiente de la tecnología de persistencia utilizada. Mira aquí. Se utiliza como una interfaz de marcador para múltiples tecnologías de persistencia como JPA, Neo4J, etc.

Entonces, realmente no hay una ‘diferencia’ en los dos métodos para sus casos de uso, es solo que findOne(ID) es más genérico que el más especializado getOne(ID). El que uses depende de ti y de tu proyecto, pero yo personalmente me ceñiría a las findOne(ID) ya que hace que su código sea menos específico de implementación y abre las puertas para pasar a cosas como MongoDB, etc.en el futuro sin demasiada refactorización 🙂

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *