1

I'm having a problem exposing a relationship to a sub-type via rest. I have an abstract class called Page:

@NodeEntity
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "category", visible = true)
@JsonSubTypes({ @Type(value = Musician.class), @Type(value = Book.class),
        @Type(value = Song.class) })
public abstract class Page extends BaseEntity{

    @Fetch
    @CreatedBy
    private User creator;

    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @GraphProperty(propertyType = Long.class)
    @CreatedDate private LocalDateTime timeCreated;

    @NotEmpty
    @Size(min = 1, max = 160)
    @Indexed(indexType = IndexType.FULLTEXT, indexName = "search")
    private String screenname;

    @Fetch
    @RelatedTo(type = "CHANNEL")
    private Channel channel = new Channel();

    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }

    public String getScreenname() {
        return screenname;
    }

    public void setScreenname(String screenname) {
        this.screenname = screenname;
    }

    // This is to work around the bug where type name is not exported by SDR.
    @JsonGetter(value = "category")
    public String getType() {
        return this.getClass().getSimpleName();
    }

    public User getCreator() {
        return creator;
    }

    public void setCreator(User creator) {
        this.creator = creator;
    }

    public LocalDateTime getTimeCreated() {
        return timeCreated;
    }

    public void setTimeCreated(LocalDateTime timeCreated) {
        this.timeCreated = timeCreated;
    }

}

Two subtypes Song and Musician:

@NodeEntity
@JsonTypeName("Song")
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="category", visible=true)
public class Song extends Page {

    @Fetch
    @RelatedTo(type = "SINGER")
    private Musician singer;

    public Musician getSinger() {
        return singer;
    }

    public void setSinger(Musician singer) {
        this.singer = singer;
    }

}

and

@NodeEntity
@JsonTypeName("Musician")
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="category", visible=true)
public final class Musician extends Page {

}

And a repository that is supposed to manage all subtypes of Page:

@RepositoryRestResource(collectionResourceRel = "pages", path = "pages")
public interface PageRepository extends PagingAndSortingRepository<Page, Long> {

    org.springframework.data.domain.Page<Musician> findMusicianByScreennameLike(@Param("0") String screenname, Pageable page);

}

When I get a Song instance from my api the json looks like:

{
  "uuid" : "ee9daf8b-4285-45bb-a583-e37f54284c43",
  "timeCreated" : null,
  "screenname" : "songtest",
  "singer" : null,
  "id" : 213,
  "category" : "Song",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/pages/213"
    },
    "channel" : {
      "href" : "http://localhost:8080/api/pages/213/channel"
    },
    "creator" : {
      "href" : "http://localhost:8080/api/pages/213/creator"
    }
  }
}

The problem is that the singer field appears embedded. And I can not associate an existing musician to this singer field. When I try to assign a uri of an existing musician to the singer field, it complains that it can not convert from String to Musician. If I provide json instead of uri, then it creates a new musician with same field values. So when the subtype Musician is referenced from an entity, it is treated as if it is not managed by it's supertype's repository. How can I make it so that the singer is exported like other associated top level resources under the links section and can be assigned an existing resource by accepting a uri? Or is it not possible at all?

Andrea
  • 15,900
  • 18
  • 65
  • 84
aycanadal
  • 1,106
  • 2
  • 15
  • 42

1 Answers1

2

I believe you'll need separate repositories for Musician, Book, and Song for Spring Data Rest to correctly determine the relationships between the entities. Once that's fixed, it should behave the way you expect - returning links for embedded entities instead of JSON, and accepting uri's when posting embedded entities.

You might want to take a look at this answer for an idea of how to handle inheritance in your Repository definitions:

https://stackoverflow.com/a/27549198/4601679

Community
  • 1
  • 1
TLA
  • 166
  • 1
  • 3
  • Yes but I'm looking for a way to manage all subclasses from one repository. Right now it does support retrieval by type and creation of different types on the same repository. It's weird that the relationships among subtypes appear embedded. If you can provide reference from a reliable source that this can not be fine tuned, I'll accept your answer. – aycanadal Jul 07 '15 at 17:52
  • 1
    I don't see anything official saying it's impossible, but I do see something saying it's not recommended. According to the spring-data-neo4j documentation here: http://docs.spring.io/spring-data/data-neo4j/docs/current/reference/html/#composing_repositories "The recommended way of providing repositories is to define a repository interface per domain class." – TLA Jul 07 '15 at 18:42
  • Yes but it continues: "... The mechanisms provided by the repository infrastructure will automatically detect them, along with additional implementation classes, ...". – aycanadal Jul 08 '15 at 00:07
  • I will of course accept your answer if I can't find a better answer. Thanks for your attention. – aycanadal Jul 08 '15 at 00:08
  • It's also weird that one repository for every class is recommended as only aggregate roots are supposed to have a dedicated repository. – aycanadal Jul 08 '15 at 08:40