Solution ignoring the attribute when serializing
After seeing the comment and getting a better understanding of the problem, I think the solution is to ask the Json library to ignore the attributes with the serialization collections only when needed, since you mentioned that in some situations you will want to include the collections .
Solution with Gson
Based on this SOEN issue , there are two ways to exclude certain fields.
The first is to note the fields that always will be serialized with the @Expose
annotation. When it is necessary to serialize all fields, the library is normally used, but if we only want to serialize the fields with the annotation, we can do this:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
The second way is by implementing a ExclusionStrategy
. Here's 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 addresses and contacts, do so:
Gson gson = new GsonBuilder().setExclusionStrategies(new ClienteExclusionStrategy()).create();
Solution with Jackson
To override the original behavior of a class at the time of serialization you can use Jackson's Mixins . This is an abstract class where you define the behavior you want without changing the original class.
See the example:
abstract class ClienteMixIn {
@JsonIgnore List<Contato> getContatos();
@JsonIgnore List<Endereco> getEnderecos();
}
And finally add the mixin to the serialization when needed:
objectMapper.getSerializationConfig().addMixInAnnotations(Cliente.class, ClienteMixIn.class);
Solution retrieving relationship data
If you understand correctly you want to execute size()
without executing the queries in lazy mode. This is simply not possible, after all how could Hibernate know how many records 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 check the items in the collection.
One of the solutions to force the collection to read while the EntityManager is open, even if the entity relationship is set to lazy , is to use a join fetch .
join fetch is often used to improve performance when we know in advance that we need to access that relationship. Internally, Hibernate will fetch the data from the parent entity and perform a native OUTER JOIN
on the database to fetch the records from the collection. For more details, see the documentation for 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, items in the collection will be loaded immediately and no additional queries will be required, meaning nothing of LazyLoadException
and only a call to the bank.
Alternatively, another way to initialize a collection forcibly when it is required is to use the static method Hibernate.initialize()
.
This method forces the proxy of the collection to load everything that is required to run when the EntityManager is closed.
See an example:
Hibernate.initialize(object.getColecao());
Solution de-encapsulating the proxy entity
Hibernate creates a proxy class to intercept calls to lazy attributes. Another idea would be to get the original entity, without a proxy, because in that case we would not have the problem of LazyLoadingException
.
I found two SOAP issues with codes that purports 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 Json generation. Just apply it right after doing the detach of the entity.