Lazy Load OneToMany

3

Hello.

I'm having difficulty running my fetch = FetchType.LAZY relationships with the @OneToMany annotation. In my entity Nota Fiscal, 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 the tab and the request of the mailing list (listEmail) is made, I am returning the error:

  

org.hibernate.LazyInitializationException: failed to initialize a collection of role: br.inf.criare.erp.nota.domain.NotaFiscal.listaEmail, could not initialize proxy - no Session

To try to explain better, it follows part of the class Fiscal Note

@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 does load 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 where error occurs when requesting the list.

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 go tell me that I'm posting here. I thank the help of all you. Hugs

    
asked by anonymous 22.09.2014 / 19:46

1 answer

3

The LazyInitializationException error is one of the most common and misunderstood by those using Hibernate.

How LazyInitializationException occurs

What's important to understand the error is that Hibernate postpones the loading of lazy relationships for when the getter method of the relationship is called, that is, it does not loads all data when getSingleResult or getResultList methods are called.

The problem is that the session (and the connection to the database) still needs to be open the moment the getter is called, which is not always true in the "higher" application, such as controllers and views .

Therefore, the LazyInitializationException error occurs while your controller or your view calls a certain get method and Hibernate, which intercepts this call through a < in> proxy , you 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 avoiding the error. However, the left outer join commands are not suitable for this.

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

Solution # 2: Do not close the session

Another commonly used solution is to keep your session open. This is the easiest method, however, many consider as anti-default .

It's hard to disagree that it seems like a bad idea to query the database while writing the view in HTML to the user.

Solution # 3: Force queries manually

This is another game, but stay here to register.

An alternative to forcing relationships to be loaded is to invoke within their database layer methods that retrieve related entities, even if they do not do with them.

It would be the equivalent of debug , but with true method calls in code.

Solution # 4: Use DTOs

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

Some people do not like this, but I consider it a better alternative than exposing the model and database access to the view and controller layers. >

Why does debugging work?

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

Many people are unaware of this, but inspecting a piece of code actually executes that passage. If an inspected method affects the state of some objects, the state will be part of the program that it is running.

Have you heard about Heisenberg ?

Conclusion

Look for fetch joins .

If I can not, I suggest storing the query in DTOs.

    
22.09.2014 / 20:49