Search code examples
jsonspringspring-data-jpahateoasspring-hateoas

Spring HATEOAS. Embedded resource + ability to CRUD it?


In typical situation with embedded resources:

@Entity @Data
class Item {
    id, name
    ...
    @ManyToOne
    @JoinColumn(name="status", referencedColumnName="ID")
    private Status status;
}

@Entity @Data
class Status {
    id, name
    ...
    @JsonIgnore //break infinite reference loop during serialization
    @OneToMany(mappedBy="status")
    private List<Item> items;
}

Instead of having links to Status id's in Item JSON, I want to INCLUDE Status object in Item JSON

        {
            "itemName": "abc",
              ... ,
            "status": {
                "statusName":"ACTIVE",
                  ...
            }
            "_links": {
              ...
            }
        }

I managed embedding doing any of the following:

  • Marking Item class status property as @RestResource(exported=false)
    @Entity @Data
    class Item {
        ...
        @RestResource(exported=false) // <-- HERE
        @ManyToOne
        @JoinColumn(name="status", referencedColumnName="ID")
        private Status status;
  • Marking Status repo interface @RepositoryRestResource(..., exported=false)
    @RepositoryRestResource(collectionResourceRel="statuses", path="status", exported=false)
    public interface StatusRepository extends JpaRepository<Status, String>
  • Deleteting repository for Status entity
    // DELETED
    @RepositoryRestResource
    public interface StatusRepository extends JpaRepository<Status, String>{}

QUESTION:

Any of that embeds Status into Item JSON like I want, but I do not have an access to Status Repository anymore to get a Status object by it's ID or do any CRUD on it.

How to embed status in parent Item JSON and still CRUD status via url?


Solution

  • I implemented it as follows:

    @Entity @Data
    class Item {
        @Id
        @GeneratedValue(generator = "uuid2")
        @GenericGenerator(name = "uuid2", strategy = "uuid2")
        String id;
        String name
    
        @RestResource(exported = false) // <---- ADDED
        @ManyToOne
        @JoinColumn(name="status", referencedColumnName="ID")
        private Status status;
    }
    
    @Entity @Data
    class Status {
        @Id
        @GeneratedValue(generator = "uuid2")
        @GenericGenerator(name = "uuid2", strategy = "uuid2")
        String id
        String name
    
        // <-------- DELETED REFERENCE TO PARENT OBJECT
        //@OneToMany(mappedBy="status")
        //private List<Item> items;
    }
    

    Repositories for both Entities exist so it is possible to do CRUD on Status entity as well

    @RepositoryRestResource(collectionResourceRel="items", path="items")
    public interface StatusRepository extends JpaRepository<Item, String>
    {}
    
    @RepositoryRestResource(collectionResourceRel="statuses", path="statuses")
    public interface StatusRepository extends JpaRepository<Status, String>
    {}
    

    For future generations I'll leave it here.

    For convenience:

    1) Exposed ids for entities in JSON

    /**
     * Exposing Ids as properties for entities specified
     */
    @Configuration
    public class ExposeIdConfig extends RepositoryRestMvcConfiguration {
      @Override
      protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.exposeIdsFor(Item.class, Status.class);
      }
    }
    

    So getting list of items looks like:

    {
      "_links": {
        "self": {
          "href": "http://localhost:8080/app/items{?page,size,sort}",
          "templated": true
      }
    },
    "_embedded": {
      "as": [
        {
          "id": "29117425-f011-4ff9-8952-38b05d3df7f0",
          "name": "item 1",
          "status": {
            "id": "e9192ae7-29f8-4d5e-ad62-cad8d87de9e2",
            "name": "ACTIVE"
          },
          "_links": {
            "self": {
              "href": "http://localhost:8080/app/items/29117425-f011-4ff9-8952-38b05d3df7f0"
            }
      },
      ...
    }