Story of LazyInitializationException

Story of LazyInitializationException This week I had a problem with LazyInitializationException in Hibernate. My case was pretty standard - one entity contained a list of lazy loaded children entities. I had a Spring service that queried JpaRepository for the parent entity, made some operations on the children entities to force loading them as well and returned them all for further processing. And I got LazyInitializationException ...

confused

This week I had a problem with LazyInitializationException in Hibernate. My case was pretty standard - one entity contained a list of lazy loaded children entities. I had a Spring service that queried JpaRepository for the parent entity, made some operations on the children entities to force loading them as well and returned them all for further processing. And I got LazyInitializationException ... According to my knowledge I was doing everything right but without a success. Searching the internet was not helpful either but lets start from the beginning.

 

My code

The supporting role in the action was played by two entities. Parent contained a list of children.

@Entity(name = ...)
public class Parent {
@Id
@GeneratedValue
@Column(name = "...")
private Long id;

@Column(name = "...")
private String name;

@ManyToMany
@JoinTable(
name="...",
joinColumns=@JoinColumn(name="...", referencedColumnName="..."),
inverseJoinColumns=@JoinColumn(name="...", referencedColumnName="..."))
private List<Child> children;

getters and setters ...
}
@Entity(name = "...")
public class Child {
@Id
@GeneratedValue
@Column(name = "...")
private Long id;

@Column(name = "...")
private String name;

getters and setters ...
}

There also was a repository of parents:

public interface ParentsRepository extends JpaRepository<Parent, Long> {
}

The main role was played by a service - ChildrenService.

@Service
public class ChildrenService {

private ParentsRepository parentsRepository;

@Autowired
public ChildrenService(ParentsRepository parentsRepository) {
this.parentsRepository = parentsRepository;
}

@Transactional
public List<ChildDTO> getChildrenByParent(Long parentId) {
com.dbapresents.project.dao.Parent parent = parentsRepository.findOne(parentId);
if (parent == null) {
return null;
}
List<Child> children = parent.getChildren();
return Lists.transform(children, ChildDTO::from);
}
}

 

LazyInitializationException

When I executed the getChildrenByParent method, I got:

.w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: failed to lazily initialize a collection of role: com.dbapresents.project.dao.Parent.children, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.dbapresents.project.dao.Parent.children, could not initialize proxy - no Session

This is a pretty standard one. It happens when an entity's first access happens outside the session when Hibernate is not able to lazy load missing data. To prevent such case, I took two actions:

  1. I added the @Transactional annotation to the service method. It should have instructed Hibernate to keep the same session through the entire method execution so all not eagerly loaded data (like the list of children) could be lazily loaded at the Lists.transform step.
  2. Transformation of the children to DTO objects was done in the service method to make sure Hibernate noticed those entities are needed and should be loaded from the database.

Of course that did not help. I turned my face up to the internet for help. Changing the Parent entity definition to EAGERLY load children solved the problem but it was not a solution for me. This was not the only such case. If I had to change all my joins to EAGER, it would mean loading the whole database with the first query. So it was not an option.

 

Google Guava

Have you figured out what caused my problem yet? It was Guava's List.transform. The transform method is not implemented as a loop over all list elements. It is a random access view instead. It means that the tranformation is done when a particular element of the transformed list is needed. In my case, the list of children was produced inside of the service method but the first time access of the elements happened outside of it. Then, Guava wanted to transform a particular child entity object. To provide one, Hibernate needed to lazily load it based on the parent object but unfortunately it was outside of the transaction scope (the service method with @Transactional annotation). The session was already closed and the exception was thrown. So I have to remember that Lists.transform is a view that performs transformation at the access time.

 

Solution

Yeah, right, solution. There are a few of them. I chose to copy the transformed list to a new list using Lists.newArrayList.

return Lists.newArrayList(Lists.transform(employees, EmployeeDTO::from));

It reads each single list element and adds it to the new list. It guarantees accessing all items in the scope of the service method.