TL; DR
T findOne(ID id)
(nome na API antiga) / Optional<T> findById(ID id)
(nome na nova API) conta com EntityManager.find()
a execução de um carregamento ansioso da entidade .
T getOne(ID id)
depende EntityManager.getReference()
que executa um carregamento lento da entidade . Portanto, para garantir o carregamento efetivo da entidade, é necessário invocar um método.
findOne()/findById()
é realmente mais claro e simples de usar do que getOne()
.
Portanto, mesmo no mais dos casos, favorecem findOne()/findById()
mais getOne()
.
Alteração de API
Pelo menos, a 2.0
versão, Spring-Data-Jpa
modificada findOne()
.
Anteriormente, era definido na CrudRepository
interface como:
T findOne(ID primaryKey);
Agora, o findOne()
método único em que você encontrará CrudRepository
é aquele definido na QueryByExampleExecutor
interface como:
<S extends T> Optional<S> findOne(Example<S> example);
Finalmente SimpleJpaRepository
, isso é implementado pela implementação padrão da CrudRepository
interface.
Este método é uma consulta por exemplo e você não deseja substituí-lo.
De fato, o método com o mesmo comportamento ainda está lá na nova API, mas o nome do método foi alterado.
Foi renomeado de findOne()
para findById()
na CrudRepository
interface:
Optional<T> findById(ID id);
Agora ele retorna um Optional
. O que não é tão ruim de prevenir NullPointerException
.
Portanto, a escolha real é agora entre Optional<T> findById(ID id)
e T getOne(ID id)
.
Dois métodos distintos que se baseiam em dois métodos distintos de recuperação do JPA EntityManager
1) O Optional<T> findById(ID id)
javadoc afirma que:
Recupera uma entidade pelo seu ID.
À medida que analisamos a implementação, podemos ver que ela depende EntityManager.find()
da recuperação:
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));
}
E aqui em.find()
está um EntityManager
método declarado como:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Seu javadoc declara:
Localizar por chave primária, usando as propriedades especificadas
Portanto, a recuperação de uma entidade carregada parece esperada.
2) Enquanto o T getOne(ID id)
javadoc declara (a ênfase é minha):
Retorna uma referência para a entidade com o identificador fornecido.
De fato, a referência terminologia de é realmente válida e a API JPA não especifica nenhum getOne()
método.
Portanto, a melhor coisa a fazer para entender o que o wrapper Spring faz é examinar a implementação:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Aqui em.getReference()
está um EntityManager
método declarado como:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
Felizmente, o EntityManager
javadoc definiu melhor sua intenção (a ênfase é minha):
Obtenha uma instância, cujo estado pode ser buscado preguiçosamente . Se a instância solicitada não existir no banco de dados, a EntityNotFoundException será lançada quando o estado da instância for acessado pela primeira vez. . (O tempo de execução do provedor de persistência tem permissão para lançar EntityNotFoundException quando getReference é chamado.) O aplicativo não deve esperar que o estado da instância esteja disponível no momento do desanexamento , a menos que tenha sido acessado pelo aplicativo enquanto o gerenciador de entidades estava aberto.
Então, invocando getOne()
pode retornar uma entidade buscada preguiçosamente.
Aqui, a busca preguiçosa não se refere aos relacionamentos da entidade, mas à própria entidade.
Isso significa que, se invocarmos getOne()
e o contexto de persistência for fechado, a entidade poderá nunca ser carregada e, portanto, o resultado será realmente imprevisível.
Por exemplo, se o objeto proxy for serializado, você poderá obter uma null
referência como resultado serializado ou se um método for chamado no objeto proxy, uma exceção como a LazyInitializationException
lançada.
Portanto, nesse tipo de situação, o lance EntityNotFoundException
disso é o principal motivo para getOne()
lidar com uma instância que não existe no banco de dados, pois uma situação de erro nunca pode ser executada enquanto a entidade não existe.
De qualquer forma, para garantir seu carregamento, você precisa manipular a entidade enquanto a sessão é aberta. Você pode fazer isso invocando qualquer método na entidade.
Ou um uso alternativo melhorfindById(ID id)
vez de.
Por que uma API tão obscura?
Para finalizar, duas perguntas para os desenvolvedores Spring-Data-JPA:
por que não ter uma documentação mais clara getOne()
? O carregamento lento da entidade não é realmente um detalhe.
por que você precisa apresentar getOne()
para embrulhar EM.getReference()
?
Por que não seguir simplesmente o método embrulhado getReference()
:? Esse método EM é realmente muito particular enquanto getOne()
transmite um processamento tão simples.