1

I have three entities:

@Entity
public class Presentation {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @ManyToOne
    private User author;

    @ManyToMany
    @JoinTable(joinColumns = @JoinColumn(name = "PRESENTATION_ID", referencedColumnName = "ID"), inverseJoinColumns = @JoinColumn(name = "TAG_ID", referencedColumnName = "ID"))
    private Collection<Tag> tags;

}

@Entity
public class Tag {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(unique = true, nullable = false)
    private String name;
}

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(unique = true, nullable = false)
    private String name;
}

Each Entity has an own Repository:

@Repository
public interface PresentationRepository extends CrudRepository<Presentation, Long> {
}

@Repository
public interface TagRepository extends CrudRepository<Tag, Long> {
}

@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}

So a Presentation have an Author (User) and a list of Tags.

If I create or update a Presentation I have to associate the resources User and Tags to that. According to the documentation of Spring Data Rest - Association Resource it is possible to assign them using the header Content-Type: text/uri-list. But in this case I have to do multiple calls, one to create the Presentation, one to set the Author and another for the Tags, like that:

curl -i -X POST -H "Content-Type: application/json" -d '{"name": "P1"}' http://localhost:8080/api/presentations

curl -i -X PATCH -H "Content-Type: text/uri-list" -d "
http://localhost:8080/api/users/1" http://localhost:8080/api/presentations/1/author

curl -i -X PATCH -H "Content-Type: text/uri-list" -d "
http://localhost:8080/api/tags/1
http://localhost:8080/api/tags/2" http://localhost:8080/api/presentations/1/tags

I found a way to bypass the extra call for the Author and do that "inline" of the presentation call (thanks to Chetan Kokil):

// WORK:
curl -i -X POST -H "Content-Type: application/json" -d '{"name": "P1", "author": "http://localhost:8080/api/users/1"}' http://localhost:8080/api/presentations

So it saves me an additional call and according to my understanding it follows the ACID principle.

The same I would like to do with the tag list. So I tried the following call:

// DON'T WORK:
curl -i -X POST -H "Content-Type: application/json" -d '{"name": "P1", "author": "http://localhost:8080/api/users/1", "tags":["http://localhost:8080/api/tags/1","http://localhost:8080/api/tags/2"]}' http://localhost:8080/api/presentations

And I am getting following error message:

{
  "cause": {
    "cause": null,
    "message": "Can not construct instance of com.example.model.Tag: no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/tags/2')\n at [Source: N/A; line: -1, column: -1]"
  },
  "message": "Could not read payload!; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.example.model.Tag: no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/tags/2')\n at [Source: N/A; line: -1, column: -1]"
}

My question is, is it possible to make that and if so, what is the correct syntax?

Thanks all.

Community
  • 1
  • 1
Paul S.
  • 11
  • 3

1 Answers1

0

While writing my question I found the solution myself :)

Maybe it will help others.

curl -i -X PATCH -H "Content-Type: application/json" -d '{"name": "P1", "author": "http://localhost:8080/api/users/1", "tags":[["http://localhost:8080/api/tags/1","http://localhost:8080/api/tags/2"]]}' http://localhost:8080/api/presentations

If you want to "inline" set the association resources for a list/collection you have to put the URIs inside TWO arrays:

// WORK only for updating of a list that already contains at least one resource:
[["a/b/c", "a/b/c"]]

Only one array doesn't work.

// DON'T WORK: 
["a/b/c", "a/b/c"]`

UPDATE:

It looks like it only works if there are already some Tags associated. So that means this doesn't work while creating a new Presentation instance. If the list is empty, it gives me the following error:

Can not deserialize instance of com.example.model.Tag out of START_ARRAY token\n at [Source: N/A; line: -1, column: -1]"

Is it a bug? I am still searching for a solution.

Paul S.
  • 11
  • 3