Lazy Load Onetomany

Asked

Viewed 1,738 times

3

Hello.

I’m having trouble getting my relationships fetch = Fetchtype.LAZY to work with the @Onetomany annotation. In my Notafiscal entity, there are several lists fetch = Fetchtype.LAZY, when loading the object by id, I load all these lists with left Outer Join. In the first instance (debugging) all lists are loaded. However, the moment I change tab and the request of the mailing list (mailing list) is returning me the error:

org.hibernate.Lazyinitializationexception: failed to lazily initialize a Collection of role: br.inf.criar.erp.nota.domain.Notafiscal.listaEmail, could not initialize proxy - no Session

To try to explain better, follows part of the Notafiscal class

@Entity
@Table(name = "notas_fiscais")
public class NotaFiscal implements IEntity {

private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;

@OneToMany(mappedBy = "notaFiscal", fetch = FetchType.LAZY,
        cascade = {CascadeType.ALL},
        orphanRemoval = true)
private List<NotaFiscalItem> itens;

@OneToMany(mappedBy = "notaFiscal", fetch = FetchType.LAZY,
        cascade = {CascadeType.ALL},
        orphanRemoval = true)
private List<NotaFiscalDocRef> listaDocRef;

@OneToMany(mappedBy = "notaFiscal", fetch = FetchType.LAZY,
        cascade = {CascadeType.ALL},
        orphanRemoval = true)
private List<NotaFiscalProcRef> listaProcRef;

//-----------------------------------------------------
//GRUPO DE CAMPO DE USO LIVRE
//----------------------------------------------------- 
@OneToMany(mappedBy = "notaFiscal", fetch = FetchType.LAZY,
        cascade = {javax.persistence.CascadeType.ALL},
        orphanRemoval = true)
private List<NotaFiscalObsCont> listaObsCont;

@OneToMany(mappedBy = "notaFiscal", fetch = FetchType.LAZY,
        orphanRemoval = true,
        cascade = {CascadeType.ALL})
private List<NotaFiscalVeiculo> veiculos;

@OneToMany(mappedBy = "notaFiscal", fetch = FetchType.LAZY,
        orphanRemoval = true,
        cascade = {CascadeType.ALL})
private List<NotaFiscalVolumes> volumes;

@OneToMany(mappedBy = "notaFiscal", fetch = FetchType.LAZY,
        cascade = {CascadeType.ALL},
        orphanRemoval = true)
private List<NotaFiscalEvento> eventos;

@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "notas_fiscais_email", joinColumns=@JoinColumn(name="nota_fiscal_id"))
private List<NotaFiscalEmail> listaEmail;

@ElementCollection(fetch = FetchType.LAZY)
@Column(name = "documento", length = 20, nullable = false)
@CollectionTable(name = "notas_fiscais_autxml", joinColumns=@JoinColumn(name="nota_fiscal_id"))
private List<String> listaAutXml;

Method that loads by id:

private NotaFiscal loadLazy(Long id) {
    try {
        String sql = "select o from NotaFiscal as o "
                + "left outer join o.listaEmail le "
                + "left outer join o.listaAutXml la "
                + "left outer join o.pagamentos p "
                + "left outer join o.duplicatas du "
                + "left outer join o.itens i "
                + "left outer join o.listaDocRef ldr "
                + "left outer join o.listaProcRef lpr "
                + "left outer join o.listaObsCont obs "
                + "left outer join o.eventos eve "
                + "left outer join o.volumes volumes "
                + "left outer join o.veiculos vei "
                + "where o.id = :id ";
        Query query = getEntityManager().createQuery(sql);
        query.setParameter("id", id);

        return (NotaFiscal) query.getSingleResult();
    } catch (Exception e) {
        CriareLog.log(e);
        return null;
    }
}

Method in which the error occurs at the time of the list request.

public DataModel<NotaFiscalEmail> getEmailsDM() {
    if (emailsDM == null) {
        if (this.getBean().getListaEmail() == null) {
            this.getBean().setListaEmail(new ArrayList<NotaFiscalEmail>());
        }
        // O erro ocorre exatamente nesta linha.
        emailsDM = new ListDataModel<NotaFiscalEmail>(this.getBean().getListaEmail()); 
    }
    return emailsDM;
}

If you need anything else, tell me I’ll post it here. I appreciate all your help. Hugs

1 answer

3


The mistake LazyInitializationException is one of the most common and misunderstood by those who use Hibernate.

Like the LazyInitializationException occurs

