Search code examples
hibernatejpamany-to-onejpa-2.1

JPA. FetchType.Lazy caused strange behaviour @ManyToOne


I'm trying to improve the performance of a project that uses JPA.

I'm using JPA 2.1 and Hibernate 5.0.1 implementation.


I changed the FetchType of a ManyToOne relation to Lazy, so the thing blows up.

When I just try to lookup all the records of the child object: the last record come with all empty fields and with a handler=JavassistLazyInitializer. And this strange behaviour impacts on the fields that use this objects (Like h:OneSelectMenu).

If I remove the FetchType.Lazy the thing back normal.

Some prints:

Debug child list 2

FetchType.Lazy

Code of the second print (FetchType.Lazy):

public class Usuario implements Serializable {
  private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id_Usuario")
private Integer idUsuario;

// Fields ...

@JoinColumn(name = "Id_Tipo_Logradouro", referencedColumnName = "Id_Tipo_Logradouro", nullable = true)
@ManyToOne(optional = true, fetch = FetchType.LAZY)
private TipoLogradouro tipoLogradouro;

@JoinColumn(name = "Id_Tipo_Usuario", referencedColumnName = "Id_Tipo_Usuario")
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private TipoUsuario tipoUsuario;

@JoinColumn(name = "Id_Supervisor", referencedColumnName = "Id_Usuario")
@ManyToOne(optional = true, fetch = FetchType.LAZY)
private Usuario supervisor;

// Contructor, Getters Setters etc ...

}

public class TipoUsuario implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 1)
@Column(name = "Id_Tipo_Usuario")
private String idTipoUsuario;

@Basic(optional = false)
@NotNull
@Size(min = 1, max = 20)
@Column(name = "Descricao")
private String descricao;

@Basic(optional = false)
@NotNull
@Size(min = 1, max = 1)
@Column(name = "Possui_Supervisor")
private String possuiSupervisor;

@Size(max = 1)
@Column(name = "Possui_Meta")
private String possuiMeta;

@Size(max = 1)
@Column(name = "Recebe_Alerta")
private String recebeAlerta;

@Transient
private List<Usuario> usuarioList = new ArrayList<>();

//Contructor, Getters Setters etc ...

Solution

  • Why

    With Lazy Loading, you are instructing EntityManager to NOT get these details unless someone asks for it. So, if in your transaction, you are not asking for that property to be fetched, you will only get a proxy to it. But if you ask that proxy for any thing related to that property, size for e.x., EntityManager will get that information for you on-demand.

    Looks like you are using JSF for your view, now in this case, before your model is asking for these details, the entity manager has already finished its job and thus does not know it has to get those details from the DB for you again. Remember that proxies are serialized , thus to your view, it looks like your collection is null/empty.

    How to get around this

    Correct answer depends on your usage.

    If the information you have currently lazy loaded is ALWAYS required to be shown in the front end, then you have no benefit of making it lazy loaded.

    Else, if you need it to be displayed only when someone clicks on a drop down or something, then you can populate those values based on a JPA Query with Fetch Joins.

    Else, if you have EJBs you can look at making your EntityManager extended.

    Each of the options has lot of details, but you can take a look at this blog post from Adam Bien for more details on all options mentioned above and more!

    Update 1: Take a look at this answer to see how you can load the Lazy loaded properties on the server side.