指定是否使用Spring Data延迟加载

时间:2015-03-06 09:56:56

标签: java spring hibernate jpa spring-data

我在实体中有一个懒惰的获取类型集合。我正在使用Spring Data(JpaRepository)来访问实体。

@Entity
public class Parent{
@Id
private Long id;

    @OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY)
    private Set<Child> children;
}

我想在服务类中使用两个函数,当前实现如下:

  1. &#34;的子&#34;获取父

    时应为null
    public Parent getParent(Long parentId){
        return repo.findOne(parentId);
    }
    
  2. &#34;的子&#34;在获取父级时应填写:

     public Parent getParentWithChildren(Long parentId){
         Parent p = repo.findOne(parentId);
         Hibernate.initialize(p.children);
         return p;
    }
    
  3. 返回&#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;])

4 个答案:

答案 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);
}

所以,你甚至不需要实现:

  1. 的getParent
  2. getParentWithChildren
  3. 这些方法可以由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()实例时不会尝试对子项进行编码。