How to prevent the List.size() method from running lazyload with Hibernate and JPA

Asked

Viewed 1,120 times

6

When I make a query in the bank and an object is returned, in this object has a collection, which by default is Lazyload, if I do object.getColecao().size() ai is run the lazyload to bring the records, if the session or entitymanager has been closed, of course it will happen the Lazyloadexception.

My question is, would have like after I receive the subject of the query, even if I do object.getColecao.size() it does not run Lazy load?

The problem is that I query and need to serialize the object to JSON, however, the main JSON processing frameworks like Jackson and Gson they implicitly execute the size() for collections, thus happens the Lazyloadexception.

I cannot configure the frameworks to ignore the litas because if the lists are not null you will have to serialize, that is, if it is null, it will not serialize, otherwise it will serialize. The setting to not serialize nulls of these two works but not for lists, yet they run the size() to check if it has content, so the solution I see is not to run Lazy load when calling method size().

I’ve tried cloning the objects using Springbeansutil, but still running the size() is made an attempt to lazyload.

1 answer

3


Solution ignoring the attribute when serializing

After seeing the comment and understanding the problem better, I think the solution is to ask the Json library to ignore the attributes with the collections in the serialization only when necessary, since you mentioned that in some situations you will want to include the collections.

Solution with Gson

Based on this issue of SOEN, there are two ways to exclude certain fields.

The first is to note down the fields that at all times will be serialized with the annotation @Expose. When it is necessary to serialize all fields, the library is used normally, but if we want to serialize only the fields with the annotation, we can do so:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

The second way is through the implementation of a ExclusionStrategy. Take an example:

public class ClienteExclusionStrategy implements ExclusionStrategy {

    public boolean shouldSkipClass(Class<?> arg0) {
        return false;
    }

    public boolean shouldSkipField(FieldAttributes f) {
        return f.getDeclaringClass() == Cliente.class
            && (f.getName().equals("enderecos")) || f.getName().equals("contatos"));
    }

}

So when you want to ignore the addresses and contacts, do so:

Gson gson = new GsonBuilder().setExclusionStrategies(new ClienteExclusionStrategy()).create();

Solution with Jackson

To overwrite the original behavior of a class at the time of serialization you can use the Mixins Jackson. This is an abstract class where you define the behavior you want without changing the original class.

Take the example:

abstract class ClienteMixIn {
     @JsonIgnore List<Contato> getContatos();
     @JsonIgnore List<Endereco> getEnderecos();
}

And finally add mixin to the serialization when needed:

objectMapper.getSerializationConfig().addMixInAnnotations(Cliente.class, ClienteMixIn.class);

Solution Recovering Relationship Data

If I understand correctly you want to run the size() without executing the queries in mode Lazy. This is simply not possible, after all as the Hibernate could know how many records the join will return?

Continuing this reasoning, to know if there are items in the collection and use them when necessary you will always have to consult the items in the collection.

One of the solutions to force the collection to be read while Entitymanager is open, even if the entity relationship is set in mode Lazy, is to use a Join fetch.

The Join fetch is usually used to improve performance when we know in advance that we will need access to this relationship. Internally, Hibernate will bring the main entity data and perform a OUTER JOIN native to the database to bring the collection records. For more details, see the documentation on the different optimization strategies here.

An example of this using Criteria is:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Entidade> criteriaQuery = criteriaBuilder.createQuery(Entidade.class);
Root<Entidade> root = criteriaQuery.from(Entidade.class);
root.fetch("colecao", JoinType.LEFT); // força um `LEFT OUTER JOIN` nativo

With the above code, the collection items will be loaded immediately and no additional queries will be required, i.e. LazyLoadException and only one call to the bank.


Alternatively, another way to initialize a collection forcibly when encesseary is to use the static method Hibernate.initialize().

This method forces the proxy of the collection load everything needed to run when Entitymanager is closed.

Take an example:

Hibernate.initialize(object.getColecao());

