Spring HATEOAS. Embedded resource + ability to CRUD it?

1k views Asked by At

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?

1

There are 1 answers

0
Dmitry On BEST ANSWER

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"
        }
  },
  ...
}