Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Rest: Detected multiple association links with same relation type

Regarding this question, I checked out Spring Data Rest Ambiguous Association Exception but couldn't get it to work for me.

As you can see in my code below, I added @RestResource annotation with rel equal to some other value.

Similar to the question above, POST requests work, but GET requests throw exception about multiple association links with the same relation type:

"Could not write JSON: Detected multiple association links with same relation type! Disambiguate association @org.springframework.data.rest.core.annotation.RestResource(rel=createdBy, exported=true, path=, description=@org.springframework.data.rest.core.annotation.Description(value=)) @javax.persistence.ManyToOne(optional=true, targetEntity=void, cascade=[], fetch=EAGER) @javax.persistence.JoinColumn(referencedColumnName=ASSIGNABLE_ID, nullable=false, unique=false, name=CREATED_BY, updatable=true, columnDefinition=, [email protected](name=, value=CONSTRAINT, foreignKeyDefinition=), table=, insertable=true) private com.ag.persistence.domain.PersonEntity com.ag.persistence.domain.TeamEntity.createdBy using @RestResource! (through reference chain: org.springframework.hateoas.PagedResources[\"_embedded\"]->java.util.UnmodifiableMap[\"persons\"]->java.util.ArrayList[0]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Detected multiple association links with same relation type! Disambiguate association @org.springframework.data.rest.core.annotation.RestResource(rel=createdBy, exported=true, path=, description=@org.springframework.data.rest.core.annotation.Description(value=)) @javax.persistence.ManyToOne(optional=true, targetEntity=void, cascade=[], fetch=EAGER) @javax.persistence.JoinColumn(referencedColumnName=ASSIGNABLE_ID, nullable=false, unique=false, name=CREATED_BY, updatable=true, columnDefinition=, [email protected](name=, value=CONSTRAINT, foreignKeyDefinition=), table=, insertable=true) private com.ag.persistence.domain.PersonEntity com.ag.persistence.domain.TeamEntity.createdBy using @RestResource! (through reference chain: org.springframework.hateoas.PagedResources[\"_embedded\"]->java.util.UnmodifiableMap[\"persons\"]->java.util.ArrayList[0])"

The error seems to be happening in this class:

@Entity
@Table(name = "team")
public class TeamEntity extends AssignableEntity {
    private String name;
    private LocalDateTime createdDate;
    private LocalDateTime modifiedDate;
    private Collection<MembershipEntity> memberships;
    private PersonEntity createdBy;
    private PersonEntity modifiedBy;

    @Basic
    @Column(name = "NAME")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Basic
    @Column(name = "CREATED_DATE")
    public LocalDateTime getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(LocalDateTime createdDate) {
        this.createdDate = createdDate;
    }

    @Basic
    @Column(name = "MODIFIED_DATE")
    public LocalDateTime getModifiedDate() {
        return modifiedDate;
    }

    public void setModifiedDate(LocalDateTime modifiedDate) {
        this.modifiedDate = modifiedDate;
    }

    @OneToMany(mappedBy = "team")
    public Collection<MembershipEntity> getMemberships() {
        return memberships;
    }

    public void setMemberships(Collection<MembershipEntity> memberships) {
        this.memberships = memberships;
    }

    @RestResource(rel = "team_createdBy")
    @ManyToOne
    @JoinColumn(name = "CREATED_BY", referencedColumnName = "ASSIGNABLE_ID", nullable = false)
    public PersonEntity getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(PersonEntity createdBy) {
        this.createdBy = createdBy;
    }

    @RestResource(rel = "team_modifiedBy")
    @ManyToOne
    @JoinColumn(name = "MODIFIED_BY", referencedColumnName = "ASSIGNABLE_ID", nullable = false)
    public PersonEntity getModifiedBy() {
        return modifiedBy;
    }

    public void setModifiedBy(PersonEntity modifiedBy) {
        this.modifiedBy = modifiedBy;
    }
}

Ironically, I am not accessing this specific resource. I also have other resources with createdBy and modifiedBy-- is it the one causing this issue?

like image 263
Chad Avatar asked Sep 14 '14 10:09

Chad


2 Answers

I suspect that you might have exported = false on your PersonEntity object. Because of this, any references in PersonEntity will be added to the _links section of your TeamEntity. Because both PersonEntity objects have a createdBy reference, they are colliding with the TeamEntity.createdBy reference.

To understand what is happening this example might help:

class Answer {
   Question q; //JPA ManyToOne back-reference
}

class Question {
   List<Answer> as; // JPA OneToMany reference
}

class UserAnswer {
   Answer a;
   Question q;
}

In my case, because an Answer can only exist within a Question, we have the following on our AnswerResource, to prevent Answers from being exported:

@RestResource(exported = false)

This causes Answer objects to be serialized within the parent object, rather than as a reference in the _links section, and this ends up being the cause of the problem....

When UserAnswer is serialized, it renders something like this:

{
  "answer" : {
    "creationDate" : "2014-09-18T17:28:31.000+0000",
    "modificationDate" : "2014-09-18T17:28:31.000+0000",
    "answerText" : "Vendas",
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9090/data/userAnswers/15"
    },
    "question" : {
      "href" : "http://localhost:9090/data/userAnswers/15/question"
    }
  }
}

Note that "_links.question" above is from the Answer!

So if you add a Question to UserAnswer, you will see the error you are asking about, because the UserAnswer itself also wants to include a _link reference to a Question.

In your case, I think your PersonEntity and your TeamEntity both have createdBy references.

I'm not 100% sure what the solution is yet, I don't know if you can specify hierarchical rel names.

like image 83
JBCP Avatar answered Nov 11 '22 23:11

JBCP


Short answer: Add Spring Data repositories for ALL (or at least more of) your entities. (In this case, it sounds like you need to make a PersonRepository).

Long answer:

Spring Data Rest uses _links to avoid sending back the entire JSON tree, but it can only make _links to mapped entities that have Repositories.

If there isn't a repository for an entity, Spring can't use a link and will instead just put the whole JSON object in the response.

I THINK what's happening with this exception is that a lower, second level relationship is being put into a _link, and more than one of these have the same name.

I'm not 100% sure I understand it all, but I had the same exception when only had repositories for a few entities, and when I made a Repository for every single entity the problem vanished.

like image 1
LordBlackhole Avatar answered Nov 11 '22 23:11

LordBlackhole