Я перевожу мой REST API Spring Boot с 1.5.4 на 2.0.3.
Это две мои сущности, хранилище для одной из них и контроллер для доступа к ним:
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();
}
}
Похоже, что в классах @RestController
больше нет активного сеанса с
parentRepository.findAll().get(0).getChildren().get(0).getName();
теперь выбрасывает
LazyInitializationException: failed to lazily initialize a collection of role: com.mycompany.myapplication.entity.Parent.children, could not initialize proxy - no Session
Это можно исправить, установив аннотацию @Transactional
для метода контроллера или класса контроллера.
Однако у меня проблема с лениво загруженной children
.
Если я запускаю приведенный выше пример кода, даже с аннотацией @Transactional
, я получаю то же исключение, но с вложенным
com.fasterxml.jackson.databind.JsonMappingException
Это связано с тем, что сериализация в JSON происходит вне контроллера, следовательно, вне активного сеанса.
Существует некрасивое исправление, считывающее некоторые данные из каждого child
перед выходом из метода:
@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;
}
Это работает, но ужасно уродливо и добавляет много шаблонного.
Другим решением было бы сопоставить все сущности с DTO, прежде чем возвращать их клиенту. Но это решение добавляет еще один слой в мое приложение, которое мне не нужно.
Есть ли способ убедиться, что во время автоматической сериализации сущностей существует активный сеанс?