The important thing to understand the error is that Hibernate postpones loading of type relationships Lazy when the method getter relationship is called, that is, it does not load all the data when the methods getSingleResult or getResultList are called.

The problem is that the session (and the connection to the bank) still needs to be open at the time when the getter is called, which is not always true in the "higher" layers of the application, such as in controllers and views.

Therefore, the mistake LazyInitializationException occurs while your controller or your view calls a certain method get and Hibernate, who intercepts this call through a proxy, can no longer make the necessary call to the database.

Solution #1: Bring everything from the database and once

Bringing everything from the database is an alternative to avoid the error. However, the commands left outer join are no use for that.

The right thing would be to use one Fetch Join. See documentation Improving performance.

Solution #2: Do not close the session

Another commonly used solution is to keep the session open. This is the easiest but many consider as a anti-standard.

It’s hard not to agree that it seems a bad idea to objects consulting the bank while writing to view in HTML for the user.

Solution #3: Force queries manually

This is more of a scam, but it’s here for the record.

An alternative to force the loading of relationships is to invoke within your database layer the methods that recover the related entities, even if you do not.

It would be the equivalent of what occurs during the debug, only with calls for truth methods in the code.

Solution #4: use Dtos

Recovering all necessary data in the layers that access the database and return "lean" objects only with the required data is also a valid approach.

Some people don’t like it, but I think it’s a better alternative than exposing the model and access to databases for layers view and controller.

Why Debugging Works?

Because when the breakpoint is stopped inside the method the session is open and Hibernate can load the related entities.

A lot of people don’t realize this, but inspecting any chunk of code actually runs that chunk. If an inspected method affects the state of some objects, the state will be part of the program that is running.

Have you heard about Heisenberg?

Completion

Try to use fetch joins.

If you can’t, I suggest storing the query in Dtos.

  • Good afternoon. Great explanation, when I’m in trouble I always try to research where things come from and why they really work to better understand them. Therefore, I ended up checking that I should use Join fetch, however after updating the load search to Join fetch I have the following problem: javax.persistence.Persistenceexception: org.hibernate.Loader.Multiplebagfetchexception: cannot fetch simultaneously Multiple bags. I’m already looking for the solution, but I’m also struggling! I really appreciate your help!

  • @Gilvanandré The problem is that it is not possible to fetch from multiple lists at the same time, because this is impossible via SQL. I guess you’ll have to use another alternative.

  • Man, what a headache this! I wouldn’t want to use Opensessioninview. My biggest problem is that I would need to optimize searches/reports, which due to the use of EAGER is too slow. Going to use Lazy, the research works perfectly, however I end up suffering from other problems! Anyway thank you so much for the reply, sensational! My congratulations!

  • @Gilvanandré Another thing you can do is to assemble each necessary object or list in your hand instead of composing a "monster object" at once. I understand your headache, which is why I’ve been avoiding JPA to the fullest. There just comes a time when the facilities don’t make up for the problems it brings.

  • Yes, our application is becoming large. And for some time we are already thinking about "abandoning" JPA. Even trying to use it the best, sometimes it gives more work than not using it!

  • @Gilvanandré So it is. Another thing you can try to do in these cases is to use Activate queries when necessary. But you still won’t be able to bring all that data at once.

  • @Gilvanandré The problem is using JPA the wrong way and not JPA itself. Write queries for reports instead of using the entity’s API to rescue collections through the root entity. It doesn’t have to be Native query, it can be HQL and you still have the ability to use business terms instead of field and table names. Just write down the queries you need, as many as you need, instead of waiting for automatic loading of child objects. Deliver the result of these queries into relevant data structures to the report instead of into collections of business objects.

  • @Caffé For reports I use this structure that you put, the question here is not for use in reports, but rather to use it in a CRUD. Since this CRUD is in JSF. Ported when closing the section (for example: submitting the page), I no longer have access to the variables that are Lazy. My solution would be to keep the session open, but it’s the last solution I want to use. For now we are using EAGER even, but studies continue to try to find a more efficient solution.

  • Another option we are studying to use is the Dtos, as the friend said when answering the question. This is also not out of the question, and may also be the way to solve the problem.

  • @Gilvanandré You wrote reports in your comment. Yes, in my comment (data structures) I’m agreeing with the utluiz Dtos option - it serves for reports and also for presentation layer manipulation.

  • @Caffé A yes... no comentário sim! : D

Show 6 more comments

Browser other questions tagged

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