How to avoid LazyExceptions?

3

My application faces numerous LazyExceptions issues and what causes a "filth" in the server log. I understand that they occur because the connection to the database has already been closed and I am trying to recover some attribute / object that has not been initialized.

The most recent problem I had was getting a LazyException in the filter because the ProductValue merchants were not initialized. These occur only in a specific system flow.

Service

List<ProdutoValorETO> valores = mgrProdutoValorEDAO.findByProdutoId(produto.getId());
Optional<ProdutoValorETO> optionalProdValor = valores.stream()
                        .filter(v -> v.getLojista().getId().equals(lojista.getId()))
                        .findFirst();

DAO

public List<ProdutoValorETO> findByProdutoId(Long id) {
        if (id != null) {
            String query =
                    " SELECT * " +
                            " FROM produtovalor " +
                            " WHERE idproduto = " + id + " AND ativo = TRUE; ";
            SQLQuery eQuery = getCurrentSession().createSQLQuery(query).addEntity(ProdutoValorETO.class);
            List<ProdutoValorETO> lista = CastUtils.castList(eQuery.list(), ProdutoValorETO.class);
        }
        return new ArrayList<>();
    }

I solved it as follows but it seems more like a workaround:

public List<ProdutoValorETO> findByProdutoId(Long id) {
        if (id != null) {
            String query =
                    " SELECT * " +
                            " FROM produtovalor " +
                            " WHERE idproduto = " + id + " AND ativo = TRUE; ";
            SQLQuery eQuery = getCurrentSession().createSQLQuery(query).addEntity(ProdutoValorETO.class);
            List<ProdutoValorETO> lista = CastUtils.castList(eQuery.list(), ProdutoValorETO.class);
            for (ProdutoValorETO item : lista) {
                Hibernate.initialize(item);
            }
        }
        return new ArrayList<>();
    }

Setup                

<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
    <property name="poolName" value="springHikariCP" />
    <property name="dataSourceClassName" value="org.postgresql.ds.PGSimpleDataSource" />
    <property name="leakDetectionThreshold" value="30000"/>
    <property name="connectionTimeout" value="30000" />
    <property name="idleTimeout" value="600000"/>
    <property name="maxLifetime" value="1800000"/>
    <property name="maximumPoolSize" value="5"/>
    <property name="registerMbeans" value="true"/>
    <!-- <property name="jdbcUrl" value="${jdbc.url}"/> -->
    <property name="connectionTestQuery" value="select 1"/>

    <property name="dataSourceProperties">
        <props>
            <prop key="url">${jdbc.url}</prop>
            <prop key="user">${jdbc.username}</prop>
            <prop key="password">${jdbc.password}</prop>
        </props>
    </property>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="br.com.example.*.to"/>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.show_sql">false</prop>
            <prop key="hibernate.cache.use_query_cache">true</prop>
            <prop key="hibernate.cache.use_second_level_cache">true</prop>
            <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
        </props>
    </property>
</bean>

ProductValorETO

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "idProduto")
    private ProdutoETO produto;

Product

    @OneToMany(mappedBy = "produto", targetEntity = ProdutoValorETO.class, cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
    private List<ProdutoValorETO> valores;
  • When we perform a very large flow in the system, is it more likely that a Lazy will occur? I got the list of values one line above when I try to access the items and I already got the exception.
  • Is there any mechanism in Spring JPA or new versions of Hibernate to handle this better? (This application uses Spring 3 Hibernate 3.2)
  • Is there any implication in Lazy occurring when writing direct select instead of using Hibernate criteria?
  • Are there better ways to resolve this (other than Eager)?
  • asked by anonymous 26.01.2018 / 19:49

    2 answers

    0
      

    When we perform a very large flow in the system, it is more likely   will a Lazy Happen? I got the list of values one line above   when I try to access the items and I already got the exception.

    Yes, if you are using non-transaction entities.

    I think you can organize your code in a way that entities are not used out of a transaction, avoiding the error. You can achieve this by mapping the information to other classes (DTOs) that are thus out of the transaction, being used to populate screens, reports, etc.

    Every application I saw with many LazyException problems was because the entities were used out of the transaction. I consider this a practice to be avoided for reasons other than LazyException.

      

    Is there a mechanism in Spring JPA or in new versions of Hibernate   to treat this better? (This application uses Spring 3 Hibernate 3.2)

    There are resources to help you have information already without relying on the open transaction: EAGER (used in the relationship, but do not recommend ) and FETCH (used in the query).

      

    There is some implication that Lazy occurs when writing direct to the select   instead of using Hibernate criteria?

    Not that I know. Equivalent queries in JPQL and Criteria should generate the same SQL.

      

    Are there better ways to resolve this (other than Eager)?

    Yes, using FETCH :

    SELECT pv
    FROM produtovalor pv
    JOIN FETCH pv.lojista 
    WHERE pv.idproduto = :id AND pv.ativo = TRUE;
    
        
    29.01.2018 / 15:13
    0
      

    How to avoid LazyExceptions?

    Unfortunately there is no ready answer to this question, the solution always depends on the context.

      
  •   
  • LazyException is always thrown when you try to access a collection that is not "loaded" and the session has been closed. What may be happening is that when you do a search, the default constructor of the entity is fired (you can put a breakpoint in the constructor to verify that fact), maybe in the constructor you are already trying to access the collection, or some other place, which you still can not see.

      
  • Is there any mechanism in Spring JPA or new versions of Hibernate to handle this better? (This application uses Spring 3 Hibernate 3.2)
  •   

    Considering that Spring 4 has already been consolidated in the market for a long time, and that Spring 5 has just been released, and although Hibernate is currently in version 5.x, I would say that an update would be very welcome from technological point of view, but in legacy systems this is not always possible. But the question of LazyException is not directly related to the versions of the frameworks, we can say that LazyExection would be a defense mechanism to avoid an overload, the framework wants you to only load the collections when it is really needed , and this is the secret to solving the problem.

      
  • Is there any implication that Lazy occurs when writing direct select instead of using Hibernate criteria?
  •   

    Hibernate is an implementation of JPA, so when you "write the SQL directly into the code" using a native query, that query is still managed by the application's persistence layer, notice in your code that you requested the current session and then requested the creation of the native query: SQLQuery eQuery = getCurrentSession().createSQLQuery(query).addEntity(ProdutoValorETO.class); , so there is no difference from a LazyException thrown by a Criteria or a native query.

      
  • Are there better ways to resolve this (other than Eager)?
  •   

    Well, eager really is a very dangerous alternative as it will always bring collections already loaded, which can affect performance in a very significant way. Another option is the one already said by Igor Venturelli in the comments, using Open Session in View, you can see a brief reference to the subject here .

    I particularly suggest understanding the application context and knowing when to bring the collections and when should I not. For example, let's assume a sales control system, where the selling entity has a collection of sales items. In this system there is the sales query screen, where the sales list will be displayed, but only the date, the customer, and the sales situation. So, I do not need to bring the items from the sale in my search query that does the sales listing. But when the user clicks on the view / edit sale button, it is necessary to display the items of each sale, then at the moment I look for the items, using for example the join fetch:

    FROM Venda v JOIN FETCH v.itens
    

    A good reference to join fetch can be seen here .

    In the case of when you get a LazyException before you even access the collection, you can assign an empty (or even null) collection before the exception is thrown, but as you can imagine, this would be a gambiarra. / p>     

    29.01.2018 / 15:00