指定是否延迟加载 Spring 数据
Specifying whether or not to lazily load with Spring Data
我在一个实体中有一个延迟获取类型的集合。我正在使用 Spring 数据 (JpaRepository) 来访问实体。
@Entity
public class Parent{
@Id
private Long id;
@OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY)
private Set<Child> children;
}
我想在服务中使用两个功能 class,目前的实现如下:
"children" 获取父项时应为 null
public Parent getParent(Long parentId){
return repo.findOne(parentId);
}
"children"取父时要填:
public Parent getParentWithChildren(Long parentId){
Parent p = repo.findOne(parentId);
Hibernate.initialize(p.children);
return p;
}
从 RestController 返回 "Parent" 实体时,抛出以下异常:
@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:
Could not write content: failed to lazily initialize a collection of
role: com.entity.Parent.children, could not initialize proxy - no
Session (through reference chain: com.entity.Parent["children"]);
nested exception is
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily
initialize a collection of role: com.entity.Parent.children, could not
initialize proxy - no Session (through reference chain:
com.entity.Parent["children"])
RestController 应该 return 一个 ParentDTO
而不是 Parent
实体。 ParentDTO
可以在事务服务方法中填充。
抛出异常是因为 JSON 序列化程序要求所有属性都已初始化。因此,所有需要 return a Parent 的 REST 控制器都必须先初始化属性:
@RequestMapping("/parent/{parentId}")
public Parent getParent(@PathVariable("parentId") Long id) {
return parentService.getParentWithChildren(id);
}
getParentWithChildren
服务方法在运行事务内部,提交事务时关联的Hibernate Session关闭。这意味着您必须在 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);
}
因此,您甚至不需要实施:
- 得到Parent
- 得到ParentWithChildren
这些方法可以由 Spring 数据提供。
首先,您没有向我们展示 Child
Java class:我希望 属性 被称为 parentId
而不是 parent
:
public class Child {
@ManyToOne
private Parent parentId;
}
解决方案 1:您的代码实际上是正确的,只是您必须使用第二层 DTOs(简单的 POJO classes)将您的域层传输到 client/browser.如果你不这样做,在你解决延迟异常之后,你会遇到从 Parent 到 Child 的循环依赖问题,并且 JSON 编组器(Jackson)将尝试编码a Child
,然后是 Parent
,然后是 children,然后是 Parent
,依此类推。 DTO 的一个例子是:
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.
}
解决方案 2:为您的 public 属性 Parent.getChildren()
使用 @JsonIgnore,这样 Jackson 在编组Parent
实例。
如果您希望根据用例允许不同的 JSON 表示同一域模型,那么您可以查看以下内容,无需 DTO 即可实现:
https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
或者,另请参阅以下
中的 'Projections in Spring Data REST' 部分
https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra#projections-in-spring-data-rest
我在一个实体中有一个延迟获取类型的集合。我正在使用 Spring 数据 (JpaRepository) 来访问实体。
@Entity
public class Parent{
@Id
private Long id;
@OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY)
private Set<Child> children;
}
我想在服务中使用两个功能 class,目前的实现如下:
"children" 获取父项时应为 null
public Parent getParent(Long parentId){ return repo.findOne(parentId); }
"children"取父时要填:
public Parent getParentWithChildren(Long parentId){ Parent p = repo.findOne(parentId); Hibernate.initialize(p.children); return p; }
从 RestController 返回 "Parent" 实体时,抛出以下异常:
@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: Could not write content: failed to lazily initialize a collection of role: com.entity.Parent.children, could not initialize proxy - no Session (through reference chain: com.entity.Parent["children"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.entity.Parent.children, could not initialize proxy - no Session (through reference chain: com.entity.Parent["children"])
RestController 应该 return 一个 ParentDTO
而不是 Parent
实体。 ParentDTO
可以在事务服务方法中填充。
抛出异常是因为 JSON 序列化程序要求所有属性都已初始化。因此,所有需要 return a Parent 的 REST 控制器都必须先初始化属性:
@RequestMapping("/parent/{parentId}")
public Parent getParent(@PathVariable("parentId") Long id) {
return parentService.getParentWithChildren(id);
}
getParentWithChildren
服务方法在运行事务内部,提交事务时关联的Hibernate Session关闭。这意味着您必须在 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);
}
因此,您甚至不需要实施:
- 得到Parent
- 得到ParentWithChildren
这些方法可以由 Spring 数据提供。
首先,您没有向我们展示 Child
Java class:我希望 属性 被称为 parentId
而不是 parent
:
public class Child {
@ManyToOne
private Parent parentId;
}
解决方案 1:您的代码实际上是正确的,只是您必须使用第二层 DTOs(简单的 POJO classes)将您的域层传输到 client/browser.如果你不这样做,在你解决延迟异常之后,你会遇到从 Parent 到 Child 的循环依赖问题,并且 JSON 编组器(Jackson)将尝试编码a Child
,然后是 Parent
,然后是 children,然后是 Parent
,依此类推。 DTO 的一个例子是:
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.
}
解决方案 2:为您的 public 属性 Parent.getChildren()
使用 @JsonIgnore,这样 Jackson 在编组Parent
实例。
如果您希望根据用例允许不同的 JSON 表示同一域模型,那么您可以查看以下内容,无需 DTO 即可实现:
https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
或者,另请参阅以下
中的 'Projections in Spring Data REST' 部分https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra#projections-in-spring-data-rest