How to refetch correctly Lazy Objects


#1

Hi, sorry if you guys don’t consider this question as a Hibernate question, but I asked in JSF forum and people said: “this isn’t a JSF question”.

I’m using JSF and CDI Transactional with Hibernate. My problem is:

I created a method where I initialize my entity and the lazy collections:

public void initializePessoa() {
    pessoa = getEntityManager().createQuery(
                "SELECT p FROM Pessoa p " +
                        "LEFT JOIN FETCH p.enderecos e " +
                        "LEFT JOIN FETCH e.tipo " +
                        "WHERE p.empresa.id = :empresaId " +
                        "AND p.empresaCodigo = :empresaCodigo", Pessoa.class)
                .setParameter("empresaCodigo", idParameter)
                .setParameter("empresaId", loginController.getEmpresa().getId())
                .getSingleResult();

    pessoa.setEmails(getEntityManager().createQuery(
                "SELECT pe FROM PessoaEmail pe " +
                        "JOIN FETCH pe.tipo " +
                        "WHERE pe.pessoa.id = :pessoaId", PessoaEmail.class)
                .setParameter("pessoaId", pessoa.getId())
                .getResultList());
}

So I edit the Pessoa entity and I use a method with @Transactional to merge, but in my business view user could save and stay in the view to change again, so if I don’t initialize objects again after merge() everything is detached.

But if I use initializePessoa() inside the method with @Transactional, like this:

@Transactional(rollbackOn = {Exception.class})
public String savePessoa(boolean saveAndReturn) {
    pessoa = getEntityManager().merge(pessoa);
    initializePessoa();
}

I get the error: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance when I try to set a Lazy Collection again.

So is there any suggestion when and how I could refetch objects after merge()?

Thanks in advance


#2

There are many things wrong here:

  1. Why do you fetch Perso when you already merged it?
  2. Why do you try to reinitialize a child collection after you fetch the parent entity? Just traverse it and it will be fetched.

#3

Well, I don’t know if I did something else wrong.

  1. Because if I edit Person edit and hit button save, I merged the Person. If I just hit button save again, without initialize Collections again, I got LazyInitiationException.

  2. I didn’t understand, what do you mean traverse? Use the getter?


#4
  1. You get the LazyInitializationException because you didn’t fetch the collection after merge. Also, merge is not like save. It’s for merging detached entities.
  2. Traversing means accessing the collection and maybe read its size.

#5
  1. Yes, that’s what I’m trying to do, fetch collections after merge. And about merge if I understand what you’re saying this isn’t a situation that I should use merge() ?

  2. Now I understand.

Do you have any example what I should do?

Let me try to explain showing what’s going on:

I start the view editing a Pessoa and fetch the Lazy Collections that I need using initializePessoa().

I edit something and hit save button, before the merge my obj is this:

Captura%20de%20Tela20180514103236

After merge the obj turns like this:

Captura%20de%20Tela20180514103254

I read that is the normal behavior. So I need to fetch and initialize my Pessoa again, because if I didn’t, on the first pessoa.getEnderecos(), I get LazyInitiationException.

But if I use initializePessoa() or even if I use pessoa.setLazyListX(), inside the method with @Transactional CDI, I get A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance.

So what I’m doing wrong, it’s the way that I’m refetching my objects and which is the right way? Or I should not use merge() like this?


#6

You have two possibilities.

Either you initialize the associations after merge:

pessoa = getEntityManager().merge(pessoa);

pessoa.getEnderecos().size();
pessoa.getType().getName();
pessoa.getEmails().size();

Or, you flush, evict, refetch:

pessoa = getEntityManager().merge(pessoa);
getEntityManager().flush();
getEntityManager().evict(pessoa);

pessoa = getEntityManager().createQuery(
	"SELECT p FROM Pessoa p " +
	"LEFT JOIN FETCH p.enderecos e " +
	"LEFT JOIN FETCH e.tipo " +
	"WHERE p.empresa.id = :empresaId " +
	"AND p.empresaCodigo = :empresaCodigo", Pessoa.class)
.setParameter("empresaCodigo", idParameter)
.setParameter("empresaId", loginController.getEmpresa().getId())
.getSingleResult();

pessoa.getEmails().size();

#7

Sorry, but I tried both of two options and didn’t work, could be any @Transactional behavior what do you think?

  1. First option:
pessoa = getEntityManager().merge(pessoa);
pessoa.getEnderecos().size();
pessoa.getEmails().size();
pessoa.getTelefones().size();
pessoa.getReferenciasBancarias().size();

After all size(), the obj continue like the second screenshot that I’ve sent before and at the first use of any collection I get LazyInitializationException.

  1. Second option:
pessoa = getEntityManager().merge(pessoa);
getEntityManager().flush();
getEntityManager().unwrap(Session.class).evict(pessoa);

pessoa = getEntityManager().createQuery(
                "SELECT p FROM Pessoa p " +
                        "LEFT JOIN FETCH p.enderecos e " +
                        "LEFT JOIN FETCH e.tipo " +
                        "LEFT JOIN FETCH e.cidade c " +
                        "LEFT JOIN FETCH c.uf u " +
                        "LEFT JOIN FETCH u.pais " +
                        "WHERE p.empresa.id = :empresaId " +
                        "AND p.empresaCodigo = :empresaCodigo", Pessoa.class)
                .setParameter("empresaCodigo", idParameter)
                .setParameter("empresaId", loginController.getEmpresa().getId())
                .getSingleResult();

.getSingleResult() throw:

org.hibernate.PersistentObjectException: detached entity passed to persist: com.lorensid.model.Pessoa

#8

Use this template to provide a replicating test case:

http://in.relation.to/2016/01/14/hibernate-jpa-test-case-template/

To demonstrate that it does not work.