Tecnologia

Hibernate, @OneToOne e como conseguir (ou não) um LAZY

Por: , agosto 26, 2013


Olá pessoal!
Descobri algo interessante e que pode servir de alerta a quem está em algum projeto com JPA/Hibernate, principalmente se estiver preocupado com desempenho. O alerta gira em torno do OneToOne com fetch LAZY, que nem sempre funciona como esperamos… O que acontece? Veja a seguir.
Estava eu fazendo alguns exemplos para o curso de JPA da Dextraining, de forma a explicar para os alunos como o LAZY funciona dentre os vários relacionamentos do JPA: OneToMany, ManyToOne, ManyToMany e o OneToOne. Este último, que deveria ser o mais simples em matéria de fetch com LAZY, mostrou-se um pouco mais nebuloso do que eu imaginava.

Tudo começa quando criei um exemplo OneToOne. Tome como exemplo 2 entidades: Pessoa e Endereco, relacionamento unidirecional.
public class Pessoa {
@OneToOne(fetch = FetchType.LAZY)
private Endereco endereco;
}

Aqui, sem problemas, o LAZY funciona como esperado. Mas as coisas começam a ficar interessantes quando tentamos mapear o relacionamento do lado do Endereco, usando um mappedBy no OneToOne da entidade Pessoa para um campo “pessoa” presente no Endereco (relacionamento bidirecional):
public class Pessoa {
@OneToOne(fetch = FetchType.LAZY, mappedBy="pessoa", optional = true)
private Endereco endereco;
}

public class Endereco {
@OneToOne
private Pessoa pessoa;
}
Se colocarmos o fetch como LAZY neste relacionamento, o Hibernate ignora: sempre que buscamos a pessoa, vemos um novo SELECT para retornar a informação de Endereco, ou seja, o EAGER atuando. Por que isto ocorre?
Para entendermos, temos que entender como o hibernate faz o LAZY. Em termos gerais, este processo ocorre por meio de um objeto “proxy” do hibernate, colocado em cada relacionamento marcado como LAZY nas entidades. Este objeto proxy fica preparado para ser acionado quando existe uma marcação LAZY no relacionamento, para buscar a informação no banco de dados apenas quando solicitado. Mas, como veremos a seguir, o Hibernate parece não saber o que fazer direito com o objeto proxy em determinados casos.
Do caso que estamos falando, quando uma Pessoa é buscada, o Hibernate não tem certeza se aquele registro de Pessoa tem algum relacionamento existente com algum Endereco, uma vez que a Pessoa tem um relacionamento opcional com Endereco e a chave estrangeira não está na Pessoa. Uma explicação dada, sendo B nosso Pessoa e C nosso Endereco, é a seguinte:
But now imagine your B object may or may not have associated C (constrained=”false”). What should getCee() return when specific B does not have C? Null. But remember, Hibernate must set correct value of “cee” at the moment it set B (because it does no know when someone will call getCee()). Proxy does not help here because proxy itself in already non-null object.
Esta explicação, por si só, não convence muito se quiser entender o motivo de estar funcionando assim. É difícil entender porque o hibernate simplesmente não consegue tratar este caso, tentando resolver o problema do proxy quando ocorre o “get” do Endereco.



Isto também é questionado por outro usuário:
A one to one relationship is represented in the database by a foreign key. If this foreign key is null, Hibernate could set the value null in the attribute at the moment it loads the object from the DB. Else, if the foreign key is not null, Hiberante could insert a proxy in the attribute and realize a lazy loading.
The foreign key could naturally be in the other table. In this case Hibernate could make the same test just by accessing the index of the othe table, which would be less work than accessing the whole record, especially if the other table contains large fields like blobs.
Mas o hibernate não faz algo parecido.
E as coisas ficam piores. Estava conversando com o Ivan que notei este comportamento mesmo quando o relacionamento não era opcional! Exemplo:
public class Pessoa {
@OneToOne(fetch = FetchType.LAZY, mappedBy="pessoa", optional = false)
private Endereco endereco;
}

Antes mesmo de recuperar o exemplo para mostrar isto ocorrendo, me deparei com o comentário de outro desenvolvedor:
So,if I keep optional parameter as false, so that  a not-null relationship should always exist, then Lazy loading should work.. But, it’s still not working.
Mesmo assim, é possível fazer seu relacionamento OneToOne funcionar como gostaria. Só que não é tão simples quanto colocar um fetch = LAZY e cada solução existente tem prós, contras e efeitos colaterais.
O que consigo concluir é que, por algum detalhe na implementação do Hibernate, o OneToOne com LAZY é mais complexo do que teoricamente precisaria ser. Para conferirem toda a discussão e as soluções apresentadas, sigam os links:
http://justonjava.blogspot.com.br/2010/09/lazy-one-to-one-and-one-to-many.html
https://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one

  • Receba nosso conteúdo em primeira mão.