Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在JPA实体序列化(JSON)上防止JAX-RS中的无限递归(不使用Jackson注释)_Java_Jpa_Jackson_Jax Rs_Resteasy - Fatal编程技术网

Java 在JPA实体序列化(JSON)上防止JAX-RS中的无限递归(不使用Jackson注释)

Java 在JPA实体序列化(JSON)上防止JAX-RS中的无限递归(不使用Jackson注释),java,jpa,jackson,jax-rs,resteasy,Java,Jpa,Jackson,Jax Rs,Resteasy,我有一个实体如下: @XmlRootElement @Entity @Table(name="CATEGORY") @Access(AccessType.FIELD) @Cacheable @NamedQueries({ @NamedQuery(name="category.countAllDeleted", query="SELECT COUNT(c) FROM Category c WHERE c.deletionTimestamp IS NOT NULL"), @Named

我有一个实体如下:

@XmlRootElement
@Entity
@Table(name="CATEGORY")
@Access(AccessType.FIELD)
@Cacheable
@NamedQueries({
    @NamedQuery(name="category.countAllDeleted", query="SELECT COUNT(c) FROM Category c WHERE c.deletionTimestamp IS NOT NULL"),
    @NamedQuery(name="category.findAllNonDeleted", query="SELECT c from Category c WHERE c.deletionTimestamp IS NULL"),
    @NamedQuery(name="category.findByCategoryName", query="SELECT c FROM Category c JOIN c.descriptions cd WHERE LOWER(TRIM(cd.name)) LIKE ?1")
})
public class Category extends AbstractSoftDeleteAuditableEntity<Integer> implements za.co.sindi.persistence.entity.Entity<Integer>, Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 4600301568861226295L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="CATEGORY_ID", nullable=false)
    private int id;

    @ManyToOne
    @JoinColumn(name="PARENT_CATEGORY_ID")
    private Category parent;

    @OneToMany(cascade= CascadeType.ALL, mappedBy="category")
    private List<CategoryDescription> descriptions;

    public void addDescription(CategoryDescription description) {
        if (description != null) {
            if (descriptions == null) {
                descriptions = new ArrayList<CategoryDescription>();
            }

            descriptions.add(description);
        }
    }

    /* (non-Javadoc)
     * @see za.co.sindi.entity.IDBasedEntity#getId()
     */
    public Integer getId() {
        // TODO Auto-generated method stub
        return id;
    }

    /* (non-Javadoc)
     * @see za.co.sindi.entity.IDBasedEntity#setId(java.io.Serializable)
     */
    public void setId(Integer id) {
        // TODO Auto-generated method stub
        this.id = (id == null) ? 0 : id;
    }

    /**
     * @return the parent
     */
    public Category getParent() {
        return parent;
    }

    /**
     * @param parent the parent to set
     */
    public void setParent(Category parent) {
        this.parent = parent;
    }

    /**
     * @return the descriptions
     */
    public List<CategoryDescription> getDescriptions() {
        return descriptions;
    }

    /**
     * @param descriptions the descriptions to set
     */
    public void setDescriptions(List<CategoryDescription> descriptions) {
        this.descriptions = descriptions;
    }
}

有一些问题需要回答,例如,需要使用特定于Jackson的注释。我的项目要求严格遵守JavaEE特定的框架。有没有一种解决方案可以用来防止无限递归而不使用Jackson注释?如果没有,我们可以创建一个配置文件(XML文件等),Jackson可以用它代替注释吗?原因是应用程序不仅必须绑定到特定于Wildfly的库。

是。创建专用数据结构(例如数据传输对象或DTO)并映射要从HTTP端点发送的字段

你的担忧混杂在一起,结果往往很糟糕


JPA实体是针对数据结构的API,REST表示(JSON或XML DTO)是REST API提供的数据负载。

