【问题标题】:adding excerpt projection to spring data rest repository causes stack overflow error将摘录投影添加到 Spring Data Rest 存储库会导致堆栈溢出错误
【发布时间】:2015-10-31 22:39:32
【问题描述】:

当我们在 REST 存储库上启用 except 投影时,我们将面临 StackoverflowError。实体是问题有两个关联,一个@ManyToOne 与一个Venue 实体必须包含在所有响应的内联中,以及一个@OneToMany 与Trainee 实体我们总是想隐藏。实体(相关)sn-p

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Workshop implements Serializable {

  private static final long serialVersionUID = -5516160437873476233L;

  private Long id;

  // omitted properties

  private Venue venue;

  private Set<Trainee> trainees;

  @Id
  @GeneratedValue
  public Long getId() {
    return id;
  }

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinTable(name = "workshop_venue", joinColumns = { @JoinColumn(name = "workshop_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "venue_id", referencedColumnName = "id") })
  public Venue getVenue() {
    return venue;
  }

  @OneToMany
  @JoinTable(name = "workshop_trainees", joinColumns = { @JoinColumn(name = "workshiop_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "trainee_id", referencedColumnName = "email") })
  @RestResource(exported = false)
  @JsonIgnore
  public Set<Trainee> getTrainees() {
    return trainees;
  }

  // omitted getters/setters
}

当我添加这个投影时

@Projection(name = "default", types = { Workshop.class })
public interface InlineVenueProjection {

  String getName();

  Integer getSeatsAvailable();

  WorkshopType getWorkshopType();

  Date getDate();

  Venue getVenue();
}

并在存储库中启用它

@RepositoryRestResource(collectionResourceRel = "workshop", path = "workshops", excerptProjection = InlineVenueProjection.class)
public interface WorkshopRepository extends JpaRepository<Workshop, Long> {
// omitted methods
}

我在 POST 请求中遇到堆栈溢出错误

org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.StackOverflowError

在堆栈跟踪中进一步

Caused by: java.lang.StackOverflowError: null
    at java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter.initialValue(ReentrantReadWriteLock.java:286)
    at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:180)
    at java.lang.ThreadLocal.get(ThreadLocal.java:170)
    at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryReleaseShared(ReentrantReadWriteLock.java:423)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(AbstractQueuedSynchronizer.java:1341)
    at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.unlock(ReentrantReadWriteLock.java:881)
    at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:169)
    at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:140)
    at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:67)
    at org.springframework.data.mapping.context.PersistentEntities.getPersistentEntity(PersistentEntities.java:63)
    at org.springframework.data.rest.webmvc.mapping.LinkCollectingAssociationHandler.doWithAssociation(LinkCollectingAssociationHandler.java:100)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:352)

【问题讨论】:

    标签: spring spring-data spring-data-jpa spring-data-rest


    【解决方案1】:

    您收到堆栈溢出异常的原因是因为我猜Venue (1-Many) 和 Workshop (Many-1) 之间定义了双向关系。你可以证实我的假设。

    当您尝试直接序列化场所时,因为关系研讨会已加载并且它具有对场所的引用,因此无休止的递归。

    解决方案

    为了解决这个问题,Jackson 有@JsonManagedReference 和@JsonBackReference。

    工作坊课堂

    public class Workshop implements Serializable {
        ...
    
        @ManyToOne(fetch = FetchType.EAGER)
        @JoinTable(name = "workshop_venue", joinColumns = { @JoinColumn(name = "workshop_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "venue_id", referencedColumnName = "id") })
        @JsonManagedReference
        public Venue getVenue() {
            return venue;
        }
    
        ...
    }
    

    场地课

    public class Venue implements Serializable {
        ...
    
        @OneToMany
        @JsonBackReference
        public List<Workshop> getWorkshops() {
            return workshops;
        }
    
        ...
    }
    

    希望这会有所帮助。让我知道这是否有效。

    【讨论】:

    • 有没有办法在不使用双向关系的情况下解决这个问题?我通过了StackoverflowError,但现在,内联更加复杂,因为当我们获取Venue 时,它总是显示Workshops,考虑到我们的用例,这不是一个好主意。
    • 尝试让@OneToMany 关系变得懒惰。这样当您获取 Venue.Workshops 时不会获取。如果这不起作用,则使用@JsonView 来控制字段的呈现。
    猜你喜欢
    • 2019-01-27
    • 2011-02-08
    • 1970-01-01
    • 2020-09-14
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    相关资源
    最近更新 更多