Return empty collection when this collection is lazy intitialized

Viewed 79

I have this object:

Entity

@Entity
public class someClass{
    private String name;
    private String labelKey;

    @ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, fetch = FetchType.LAZY)
    private Set<Product> products = new HashSet<>();
}

DTO

public class someClass{
    private String name;
    private String labelKey;
    private Set<Product> products = new HashSet<>();
}

My problem is that when I get this object but products are lazy initialized, when I mapp entity to DTO using Dozer, I get a LaziInitializedException, then i want to get that when I get products lazy initialized, this products will return a empry Set. Is this possible?

Thanks for your time and sorry for my english, it's not my native language.

4 Answers

As you can see in this tutorial here you can instruct dozer to exclude some field from the mapping.

If you do so, then the dozer will not invoke the method of getProducts of your entity class and therefore the exception LaziInitializedException will not be thrown.

At the same time because your DTO object is initialized with an empty HashSet for the field products, this is what will remain at the end in the DTO.

So your requirement will work, where your entity is lazily initialized for products and your DTO returns an empty list while at the same time the mapping happens from dozer.

Here is the configuration that you need for the mapper of dozer.

BeanMappingBuilder mappingExclusion = new BeanMappingBuilder() {
    @Override
    protected void configure() {
        mapping(SomeClassEntity.class, SomeClassDto.class).exclude("products");
    }
};
mapper = new DozerBeanMapper();
mapper.addMapping(mappingExclusion); 

Then you can use it to do the mapping as following

mapper.map(someClassEntityInstance, someClassDtoInstance);

You could create/modify your Getter such that:

public Set<Product> getProducts() {
   if (products == null) {
       return new HashSet<>();
       //or products = new HashSet<>(), but I'm not sure of the side effects as far as database framework is concerned.
   }
   return products;
}

Try marking your service class or method as @Transactional to let Spring handle session management.

public class ServiceUsingSomeClass {
    final SomeClassRepository someClassRepository;

    //Constructor ...
    
    @Transactional
    showProducts() {
       someClassRepository.findAll();
       // Do something with Set<Product>

    }
    
}

If you only want to avoid fetching the association in cases where you use Dozer for DTO mapping, you could configure it to ignore products field in source object by extending DozerConverter and using that custom converter.

I also feel that maybe that means your target type doesn't really need to have a products field to begin with, since you're not going to populate it.

If there's many places like this in your codebase, consider using projections to only fetch the properties necessary for the purpose at hand.

@fella7ena brings up a point about @Transactional, however this is actually unrelated - you can still come across LazyInitializationException within a transaction. This happens because Hibernate loses track of the relation between the java bean's persistence state and the database state. If you actually wanted to fetch products association from the database, you would have to use eager fetchtype (leads to n+1 issue), batching, or entitygraphs.

Related