与spring数据jpa的自引用关系

Posted

技术标签:

【中文标题】与spring数据jpa的自引用关系【英文标题】:self referencing relationship with spring data jpa 【发布时间】:2019-07-28 06:47:01 【问题描述】:

我正在使用 Spring Boot 构建应用程序,并且必须设置自引用关系。

为了使场景简单易懂,我尝试使用部门实体示例来处理类似的场景。

以下是我需要设置自引用关系的场景

    使用以下属性创建部门 - 名称、成本中心、位置。 部门可以与同样属于部门类型的父部门相关 一个部门可以有同样属于部门类型的子部门(子部门)。

为了设置这样的场景,我通过以下方式定义了实体部门。

Department.java

public class Department 

    @Id
    @GenericGenerator(name = "sequence_department_id", strategy = "com.app.mycompany.AgileCenterServices.util.DepartmentIdGenerator")
    @GeneratedValue(generator = "sequence_department_id")
    @Column(unique = true)
    private String id;

    private String name;

    private String location;

    private String costCenter;

    @ManyToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="parentDepartment")
    private Department parentDepartment;

    @OneToMany(mappedBy="parentDepartment")
    private Set<Department> linkedDepartments = new HashSet<Department>();

    /* getters & setters */


DepartmentController.java

@CrossOrigin
    @RequestMapping(value = "/", method = RequestMethod.POST)
    public Department createDepartment(@RequestBody String trial) throws Exception 

        logger.info("Inside createDepartment() API ");

        ObjectMapper objmapper = new ObjectMapper();

        ObjectNode node = objmapper.readValue(trial, ObjectNode.class);

        Department deptInput = objmapper.convertValue(node, Department.class);

        Department deptRec = null;

        /* check if parent department information was passed */
        if(deptInput.getParentDepartment() != null) 
            Department parentDepartment = departmentRepository.findOne(deptInput.getParentDepartment().getId());
            deptInput.setParentDepartment(parentDepartment);

        

        try 

            logger.info("createDepartment() :: Before save :: Input data ::: " + deptInput.toString());

            deptRec = departmentRepository.save(deptInput);

            logger.info("createDepartment() :: After save :: Saved successfully ::: " + deptRec.toString());
         
        catch (Exception ex) 
            ex.printStackTrace();
            throw ex;
        

        logger.info("Leaving createDepartment() API");

        return deptRec;
    

目前,我刚刚尝试将部门链接到另一个 parentDepartment,如上例所示,并尝试使用 spring boot-REST 服务创建部门

部门正在适当地创建。

Saved Department 1 with following input
"name":"Sales", "costCenter": "SLS", "location":"Global"
Output:

    "id": "1000",
    "name": "Sales",
    "location": "Global",
    "costCenter": "SLS",
    "parentDepartment": null,
    "linkedDepartments": []


    Saved department 2 with following input
    "name":"Sales-IN", "costCenter": "SLS-IN", "location":"India", "parentDepartment":"id":"1000"

  Output:   
    
        "id": "1001",
        "name": "Sales-IN",
        "location": "India",
        "costCenter": "SLS-IN",
        "parentDepartment": 
            "id": "1000",
            "name": "Sales",
            "location": "Global",
            "costCenter": "SLS",
            "parentDepartment": null,
            "linkedDepartments": []
        ,
        "linkedDepartments": []
    

但是,当我现在使用 postman 查询部门中的数据时,我注意到以下异常

@CrossOrigin
    @RequestMapping(value="/", method = RequestMethod.GET)
    public Page<Department> listDepartments(Pageable pageable) 

        logger.info("Inside listDepartments() API");

        return departmentRepository.findAll(pageable);
    

例外

2019-03-06 20:04:12.190  WARN 19520 --- [io-8080-exec-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by handler execution: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (***Error); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (***Error) (through reference chain: com.app.mycompany.AgileCenterServices.entities.Department["linkedDepartments"]->org.hibernate.collection.internal.PersistentSet[0]->com.app.mycompany.AgileCenterServices.entities.Department["parentDepartment"]->com.app.mycompany.AgileCenterServices.entities.Department["linkedDepartments"]->org.hibernate.collection.internal.PersistentSet[0]->com.app.mycompany.AgileCenterServices.entities.Department["parentDepartment"]->com.app.mycompany.AgileCenterServices.entities.Department["linkedDepartments"]

为了解决上述问题,我在“linkedDepartments”属性上设置了@JsonBackReference,之后“GET”操作正常工作。但是现在保存操作失败了

2019-03-06 20:19:03.176  WARN 19520 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by handler execution: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.***Error

我在这里做错了什么?

【问题讨论】:

【参考方案1】:

我试过你的代码,只要你在linkedDepartments或parentDepartment上有@JsonBackReference,它就不会被序列化,这解决了无限递归。保存方法也可以正常工作。我可以将代码发布到 GitHub 供您参考。告诉我。

【讨论】:

这是答案还是评论? 嗨罗尼。我很想看看你的代码。提前感谢您的帮助 @tomrlh,它更像是一条评论。我会转换它。谢谢! @svijay.aug12,这是我的Github repo @svijay.aug12,GitHub 代码对您的问题有帮助吗?如果没有,请告诉我。 tomrlh,抱歉,我似乎无权将答案转换为 cmets。

以上是关于与spring数据jpa的自引用关系的主要内容,如果未能解决你的问题,请参考以下文章

spring jpa ManyToMany 理解和使用

Spring Data JPA - JpaRepository 中的自定义排序

Spring Data Jpa系列教程--------实体解析和关联关系

用于在 Spring Data Jpa 中从多个表中获取数据的自定义查询

Spring Data JPA

Spring-Data-JPA ManyToMany 与额外列的关系