json 使用 jax-rs 和 jpa 的递归循环 stackoverflow

json recursive loop stackoverflow using jax-rs and jpa

我正在以正确的方式获取对象,但 JSON 响应出现了无穷无尽的递归问题。

实体

      @Entity
      public class ListItem {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
          @ManyToOne (cascade = CascadeType.ALL, fetch = FetchType.EAGER )
          private ListItem parentListItem;
          @OneToMany (mappedBy ="parentListItem", cascade = CascadeType.ALL, fetch = FetchType.EAGER )
          private List<ListItem> childListItemList;

      }

      public ListItem getMenuListById(Long id){
          ListItem listItem = em.find(ListItem.class, id);
          log("from DAO, listItem parent name: "+ listItem.getName());
          List<ListItem> listItemLevel2List = listItem.getChildListItemList();
          recurse(listItemLevel2List);


          return listItem;
          }
          public void recurse(List<ListItem> listItemList){
          log("from DAO, level2 list item childres size: "+listItemList.size());
          if(listItemList.size() > 0){
              for(ListItem listItem : listItemList){
              log("from DAO, listItem name: "+ listItem.getName());
              log(" -> from DAO, listItem parent name: "+ listItem.getParentListItem().getName());
              List<ListItem> level2List = listItem.getChildListItemList();
              if(level2List.size() > 0){
                  recurse(level2List);
              }
              }
          }
          }

控制器

      @GET
          @Path("level1")
          @Produces ("application/json")
          public ListItem getMenuList(){
          ListItem listItem = listItemService.getMenuListById(1L);
          return listItem;
          }

控制台输出:似乎正确

      from DAO, listItem parent name: Baby Changing
      from DAO, level2 list item childres size: 6
      from DAO, listItem name: Changing Mats
        -> from DAO, listItem parent name: Baby Changing
      from DAO, listItem name: Nappy Stacker
      -> from DAO, listItem parent name: Baby Changing
      from DAO, listItem name: Potty Training
      -> from DAO, listItem parent name: Baby Changing
      07:10:37,576 INFO  [stdout] (default task-24) from DAO, listItem name: Nappies & Wipes
      -> from DAO, listItem parent name: Baby Changing
      07:10:37,576 INFO  [stdout] (default task-24) from DAO, listItem name: Baby Changing Bags
      -> from DAO, listItem parent name: Baby Changing
      from DAO, listItem name: Changing Stations
      -> from DAO, listItem parent name: Baby Changing

JSON 输出 -- 以错误的方式嵌入长递归对象 --

      http://pastebin.com/bHwbN1yh  (using paste bin as its too long)

错误

      org.jboss.resteasy.spi.UnhandledException: Response is committed, can't handle exception

      Caused by: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (WhosebugError) (through reference chain: goo.entities.ListItem["parentListItem"]->goo.entities.ListItem["childListItemList"]->org.hibernate.collection.internal.PersistentBag[0]->goo.entities.ListItem["parentListItem"]

      Caused by: java.lang.WhosebugError
          at java.lang.ClassLoader.defineClass1(Native Method)

数据库table - 看起来不错

json 输出视图 - 部分

pom.xml(查看我正在使用的库;只是为了确定)

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

        <modelVersion>4.0.0</modelVersion>

        <groupId>goo</groupId>
        <artifactId>goo</artifactId>
        <packaging>war</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>goo</name>

        <build>
        <finalName>goo</finalName>
        <plugins>
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.5.1</version>
            <inherited>true</inherited>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
            </plugin>
        </plugins>
        </build>



        <dependencyManagement>

        <dependencies>
            <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
            </dependency>



        </dependencies>
        </dependencyManagement>

        <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
            <!-- artifactId>jersey-container-servlet</artifactId -->
        </dependency>
        <!-- uncomment this to get JSON support
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
        </dependency>
        -->
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
            <version>2.22</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.6.2</version>
        </dependency>


        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

        <!--<dependency>-->
            <!--<groupId>org.jboss.ejb3</groupId>-->
            <!--<artifactId>jboss-ejb3-ext-api</artifactId>-->
            <!--<version>2.1.0</version>-->
            <!--<scope>provided</scope>-->
        <!--</dependency>-->

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
            <scope>provided</scope>



        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.3-1103-jdbc41</version>
        </dependency>

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
            <version>1.19</version>
        </dependency>

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.12</version>
        </dependency>


        </dependencies>
        <properties>
        <jersey.version>2.21</jersey.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jdk.version>1.7</jdk.version>
        <hibernate.version>5.0.1.Final</hibernate.version>
        </properties>
    </project>

更新

我使用了 @JsonBackReference@JsonManagedReference 注释,但它只是忽略了 childListIemList

    @Entity
    public class ListItem {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        @JsonManagedReference
        @ManyToOne (cascade = CascadeType.ALL, fetch = FetchType.EAGER )
        private ListItem parentListItem;
        @JsonBackReference
        @OneToMany (mappedBy = "parentListItem",  cascade = CascadeType.ALL, fetch = FetchType.EAGER )
        private List<ListItem> childListItemList;

@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id") 放在实体顶部 class 有所帮助。

使用 @JsonBackReference@JsonManagedReference 注释只是忽略了 childListItem

http://wiki.fasterxml.com/JacksonFeatureObjectIdentity 在 Jackson 2.0 之前,循环对象图(或更一般地,共享引用的处理)的处理仅限于 parent/child(双向)引用的特定情况。虽然这可用于支持数据库模型常见的直接一对多和一对一引用,但它不是通用解决方案。

由于 2.0 允许对 API 和内部接口进行更大的更改,因此计划为此版本提供更通用的解决方案