I wanted to extend the example Accessing JPA Data with REST by adding an address list to the Person
entity. So, I added a list addresses
with @OneToMany
annotation:
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Address> addresses = new ArrayList<>();
// get and set methods...
}
The Address
class is a very simple one:
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String street;
private String number;
// get and set methods...
}
And finally I added the AddressRepository
interface:
public interface AddressRepository extends PagingAndSortingRepository<Address, Long> {}
Then I tried to POST a person with some addresses:
curl -i -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Frodo", "lastName" : "Baggins", "addresses": [{"street": "somewhere", "number": 1},{"street": "anywhere", "number": 0}]}' http://localhost:8080/people
The error I get is:
Could not read document: Failed to convert from type [java.net.URI] to type [ws.model.Address] for value 'street';
nested exception is java.lang.IllegalArgumentException: Cannot resolve URI street. Is it local or remote? Only local URIs are resolvable. (through reference chain: ws.model.Person[\"addresses\"]->java.util.ArrayList[1]);
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to convert from type [java.net.URI] to type [ws.model.Address] for value 'street'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI street. Is it local or remote? Only local URIs are resolvable. (through reference chain: ws.model.Person[\"addresses\"]->java.util.ArrayList[1])
Which is the proper method to create one to many and many to many relationships and post json objects to them?
Shouldn't your rest service be accepting a Person instead of an Address?
Or, maybe you are trying to make two different rest services, which I don't understand. You should only have one rest service that takes a Person which has address entries in it.
You should POST the two addresses first, then use their URLs returned (e.g. http://localhost:8080/addresses/1 and http://localhost:8080/addresses/2) in your Person POST:
If you want to save first the person and then add its addresses you could do this:
I managed to resolve this issue by not exporting the referenced repository. This is adding the annotation on top of the interface. In your example, it would be like that:
This resolves the issue partially as Spring Data will not still propagate the foreign keys for you. However, it will persist your Person and Address(without the reference to the person that belongs to). Then, if we made another call to the API to update these missing foreign keys, you would be able to get a person through the API with all its linked addresses - as @Francesco Pitzalis mentioned
I hope it helps out. Just a last note. I am still working on this because I consider ridiculous(as well as basic and needed) that Hibernate cannot propagate the foreign keys for us. It should be possible somehow.
EDITED: Indeed it was possible. The below implementation is able to persist an entity and its children propagating the foreign keys to them for an architecture based on Spring Data(Rest - as we are exposing the repositories), Hibernate 5.0.12Final and MySQL with storage engine InnoDB (not in memory database).
https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html - This was crucial.
This is extremely important. You need to know where Hibernate is running the SQL statements on to set the dialect properly. For me, the storage engine of my tables is InnoDB. The next link helped. What mysql driver do I use with spring/hibernate?
The only thing that I have not been able to explain is that, now, I can export the "child" repository and it still works fine. Any ideas, guys?