I'm migrating my Spring Boot REST API from 1.5.4 to 2.0.3.
These are my two entities, a repository for one of them and a controller for accessing them:
Parent.java
@Entity
@Table(name = "PARENT")
public class Parent implements Serializable {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Child> children;
}
Child.java
@Entity
@Table(name = "CHILD")
public class Child implements Serializable {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name = "PARENT_ID")
private Long parentId;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PARENT_ID")
private Parent parent;
@Column(name = "name")
private String name;
}
ParentRepository.java
public interface ParentRepository extends JpaRepository<Parent, Long> {
}
ParentController.java
@RestController
@RequestMapping("/parents")
public class ParentController {
@Autowired
private ParentRepository parentRepository;
@RequestMapping(method = RequestMethod.GET)
public List<Parent> getParents() {
return parentRepository.findAll();
}
}
It appears that there is no longer an active session in the @RestController
classes since
parentRepository.findAll().get(0).getChildren().get(0).getName();
now throws a
LazyInitializationException: failed to lazily initialize a collection of role: com.mycompany.myapplication.entity.Parent.children, could not initialize proxy - no Session
This can be fixed by setting a @Transactional
annotation on either the controller method or the controller class.
However, the problem I have regards the lazily loaded children
.
If I run the example code above, even with the @Transactional
annotation, I get the same exception but with a nested
com.fasterxml.jackson.databind.JsonMappingException
This is due to the serialization to JSON happens outside of the controller, hence outside the active session.
There is an ugly fix for this, by reading some data from each child
before exiting the method:
@RequestMapping(method = RequestMethod.GET)
public List<Parent> getParents() {
List<Parent> parents = parentRepository.findAll();
parents.stream()
.flatMap(p -> p.getChildren().stream())
.forEach(Child::getName);
return parents;
}
This works, but is terribly ugly and adds a lot of boilerplate.
Another solution would be to map all entities to DTOs before returning them to the client. But this solution adds another layer to my application which I don't want.
Is there a way to make sure that there is an active session during the automagical serialization of the entities?