Como carga preguiçoso um one-to-one composição via HQL
-
12-09-2019 - |
Pergunta
Se tiver uma entidade A com um mapeamento bidirecional um-ou-zero-para-um com entidade B.
O mapeamento é o seguinte:
<class name="EntityA" table="TABLE_A" mutable="true" lazy="true">
<id name="idA" type="long" column="pk_a" unsaved-value="null">
<generator class="sequence">
<param name="sequence">pk_a_seq</param>
</generator>
</id>
<one-to-one name="propertyB" class="EntityB" property-ref="propertyA" constrained="true" outer-join="false"/>
</class>
e
<class name="EntityB" table="TABLE_B" mutable="true" lazy="true">
<id name="idB" type="long" column="pk_b" unsaved-value="null">
<generator class="sequence">
<param name="sequence">pk_b_seq</param>
</generator>
</id>
<many-to-one name="propertyA" class="EntityA" not-null="true" unique="true" lazy="proxy" column="fk_a"/>
</class>
Quando eu faço uma consulta HQL (ou melhor, uma consulta HQL nomeada) para EntityA, hibernate ansiosamente cargas EntityA # propertyB com uma instrução SELECT separado.
O meu problema é que, se os meus HQL retornos 1000 EntityA do (com todos os ter seu próprio respectivo EntityB), hibernate fará n + 1 consultas (1ª consulta seria para EntityA retornar 1000 resultados, enquanto os n perguntas seriam provenientes de o EntityA # propertyB selecionar carregamento lento).
No entanto, eu não preciso aqueles EntityA do # é por isso que eu quero carregar preguiçoso-los em vez (sem ter de hibernação usar uma consulta SQL separado) propertyB.
Isso é possível? E se é, como eu faço isso?
Obrigado, Franz
Solução
Eu fixo este problema.
O que eu fiz foi criar transformar o campo EntityA # propertyB em um conjunto com o nome EntityA # propertyBs. Mas eu manteve o EntityA # getPropertyB () e EntityA # setPropertyB (EntityB propertyB) métodos de acesso.
Os corpos de método desses métodos de acesso são agora algo como isto:
public EntityB getPropertyB() {
return CollectionUtils.get(propertyBs, 0);
}
public void setPropertyBs(EntityB propertyB) {
propertyBs= Collections.singleton(propertyB);
}
Em seguida, no meu mapeamento, eu mapeado o conjunto EntityA # propertyBs e especificar o acesso ao 'campo'.
<set name="scheduledAdInfos" lazy="true" fetch="subselect" access="field" cascade="none" inverse="true">
<key column="pk_a"/>
<one-to-many class="EntityB"/>
</set>
Com esta configuração, você pode agora criar um mapeamento preguiçoso do POJO possuir (EntityA) para o POJO propriedade (EntityB) mesmo se Table_A é propriedade de table_b.
Outras dicas
Resposta curta: Não, você não pode fazer isso, pelo menos não sem alterar o banco de dados eo mapeamento. Você basicamente tem que reverter o one-to-one mapeamento ea relação de chave estrangeira para o trabalho da maneira que você quiser.
resposta mais longa: Hibernate pode associações de carga preguiçosos. A forma como ele faz isso é através da injeção de um objeto proxy que contém o ID do objeto referenciado.
No seu caso, o mapeamento é tal que a coluna de chave estrangeira está em table_b, isto é, onde você usa o mapeamento muitos-para-um. Então, se você carregar um B, hibernate encontra a referência FK na coluna fk_a e pode criar um proxy que mantém este valor. Quando o proxy é acessado a entidade correspondente é carregado.
E se um registro da tabela A é selecionado? Hibenate irá criar um objeto A, mas para ser capaz de preencher o propertyB, ele vai ter que olhar para o table_b, para encontrar a linha correspondente com fk_a = a.id. Não há outro caminho para Hibernate para descobrir o que registro a carga em tempo de carregamento lento.
Na verdade, esta seria uma melhoria para o Hibernate, uma vez que também deve ser capaz de fazer o carregamento em outras chaves exclusivas, enquanto o carregamento lento, mas a implementação atual não permite isso, talvez você pode levantar um problema.