Costumo usar o Hibernate em combinação com o framework Spring e seus recursos de demarcação de transação declarativa (por exemplo, @Transactional ).
Como todos sabemos, o Hibernate tenta ser o mais não invasivo e transparente possível, mas isso se mostra um pouco mais desafiador quando se trata de lazy-loaded
relacionamentos.
Vejo uma série de alternativas de design com diferentes níveis de transparência.
- Faça relacionamentos não carregados lentamente (por exemplo,
fetchType=FetchType.EAGER)
- Isso viola toda a ideia de carregamento lento.
- Inicializar coleções usando
Hibernate.initialize(proxyObj);
- Isso implica um acoplamento relativamente alto ao DAO
- Embora possamos definir uma interface com
initialize
, outras implementações não têm garantia de fornecer qualquer equivalente.
- Adicione o comportamento da transação aos
Model
próprios objetos persistentes (usando proxy dinâmico ou@Transactional
)- Não tentei a abordagem de proxy dinâmico, embora nunca tenha parecido fazer o @Transactional funcionar nos próprios objetos persistentes. Provavelmente devido a essa hibernação é uma operação em um proxy para estar.
- Perda de controle quando as transações estão realmente ocorrendo
- Fornece API preguiçoso / não preguiçoso, por exemplo,
loadData()
eloadDataWithDeps()
- Força o aplicativo a saber quando empregar qual rotina, novamente o acoplamento forte
- Estouro de método,,
loadDataWithA()
....,loadDataWithX()
- Força a procura de dependências, por exemplo, fornecendo apenas
byId()
operações- Requer muitas rotinas não orientadas a objetos, por exemplo,,
findZzzById(zid)
e então emgetYyyIds(zid)
vez dez.getY()
- Pode ser útil buscar cada objeto em uma coleção, um por um, se houver uma grande sobrecarga de processamento entre as transações.
- Requer muitas rotinas não orientadas a objetos, por exemplo,,
- Faça parte da aplicação @Transactional em vez de apenas DAO
- Possíveis considerações de transações aninhadas
- Requer rotinas adaptadas para gerenciamento de transações (por exemplo, suficientemente pequeno)
- Pequeno impacto programático, embora possa resultar em grandes transações
- Fornecer ao DAO perfis de busca dinâmica , por exemplo,
loadData(id, fetchProfile);
- Os aplicativos devem saber qual perfil usar quando
- Tipo de transação AoP, por exemplo, interceptar operações e realizar transações quando necessário
- Requer manipulação de código de byte ou uso de proxy
- Perda de controle quando as transações são realizadas
- Magia negra, como sempre :)
Eu perdi alguma opção?
Qual é a sua abordagem preferida ao tentar minimizar o impacto dos lazy-loaded
relacionamentos no design de seu aplicativo?
(Oh, e desculpe por WoT )
java
hibernate
spring
lazy-loading
application-design
Johan Sjöberg
fonte
fonte
Respostas:
Eu diria que a suposição inicial está errada. A persistência transaparente é um mito, pois a aplicação deve sempre cuidar do ciclo de vida da entidade e do tamanho do gráfico do objeto que está sendo carregado.
Note que o Hibernate não pode ler pensamentos, portanto, se você sabe que precisa de um conjunto particular de dependências para uma operação particular, você precisa expressar suas intenções de Hibernar de alguma forma.
Deste ponto de vista, as soluções que expressam essas intenções explicitamente (nomeadamente, 2, 4 e 7) parecem razoáveis e não sofrem de falta de transparência.
fonte
Não tenho certeza de qual problema (causado pela preguiça) você está sugerindo, mas para mim o maior problema é evitar a perda do contexto da sessão em meus próprios caches de aplicativo. Caso típico:
foo
é carregado e colocado em um mapa;foo.getBar()
(algo que nunca foi chamado antes e é avaliado lentamente);Portanto, para resolver isso, temos uma série de regras:
OpenSessionInViewFilter
para aplicativos da web);try/finally
) para que as subclasses não precisem pensar nisso;Como você pode ver, isso está longe de ser não invasivo e transparente . Mas o custo ainda é suportável, para comparar com o preço que eu pagaria para carregar antecipadamente. O problema com o último é que às vezes leva ao efeito borboleta ao carregar um único objeto referenciado, quanto mais uma coleção de entidades. Consumo de memória, uso de CPU e latência para mencionar o mínimo também são muito piores, então acho que posso viver com isso.
fonte
transparency
é forçar o aplicativo a se preocupar com o carregamento de objetos preguiçosos. Se tudo fosse buscado avidamente, o aplicativo poderia estar completamente inconsciente se os objetos são persistidos em um banco de dados ou não, jáFoo.getBar()
que sempre terá sucesso. >when passing objects between threads, pass IDs
, sim, isso corresponderia a # 5.Um padrão muito comum é usar OpenEntityManagerInViewFilter se você estiver construindo um aplicativo da web.
Se você estiver construindo um serviço, eu abriria o TX no método público do serviço, em vez de nos DAOs, como muitas vezes um método requer para obter ou atualizar várias entidades.
Isso resolverá qualquer "exceção de carregamento lento". Se você precisa de algo mais avançado para ajuste de desempenho, acho que buscar perfis é o caminho a percorrer.
fonte
OSIV
ainda é um antipadrão e leva a problemas muito sérios, como incapacidade de lidar com exceções ou degradação de desempenho. Resumindo: IMHO OSIV é uma solução fácil de usar, mas boa apenas para projetos de brinquedo.