我在实体中有一个懒惰的获取类型集合。我正在使用Spring Data(JpaRepository)来访问实体。
@Entity
public class Parent{
@Id
private Long id;
@OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY)
private Set<Child> children;
}
我想在服务类中使用两个函数,当前实现如下:
&#34;的子&#34;获取父
时应为nullpublic Parent getParent(Long parentId){
return repo.findOne(parentId);
}
&#34;的子&#34;在获取父级时应填写:
public Parent getParentWithChildren(Long parentId){
Parent p = repo.findOne(parentId);
Hibernate.initialize(p.children);
return p;
}
返回&#34;家长&#34;来自RestController的实体,抛出以下异常:
@RequestMapping("/parent/{parentId}")
public Parent getParent(@PathVariable("parentId") Long id)
{
Parent p= parentService.getParent(id);//ok till here
return p;//error thrown when converting to JSON
}
org.springframework.http.converter.HttpMessageNotWritableException: 无法写内容:懒得初始化一个集合 角色:com.entity.Parent.children,无法初始化代理 - 没有 会话(通过参考链:com.entity.Parent [&#34; children&#34;]); 嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException:懒得失败 初始化角色的集合:com.entity.Parent.children,不能 初始化代理 - 没有会话(通过参考链: com.entity.Parent [&#34;的子&#34;])
答案 0 :(得分:6)
如果您希望根据用例允许同一域模型的不同JSON表示,那么您可以查看以下内容,这样您无需使用DTO即可:
https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
或者,另请参阅以下
中的“Spring Data REST中的预测”部分https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra#projections-in-spring-data-rest
答案 1 :(得分:2)
RestController应该返回ParentDTO
而不是Parent
实体。 ParentDTO
可以在Transactional服务方法中填充。
答案 2 :(得分:2)
抛出异常是因为JSON序列化程序要求已初始化所有属性。因此,所有需要返回Parent的REST控制器必须首先初始化属性:
@RequestMapping("/parent/{parentId}")
public Parent getParent(@PathVariable("parentId") Long id) {
return parentService.getParentWithChildren(id);
}
getParentWithChildren
服务方法在事务内部运行,并且在提交事务时关闭关联的Hibernate会话。这意味着您必须在Hibernate Session仍然打开时(在Service方法内)初始化所有属性。
您还可以使用Spring Data entity graph支持:
@Entity
@NamedEntityGraphs(@NamedEntityGraph(name = "Parent.children", attributeNodes = @NamedAttributeNode("children")))
public class Parent{
@Id
private Long id;
@OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY)
private Set<Child> children;
}
getParentWithChildren
方法变为:
@Repository
public interface ParentRepository extends CrudRepository<Parent, Long> {
@EntityGraph(value = "Parent.children", type = EntityGraphType.LOAD)
Parent getParentWithChildren(Long parentId);
}
所以,你甚至不需要实现:
这些方法可以由Spring Data提供。
答案 3 :(得分:1)
首先,您没有向我们展示Child
Java类:我希望该属性被称为parentId
而不是parent
:
public class Child {
@ManyToOne
private Parent parentId;
}
解决方案1:您的代码实际上是正确的,只需要使用第二层DTO(简单的POJO类)将您的域层传输到客户端/浏览器。如果你不这样做,在解决你的懒惰异常后,你会遇到从父到子的循环依赖问题,而JSON编组(杰克逊)将尝试编码Child
,然后编码{{1然后再是它的孩子,然后再是他们的Parent
等等。 DTO的一个例子是:
Parent
解决方案2:对您的公共财产public class ParentDto {
private Long id;
private String prop1;
public ParentDto(Parent parent) {
this.id = parent.id;
this.prop1 = parent.prop1;
//...other properties
}
//here come all getters for the properties defined above.
}
使用@JsonIgnore,以便杰克逊在编组Parent.getChildren()
实例时不会尝试对子项进行编码。