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.