Boas práticas com o Lazy Loading

Boa tarde.

Peguei um projeto já iniciado utilizando Hibernate, tenho trabalho há algum tempo e vinha notando que o fetch = FetchType.LAZY, não era respeitado, exceto quando combinado com optional = false.

Comecei a desenvolver um processo no sistema que gravava por volta de 200 registros, mas notei uma certa lentidão, como sempre notei no restante do sistema e outros processos.

Ativei o log de queries para dar uma olhada e reparei que para cada Item, eram feitas 40 consultas, lotadas de inners e left joins, apesar dos mesmos estarem Lazy.

Então pesquisando na internet encontrei sobre a propriedade:

<property name="hibernate.enable_lazy_load_no_trans" value="true"/>

Verifiquei e no projeto que eu estou trabalhando, o antigo desenvolvedor setou está propriedade.

Fiz o teste retirando a mesma e várias telas pararam de funcionar por conta das relações que não eram mais carregadas por padrão, até ai normal. Fiz o teste fazendo o JOIN FETCH do que precisava e funcionou normalmente, diminuindo drasticamente o número de queries.

A pergunta é, qual seria a boa prática para essa questão? Vejo que o enable_lazy_load_no_trans ele remove o funcionamento do Lazy, gostaria de confirmar isso é realmente uma péssima prática?

Mesmo retirando esta propriedade, os @OneToOne continuam carregando por default, mesmo com Lazy. Há alguma maneira de evitar isso? Vi algo sobre PersistentAttributeInterceptable, seria o caminho?

Obrigado desde já.

Perfeito, é isso que você deve fazer mesmo se usar Hibernate. Deixa o lazy só no mapeamento mas faz o fetch nas queries.

Lazy é um recurso inútil para aplicações web. O ideal seria não usar Hibernate, que possui excesso de engenharia para atender os resultados.

O ideal seria não usar Hibernate, que possui excesso de engenharia para atender os resultados.

Você diz trabalhar com native queries?

Sim, é mais eficiente e fica sob controle do profissional. Dá uma pesquisada sobre JDBCTemplate, é um meio termo entre JDBC puro com ORM, mas totalmente baseado em query SQL e sem as gorduras do hibernate, que não fazem mais sentido para aplicações atuais.

Certo, vou dar uma olhada no JDBCTemplate.

Apenas para concluir, essa decisão da utilização de JDBCTemplate não depende exclusivamente de mim, se tiver de optar por continuar com o Hibernate. A melhor estratégia seria utilizar tudo Lazy e sempre trabalhar com os join fetchs a nível de query, correto?

Exato, HQL com join fetch é a forma mais profissional de trabalhar com Hibernate, gerando dentro do possível uma única query para o resultado. Nunca fique gerando queries através do acesso aos atributos de forma preguiçosa, literalmente.

Entendi, perfeito.

Obrigado pelas dicas, vou dar uma olhada no JDBCTemplate, acredito que trabalhar com ambos no mesmo projeto não tenha problema.

Com Hibernate você também pode trabalhar com SQL, mas mesmo assim ainda existe o consumo da fábrica de sessões do Hibernate. Enfim, basta trabalhar com HQL para sair do caos.

Seria essa a ideia né:

List<Object[]> acumuladosMps = getEntityManager().createNativeQuery("SELECT * FROM acumulados_materias_primas WHERE id = :id AND acumulado_data = :acumulado_data")
            .setParameter("id", item.getId())
            .setParameter("acumulado_data", new Date())
            .getResultList();

Por exemplo esta mesma consulta feita com NamedQuery chega a dar uma travada quando faço o debug.

Seria essa a opção?

Com SQL sim e também pode mapear para uma entidade. Mas como está adotando Hibernate, o normal é usar HQL, e SQL para queries complexas, principalmente relatórios.

Prefiro SQL sem Hibernate, mas trabalhar com Hibernate 100% via SQL está carregando uma mochila com chumbo para não usar, meio que perde o sentido.