Spring-boot、one-to-many 关系处于同一 object(parent 和子女)

Spring-boot, one-to-many relationship in the same object (parent & childrens)

在我的 spring-boot 项目中,我想创建具有 parent 和 children 值的 object "Menu":
- 菜单可以有 一个 parent 元素
- 菜单可以有 一个或多个 childrens 元素

实体 Menu.java

@Entity
@Data @AllArgsConstructor @NoArgsConstructor
public class Menu implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany
    private List<Menu> childrens;

    @ManyToOne
    private Menu parent;

}

MenuDAO.java

@RepositoryRestResource
public interface MenuDAO extends JpaRepository<Menu, Long> {

}

DemoApplication.java 为使用 CommandLine Runner 进行测试添加数据:

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

   @Autowired
   MenuDAO menuDAO;

   public static void main(String[] args) {
       SpringApplication.run(DemoApplication.class, args);
   }

   @Override
   public void run(String... args) throws Exception {
       Menu m1 = menuDAO.save(new Menu(null,"Menu1",null,null));
       Menu m2 = menuDAO.save(new Menu(null,"Sub-Menu1",null,m1));

   }
}

但是当我通过 rest 服务调用获取 parent 元素时,我得到了这个结果 http://localhost:9090/menus/1:

{
 "name" : "Menu1",
 "_links" : {
   "self" : {
     "href" : "http://localhost:9090/menus/1"
   },
   "menu" : {
     "href" : "http://localhost:9090/menus/1"
   },
   "childrens" : {
     "href" : "http://localhost:9090/menus/1/childrens"
   },
   "parent" : {
     "href" : "http://localhost:9090/menus/1/parent"
   }
 }
}

但我的要求是获取以下 JSON 格式的数据:

{
"name" : "Menu1",
"childrens" : [{
                 "name" : "Menu2"
              }], 
"parent" : NULL,
"_links" : {
  "self" : {
    "href" : "http://localhost:9090/menus/1"
  },
  "menu" : {
    "href" : "http://localhost:9090/menus/1"
  },
  "childrens" : {
    "href" : "http://localhost:9090/menus/1/childrens"
  },
  "parent" : {
    "href" : "http://localhost:9090/menus/1/parent"
  }
}
}

有什么建议吗?

阅读 Spring 数据 REST 参考指南
的文档后,我终于有了答案 Spring Data REST 呈现您导出的域模型的默认视图。但如果出于各种原因我们可能需要更改该模型的视图,我们可以使用预测和摘录来提供简化和简化的资源视图。
在我的例子中,第一步是创建我的实体 Prejection 界面 Menu 并选择我想在 JSON响应。

@Projection(name = "MenuProjection", types = Menu.class)
public interface MenuProjection {
    Long getId();

    String getName();

    Menu getParent();

    List<Menu> getChildrens();

}

第二步是添加属性 excerptProjection of RepositroyRestRessource annotation with Menu.Class 我的 MenuRepository 界面中的值。
@RepositoryRestResource(excerptProjection = MenuProjection.class)
JSON 之前的响应

{
  "_embedded" : {
    "menus" : [ {
      "name" : "cat_test1",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9292/menus/1"
        },
        "menu" : {
          "href" : "http://localhost:9292/menus/1"
        },
        "parent" : {
          "href" : "http://localhost:9292/menus/1/parent"
        },
        "pages" : {
          "href" : "http://localhost:9292/menus/1/pages"
        },
        "children" : {
          "href" : "http://localhost:9292/menus/1/children"
        }
      }
    }, {
      "name" : "cat_test2",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9292/menus/2"
        },
        "menu" : {
          "href" : "http://localhost:9292/menus/2"
        },
        "parent" : {
          "href" : "http://localhost:9292/menus/2/parent"
        },
        "pages" : {
          "href" : "http://localhost:9292/menus/2/pages"
        },
        "children" : {
          "href" : "http://localhost:9292/menus/2/children"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9292/menus{?page,size,sort}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:9292/profile/menus"
    },
    "search" : {
      "href" : "http://localhost:9292/menus/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}

JSON 投影

{
  "_embedded" : {
    "menus" : [ {
      "name" : "cat_test1",
      "parent" : null,
      "id" : 1,
      "childrens" : [ {
        "name" : "cat_test2"
      } ],
      "_links" : {
        "self" : {
          "href" : "http://localhost:9292/menus/1"
        },
        "menu" : {
          "href" : "http://localhost:9292/menus/1{?projection}",
          "templated" : true
        },
        "childrens" : {
          "href" : "http://localhost:9292/menus/1/childrens{?projection}",
          "templated" : true
        },
        "parent" : {
          "href" : "http://localhost:9292/menus/1/parent{?projection}",
          "templated" : true
        },
        "pages" : {
          "href" : "http://localhost:9292/menus/1/pages"
        }
      }
    }, {
      "name" : "cat_test2",
      "parent" : {
        "name" : "cat_test1",
      },
      "id" : 2,
      "childrens" : [ ],
      "_links" : {
        "self" : {
          "href" : "http://localhost:9292/menus/2"
        },
        "menu" : {
          "href" : "http://localhost:9292/menus/2{?projection}",
          "templated" : true
        },
        "childrens" : {
          "href" : "http://localhost:9292/menus/2/childrens{?projection}",
          "templated" : true
        },
        "parent" : {
          "href" : "http://localhost:9292/menus/2/parent{?projection}",
          "templated" : true
        },
        "pages" : {
          "href" : "http://localhost:9292/menus/2/pages"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9292/menus{?page,size,sort,projection}",
      "templated" : true
    },
    "profile" : {
      "href" : "http://localhost:9292/profile/menus"
    },
    "search" : {
      "href" : "http://localhost:9292/menus/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}

因此,使用 Spring 数据投影,我们可以在实体资源中包含数据字段。