Reputation: 590
I have a problem with Spring Data Rest being used with Spring Data JPA. I'm using Spring-boot 1.4.4.RELEASE.
Here is my spring-data-rest repository:
public interface ProfileJpaRepository extends JpaRepository<Profile, Long> {
}
Here are my entities (getters and setters not shown for brevity).
Profile.java:
@Entity
@Table(name = "PROFILE")
public class Profile {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String description;
// Don't use mappedBy="profile" because attributes are not persisted properly
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "PROFILE_ID")
private Set<Attribute> attributes;
...
}
Attribute.java
@Entity
@Table(name = "ATTRIBUTE")
public class Attribute {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name = "ID")
private Long id;
private String uri;
@ManyToOne(fetch = FetchType.EAGER)
private Profile profile;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name="ATTRIBUTE_DATAS")
private List<String> datas = new ArrayList<>();
public Attribute() {}
public Attribute(String uri, List<String> datas) {
this.uri = uri;
this.datas = datas;
}
...
}
The POST on "http://localhost:8880/profiles" to create the entity:
{
"description" : "description-value",
"attributes" : [
{
"uri" : "uri-a",
"datas" : ["uri-a-value"]
},
{
"uri" : "uri-b",
"datas" : ["uri-b-value"]
}
]
}
And here is the result when I hit http://localhost:8880/profiles :
{
"_embedded" : {
"profiles" : [ {
"description" : "description-value",
"attributes" : [ {
"uri" : "uri-a",
"datas" : [ "uri-a-value" ],
"_links" : {
"profile" : {
"href" : "http://localhost:8880/profiles/1"
}
}
}, {
"uri" : "uri-b",
"datas" : [ "uri-b-value" ],
"_links" : {
"profile" : {
"href" : "http://localhost:8880/profiles/1"
}
}
} ],
"_links" : {
"self" : {
"href" : "http://localhost:8880/profiles/1"
},
"profile" : {
"href" : "http://localhost:8880/profiles/1"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8880/profiles"
},
"profile" : {
"href" : "http://localhost:8880/profile/profiles"
}
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
I think there is a problem because the "_links"
are specified under every attributes. Rather, I would have expected something like this:
{
"_embedded" : {
"profiles" : [ {
"description" : "description-value",
"attributes" : [ {
"uri" : "uri-a",
"datas" : [ "uri-a-value" ]
}, {
"uri" : "uri-b",
"datas" : [ "uri-b-value" ]
} ],
"_links" : {
"self" : {
"href" : "http://localhost:8880/profiles/1"
},
"profile" : {
"href" : "http://localhost:8880/profiles/1"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8880/profiles"
},
"profile" : {
"href" : "http://localhost:8880/profile/profiles"
}
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
Note that I have been switching from MongoRepository to JpaRepository, and using MongoRepository, these "_links"
were not "duplicated".
Can someone shed some light on this ? Did I misconfigure something on my JPA entities ? Do I need to configure something on the rest repository ?
More information about dependency versions can be found here should you need it, I didn't override these (http://docs.spring.io/spring-boot/docs/1.4.4.RELEASE/reference/html/appendix-dependency-versions.html)
Thanks.
Upvotes: 3
Views: 989
Reputation: 590
To solve the problem I have been using Projections (more information here: http://docs.spring.io/spring-data/rest/docs/current/reference/html/#projections-excerpts). I wasn't aware of that feature when posting.
I had to annotate my repository and tell it to use my InlineAttributes projection:
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource(excerptProjection = InlineAttributes.class)
public interface ProfileJpaRepository extends JpaRepository<Profile, Long> {
}
On the Attribute class I had to add the @JsonIgnore annotation:
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = "ATTRIBUTE")
public class Attribute {
...
@ManyToOne(fetch = FetchType.EAGER)
@JsonIgnore
private Profile profile;
...
}
And the InlineAttributes projection class I had to create. I specified the order as it was not the same as before:
import org.springframework.data.rest.core.config.Projection;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@Projection(name = "inlineAttributes", types = { Profile.class })
@JsonPropertyOrder({ "description", "attributes" })
public interface InlineAttributes {
public String getDescription();
public Set<Attribute> getAttributes();
}
And then you need to apply the projection when you are calling the rest endpoint:
http://localhost:8880/profiles?projection=inlineAttributes
Upvotes: 1