How to avoid Lazyexceptions?

Asked

Viewed 287 times

3

My application faces numerous Lazyexceptions problems and what causes a "mess" 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 on the filter because the retailers of Productovalor 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>

Productovaloreto

    @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;
  1. 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 tried to access the items and already got the Exception.
  2. Is there a mechanism in Spring JPA or new versions of Hibernate to better handle this? (This application uses Spring 3 Hibernate 3.2)
  3. Is there any implication in Lazy occurring when directly writing select instead of using Hibernate criteria?
  4. There are better ways to solve this case (other than Eager)?
  • 2

    Has a mini pattern designs (breaking another pattern) called Open Session in View. Maybe I can help you.

  • Edit your question and place your entities involved in the relationship, please. Also, why your method findByProdutoId is returning a List? He shouldn’t return just one object?

  • @Felipemarinho Added. A product has a list of values, according to the unit you buy is returned a value, business rules.

  • @Danielamorais Take a look at the answers they have already given. O Join Fetch is probably the best solution to your problem.

2 answers

1


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

Yes, if you are using the entities outside the transaction.

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

Every application I’ve seen with many problems of LazyException were because entities were used outside the transaction. Even, this is a practice to be avoided for different reasons that go beyond the LazyException.

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

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

Is there any implication in Lazy occurring when directly writing select to instead of using Hibernate criteria?

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

There are better ways to solve this case (other than Eager)?

Yes, using FETCH:

SELECT pv
FROM produtovalor pv
JOIN FETCH pv.lojista 
WHERE pv.idproduto = :id AND pv.ativo = TRUE;
  • For those who voted negative: I would like to know why my answer is not helpful.

  • I didn’t understand the negative too! A doubt: In a context of Omain driven design, I see the entities."?

  • You would need to understand in the project how the transaction control between classes is (if you have been using this control). I’ll give you an example. In Spring if a Service calls a Repository to search for an entity, the entity associated with this sought entity may give Lazyexception error (example: entity.getOther Entity()). This is only solved with the @Transactional in Service, thus it will open a transaction with the database from this moment and all entity "get" from the Service will do the search in the database without Lazyexception.

0

How to avoid Lazyexceptions?

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

  1. 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 tried to access the items and already got the Exception.

Lazyexception is always launched when you try to access a collection that is not "loaded" and the session is closed. What might be happening is that when you do a search, the default entity constructor is triggered (you can put a breakpoint in the constructor to verify that fact), maybe in the constructor you’re already trying to access the collection, or somewhere else, that you haven’t been able to visualize yet.

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

whereas Spring 4 has long been consolidated in the market, and whereas Spring 5 has just been launched, and whereas Hibernate is currently in version 5.x, I would say that an update would be very welcome from a technological point of view, but in legacy systems this is not always possible. But the issue of Lazyexception, is not directly related to the versions of the frameworks, we can say that the Lazyexection would be a defense mechanism to avoid an overload, the framework wants you to only load the collections when it’s really necessary, and this is the secret to solving the problem.

  1. Is there any implication in Lazy occurring when directly writing select instead of using Hibernate criteria?

Hibernate is a JPA implementation, so when you "write SQL directly into the code" using a native query, that query is still managed by the application’s persistence layer, note 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);, therefore, there is no difference of a Lazyexception launched by a Criteria or by a native query.

  1. There are better ways to solve this case (other than Eager)?

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

I particularly suggest understanding the context of the application and knowing when to bring the collections and when not to. For example, let’s assume a sales control system, where the selling entity has a collection of selling 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 sale situation. So, I don’t need to bring the items from the sale in my query that does the search for the sales listing. But when the user clicks on the see/edit sale button, will need to display the items of each sale, then at this time I search the items, using for example the fetch Join :

FROM Venda v JOIN FETCH v.itens

A good reference for Join fetch can be seen here.

In case when you get a Lazyexception before you even access the collection, you can assign an empty (or even null) collection before the exception is launched, but as you can imagine, this would be a gambiarra.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.