Disembobulating the entity of the proxy

Hibernate creates a class proxy to intercept calls to attributes Lazy. Another idea would be to obtain the original entity, without proxy, because in this case we would not have the problem of LazyLoadingException.

I found two issues SOEN with codes that sets out to do this. See one of them:

public static <T> T initializeAndUnproxy(T entity) {
    if (entity == null) {
        throw new 
           NullPointerException("Entity passed for initialization is null");
    }

    Hibernate.initialize(entity);
    if (entity instanceof HibernateProxy) {
        entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer()
                .getImplementation();
    }
    return entity;
}

With solution it would not be necessary to modify the generation of Json. Just apply it right after doing the detach of the entity.

  • The problem is that I do not want to be held the Lazy, I need to bring a list of customers, for example 200 clients, each client object has two lists, Contacts and Addresses and I do not want to bring neither contacts nor addresses even using JOIN to optimize. Blz, when I try to convert to JSON that list ai runs the size() by the processed JSON(Jackson and Gson), in Hibernate 4 I can use this <prop key="Hibernate.enable_lazy_load_no_trans">false</prop> ai it runs normal, only it does a lot of select in the bank, I just want to bring the simple list of customers without Lazy.

  • I know no, I’m thinking serially of using Mentabean, we have more control, I’ve lost too much time with JPA/ Hibernate, I don’t use annotations that pollute the code too much, I’m using Orm.xml even it’s all separated and more organized, my pojos are clean, I really want to use JPA but have already been two 2 direct with this problem, already try getEntityManager(). detach(Entity); but there is no way, the object is not "disconnected" and when I call size() it is trying Lazy, I am thinking of doing manually via Reflection a routine that copies an object to a new one without being linked..

  • @Rodrigorodrigues I think that I understood your situation better. I edited the question with what I believe to be a better solution for your case.

  • @Rodrigorodrigues And make sure I understand your frustration with JPA, it’s certainly promised a lot of ease, but in fact you end up having to "brush the bits" to understand how everything works and get what you want. I don’t know this Mentabean (I’ll research it tomorrow), but think that each technology has its learning curve and its nuances that "catch" us from time to time.

  • As for the solutions with Jackson and Gson I have tried, but it does not work, I will explain, with annotations I will ALWAYS say that certain attribute will not be serialized, sometimes I need to serialize the lists and sometimes not, not already lazyload between client and server as it is a Restfull application, the only communication is in JSON. As for the solution with Gson usanod Exclusionstrategy I have done and it worked, but it falls in the same, certain times I need to serialize or not, so I would have to create a toJSonWihoutList and toJson, was as close as I could...

  • It would be so simple if I had the option of not wanting the lazyLoad, ready, everything would be solved, I’ll be trying here and any solution I put to help the next, man, thank you very much for the attention, thank you very much!

  • @Rodrigorodrigues I understood that sometimes you want to serialize and others do not, but I do not understand the difficulty in making, for example, a conditional to serialize or not the attributes depending on the criterion you want using one of the methods proposed in the first part of the question. Maybe you’re mixing things up a little bit.

  • Yeah, I’m gonna have to make it that way, a parole, I’m just afraid that this parole will then increase...!

  • @Rodrigorodrigues Unfortunately sometimes our use cases seem to always be exceptions to the standard implementation. This morning I thought of another approach. Take a look at the last topic I added to the answer. I honestly never did, but if you could try feedback.

  • sorry for the delay in answering, I was trying now, I thought it was right, this is exactly what I need, unproxy the entities, however, does not enter at all in the condition 'if (Entity instanceof Hibernateproxy)'I already sent the entity containing the collection as well as the collection itself, which after it is returned changes the type of List to Persistencebag, I really thought it would work. Funny, if I manually set null lists there it works, however, if I use setando null Reflection does not work, it remains with Persistencebag the type of attribute

  • Note: I use JPA+Hibernate and not pure Hibernate

Show 6 more comments

Browser other questions tagged

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