我想说,您在这里有几个选项:

  • 使用
    transient
    关键字或让JAX-RS忽略某些属性/字段(它们不会被封送)
  • 使用DTO更好地为最终用户反映您的数据结构;随着时间的推移,实体之间的差异、它在RDBMS中的建模方式以及您返回给用户的内容将越来越大
  • 使用上述两个选项的组合,并将某些字段标记为瞬态字段,同时提供其他“JAX-RS友好”访问器,例如,仅返回
    类别的ID,而不是整个对象
  • 除了
    @JsonIgnore
    之外,还有一些特定于Jackson的解决方案,如:

    • --
      @JsonView
      可用于以更灵活的方式实现相同的目的(例如,它允许您定义何时要返回没有依赖项的简化对象(仅相关对象的ID)以及何时返回整个对象;您可以指定要使用的视图,例如在JAX-RS入口点上
    • 这将在编组对象时识别循环依赖项,并防止无限递归(对象的第一次命中意味着将其作为一个整体,相同对象的每一次命中意味着仅放置其ID)
    我相信还有其他的解决方案,但是考虑到以上这些,我个人会长期使用DTO。您可以使用一些自动映射解决方案来帮助您完成这项讨厌的重复工作。

    这就是说,最好将向用户提供和接受的数据与内部数据分开。

    我没有混用任何东西。我的RESTful WS返回一个
    集合
    ,JBoss使用
    RESTEasy
    (JAX-RS实现)它在内部使用Jackson将我的实体转换为JSON字符串。这是在容器级别完成的,我无法控制它。@BuhakeSindi您可能误解了答案。基本上,答案是:“使用DTO”,通常我都会同意。DTO似乎是解决这个问题的完美方案。因此,在您的整个项目中,您根本没有使用任何第三方库,只使用javaee api?@peeskillet是的。我相信这是可能的,这个项目将证明这一点。
    @XmlRootElement
    @Entity
    @Table(name="CATEGORY_DESCRIPTION")
    @Access(AccessType.FIELD)
    @Cacheable
    public class CategoryDescription extends AbstractModifiableAuditableEntity<CategoryDescriptionKey> implements za.co.sindi.persistence.entity.Entity<CategoryDescriptionKey>, Serializable {
    
        /**
         * 
         */
        private static final long serialVersionUID = 4506134647012663247L;
    
        @EmbeddedId
        private CategoryDescriptionKey id;
    
        @MapsId("categoryId")
        @ManyToOne/*(fetch=FetchType.LAZY)*/
        @JoinColumn(name="CATEGORY_ID", insertable=false, updatable=false, nullable=false)
        private Category category;
    
        @MapsId("languageCode")
        @ManyToOne/*(fetch=FetchType.LAZY)*/
        @JoinColumn(name="LANGUAGE_CODE", insertable=false, updatable=false, nullable=false)
        private Language language;
    
        @Column(name="CATEGORY_NAME", nullable=false)
        private String name;
    
        @Column(name="DESCRIPTION_PLAINTEXT", nullable=false)
        private String descriptionPlainText;
    
        @Column(name="DESCRIPTION_MARKDOWN", nullable=false)
        private String descriptionMarkdown;
    
        @Column(name="DESCRIPTION_HTML", nullable=false)
        private String descriptionHtml;
    
        /* (non-Javadoc)
         * @see za.co.sindi.entity.IDBasedEntity#getId()
         */
        public CategoryDescriptionKey getId() {
            // TODO Auto-generated method stub
            return id;
        }
    
        /* (non-Javadoc)
         * @see za.co.sindi.entity.IDBasedEntity#setId(java.io.Serializable)
         */
        public void setId(CategoryDescriptionKey id) {
            // TODO Auto-generated method stub
            this.id = id;
        }
    
        /**
         * @return the category
         */
        public Category getCategory() {
            return category;
        }
    
        /**
         * @param category the category to set
         */
        public void setCategory(Category category) {
            this.category = category;
        }
    
        /**
         * @return the language
         */
        public Language getLanguage() {
            return language;
        }
    
        /**
         * @param language the language to set
         */
        public void setLanguage(Language language) {
            this.language = language;
        }
    
        /**
         * @return the name
         */
        public String getName() {
            return name;
        }
    
        /**
         * @param name the name to set
         */
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * @return the descriptionPlainText
         */
        public String getDescriptionPlainText() {
            return descriptionPlainText;
        }
    
        /**
         * @param descriptionPlainText the descriptionPlainText to set
         */
        public void setDescriptionPlainText(String descriptionPlainText) {
            this.descriptionPlainText = descriptionPlainText;
        }
    
        /**
         * @return the descriptionMarkdown
         */
        public String getDescriptionMarkdown() {
            return descriptionMarkdown;
        }
    
        /**
         * @param descriptionMarkdown the descriptionMarkdown to set
         */
        public void setDescriptionMarkdown(String descriptionMarkdown) {
            this.descriptionMarkdown = descriptionMarkdown;
        }
    
        /**
         * @return the descriptionHtml
         */
        public String getDescriptionHtml() {
            return descriptionHtml;
        }
    
        /**
         * @param descriptionHtml the descriptionHtml to set
         */
        public void setDescriptionHtml(String descriptionHtml) {
            this.descriptionHtml = descriptionHtml;
        }   
    }
    
    Caused by: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: za.co.sindi.unsteve.persistence.entity.Category["descriptions"]->org.hibernate.collection.internal.PersistentBag[0]->za.co.sindi.unsteve.persistence.entity.CategoryDescription["category"]->za.co.sindi.unsteve.persistence.entity.Category["descriptions"]->