I'm building a REST API with Spring Boot to retrieve boat information. I'm using Spring Data Rest and Spring Data JPA. When I get the data from the API, I don't know why the relationship data are not with the others informations.
Do I have to configure something in Spring to get the relationship with my data ?
Here is my file.
Boat entity:
#Entity
#Table(name="boat")
#Data
public class Boat {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "type_id", nullable = false)
#JsonBackReference
private BoatType type;
}
Boat type entity :
#Entity
#Table(name = "boat_type")
#Data
public class BoatType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "type")
#JsonManagedReference
private Set<Boat> boats;
}
Boat repository :
#CrossOrigin("http://localhost:4200")
public interface BoatRepository extends JpaRepository<Boat, Long> {
}
JSON response :
{
"_embedded": {
"boats": [
{
"id": 1,
"name": "Boat 1",
"description": "A brief description of the boat 1",
"_links": {
"self": {
"href": "http://localhost:8080/api/boats/1"
},
"boat": {
"href": "http://localhost:8080/api/boats/1"
},
"type": {
"href": "http://localhost:8080/api/boats/1/type"
}
}
},
...
]
}
Result expected (with the type object too) :
{
"_embedded": {
"boats": [
{
"id": 1,
"name": "Boat 1",
"description": "A brief description of the boat 1",
"type": {
"id": 1,
"name": "Motorboats"
},
"_links": {
"self": {
"href": "http://localhost:8080/api/boats/1"
},
"boat": {
"href": "http://localhost:8080/api/boats/1"
},
"type": {
"href": "http://localhost:8080/api/boats/1/type"
}
}
},
...
]
}
I think that the problem is related with Spring Data Rest because when i do the same app with my own controller and repository, i get the data I need.
Is there a way to "configure" spring data rest?
It seems like you've used #JsonBackReference and #JsonManagedReference the other way around, than you needed. You've put #JsonBackReference on the type field in your Boat class, whereas its documentation states:
[...] Linkage is handled such that the property annotated with this annotation is not serialized
So it seems like you need to put #JsonManagedReference annotation on it instead (see: JsonManagedReference documentation) and put #JsonBackReference on boats in your BoatType class.
Alternatively, you could consider using #JsonIdentityInfo instead. See: the documentation.
Also, this article might be helpful. It explains various ways to handle bidirectional relationships using Jackson.
Change #JsonManagedReference and #JsonBackReference to #JsonIgnoreProperties.
In your case:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "type")
#JsonIgnoreProperties(value = {"type"})
private Set<Boat> boats;
and
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "type_id", nullable = false)
#JsonIgnoreProperties(value = {"boats"})
private BoatType type;
You will avoid the infinity loop in json result and get all reference objects (relationships).
The Boat response includes a uri to your BoatType resource by default since you defined a rest repository for your BoatType resource (docs)
To override this behaviour, define a projection to expose the boat type data (docs):
#Projection(name = "boatDetail", types = { Boat.class })
interface BoatDetail {
// ... all other fields you want included in the response
BoatType getType();
}
Then include the projection as a query parameter:
{apiUrl}boats/1?projection=boatDetail
The response should now include the boat type data:
{
"id": 1,
"name": "Boat 1",
"description": "A brief description of the boat 1",
"type": {
"id": 1,
"name": "Motorboats"
},
"_links": {
// ...
}
}
To automatically include the projection on a collection of resources, use an excerpt (docs):
#RepositoryRestResource(excerptProjection = BoatDetail.class)
interface BoatRepository extends JpaRepository<Boat, Long> {}
Then the http response:
{
"_embedded":{
"boats":[
{
"id":1,
"name":"Boat 1",
"description":"A brief description of the boat 1",
"type":{
"id":1,
"name":"Motorboats"
},
"_links":{
//...
}
},
// ...
]
}
}
Related
I have a Film model like below:
public class Film{
private String name;
private String category;
private Actor[] actors;
private MarketingPlan marketingPlan;
}
class Actor{
private String name;
private double salary;
}
class MarketingPlan{
private String nameOfPlan;
private Country[] affectedCountries;
}
class Country{
private int countryID;
private String marketingText;
}
which generate a JSON like this:
{
"name": "My First Film",
"category": "action",
"marketingPlan": {
"name": "plan1",
"affectedCountries": [
{
"marketingText": "This is a marketing text",
"countryID": 332
}
]
},
"actors": [
{
"name": "John",
"salary": 123456
}
]
}
I would like to represent my JSON result above as tree structure in JavaFX. Can anyone give me some hint or suggest me some tutorial how can I do it. Thank you very much in advanced!
Course.java
#Entity
#Table
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Course {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotEmpty
private String description;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "subject_course", joinColumns = #JoinColumn(name = "subject_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "course_id", referencedColumnName = "id"))
private Set<Subject> subjects = new HashSet<Subject>();
---- getter/setter ----
Subject.java
#Entity
#Table
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Subject {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String description;
#ManyToMany(mappedBy = "subjects", fetch=FetchType.EAGER)
#Cascade({CascadeType.DELETE, CascadeType.SAVE_UPDATE})
private Set<Course> courses = new HashSet<Course>();
---- getter/setter ----
Request configuration in Spring:
#RequestMapping(value = "/courses", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<?> getAllCourses() {
List<Course> courses = courseService.getAllCourses();
if (courses.isEmpty()) {
return new ResponseEntity<Message>(new Message("error", "No course found!"), HttpStatus.NOT_FOUND);
}
return new ResponseEntity<List<Course>>(courses, HttpStatus.OK);
}
Hibernate Version: 4.2.0.Final
Spring Version: 3.2.8.RELEASE
Jackson:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.4</version>
</dependency>
Expecting O/P
[{
"id": 1,
"description": "BCA",
"subjects":[{
"id":1,
"description":"Physics",
"courses":[1,2,3] //Either show blank array or only ids
}]
},{
"id": 2,
"description": "BSC",
"subjects":[{
"id":1,
"description":"Physics",
"courses":[1,2,3]
}]
},{
"id": 3,
"description": "BA",
"subjects":[{
"id":1,
"description":"Physics",
"courses":[1,2,3]
}]
},]
But getting O/P:
[
{
"id": 1,
"description": "BCA",
"subjects": [
{
"id": 1,
"description": "Math",
"staffs": [],
"courses": [
{
"id": 4,
"description": "BDA",
"subjects": [
1
],
"students": []
},
{
"id": 3,
"description": "BBA",
"subjects": [
1
],
"students": []
},
1
],
"students": []
}
],
"students": [
{
"id": 1,
"name": "",
"age": 0,
"gender": null,
"course": 1,
"subjects": []
}
]
},
3,
4
]
As per actual o/p, it is stopping the recursion at second level. But my requirement is not to repeat the same objects data from child... It means Course must not repeat its data in Subject's course property. Similarly, if call the same from Subject then subject should not repeat Course subject property value. It is better to skip, if can't then just display id values separated by comma.
Please advise how to fix this issue.
You can use #JsonIgnore in the Subject class like this:
#ManyToMany(mappedBy = "subjects", fetch=FetchType.EAGER)
#Cascade({CascadeType.DELETE, CascadeType.SAVE_UPDATE})
#JsonIgnore
private Set<Course> courses = new HashSet<Course>();
colleagues!
We want to write Rest Client to service which follow the HATEOAS principle. So we have the following HAL+JSON representation and we want to deserialize it using spring-hateoas :
{
"id": "1",
"title": "album title",
"artistId": "1",
"stockLevel": 2,
"_links": {
"self": {"href": "http://localhost:8080/rest/albums/1"},
"artist": {"href": "http://localhost:8080/rest/artist/1"}
},
"_embedded": {
"albums": [{ //can be array or object
"id": "1",
"title": "album title",
"artistId": "1",
"stockLevel": 2,
"_links": {
"self": {"href": "http://localhost:8080/rest/albums/1"}
}
}],
"artist": { //can be array or object
"id": "1",
"name": "artist name",
"_links": {
"self": {"href": "http://localhost:8080/rest/artist/1"}
}
} //....
}
}
We expected the java object like this:
HalResource {
Resource<Album> //entity
List<Link> // _links
List<Resource<BaseEntity>>{ //_embedded
Resource<Album>
Resource<Artist>
....
}
}
So we have custom resource representation with embedded(list of resources) and entity(single resource):
#XmlRootElement(name = "resource")
public class HalResource<EntityType, EmbeddedType> extends Resources<EmbeddedType> {
#JsonUnwrapped
private EntityType entity;
public HalResource() {
}
public HalResource(Iterable<EmbeddedType> content, Link... links) {
super(content, links);
}
public EntityType getEntity() {
return entity;
}
public void setEntity(EntityType entity) {
this.entity = entity;
}
}
DTO classes:
public abstract class BaseEntity{}
#XmlRootElement(name = "album")
public class Album extends BaseEntity {
private String id;
private String title;
private String artistId;
private int stockLevel;
// getters and setters...
}
#XmlRootElement(name = "artist")
public class Artist extends BaseEntity {
private String id;
private String name;
// getters and setters...
}
And we want to get something like this, where Entity will be Artist or Album, but HalResourcesDeserializer return Resource.class with null content.
HalResource<Album, Resource<Entity>> resources =
restClient.getRootTarget().path("albums/1").queryParam("embedded", true).request().accept("application/hal+json")
.get(new GenericType<HalResource<Album, Resource<Entity>>>() {});
By using #JsonTypeInfo and #JsonSubTypes anotations we successfully deserialized our JSON(you can see the example on the github), but we don't want to have some additional type filds and anotattions in our DTO and JSON format.
We see one solution that is create a custom deserializer which can processing that.
So the question is: What is the convenient way to deserialize our JSON(links + embedded container) using spring-hateoas?
We use spring-hateoas 0.16v(but we tried 0.19v) and glassfish jersey 2.22.1
Thank you!
I have two entities: Category and Item.
Category Entity:
#JsonInclude(Include.NON_EMPTY)
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
#Entity
public class Category {
#Id
#GeneratedValue
private int id;
private String categoryName;
#OneToMany(mappedBy = "category")
private List<Item> itemList;
//have getters and setters
}
Item Entity:
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
#Entity
public class Item {
#Id
#GeneratedValue
private int id;
private String itemName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_Category_id")
private Category category;
//have getters and setters
}
After doing a left join query like this:
"SELECT c from Category c Left JOIN FETCH c.itemList il"
I got the result and then I have done hibernate aware JSON serialization to the result.
My HibernateAwareObjectMapper is:
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
Hibernate4Module hibernateModule = new Hibernate4Module();
hibernateModule.disable(Hibernate4Module.Feature.FORCE_LAZY_LOADING);
registerModule(hibernateModule);
}
}
And HibernateAwareSerializerFactory is:
public class HibernateAwareSerializerFactory extends BeanSerializerFactory {
protected HibernateAwareSerializerFactory(SerializerFactoryConfig config) {
super(config);
}
}
In my dispatcher servlet I have written:
<mvc:annotation-driven>
<mvc:message-converters>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.scinv.hibernateAwareMapper.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
So, I have got a JSON Like this:
{
"ArrayList": [
{
"#id": "971ef69e-1605-46f2-8234-b595e38be11a",
"id": 1,
"categoryName": "fruit",
"itemList": [
{
"#id": "75a3a7e5-ce66-4f6d-a04c-d04145d92b21",
"id": 1,
"itemName": "mango",
"category": "971ef69e-1605-46f2-8234-b595e38be11a"
},
{
"#id": "0aa0fb71-2909-4909-8403-0765829ee8c1",
"id": 2,
"itemName": "apple",
"category": "971ef69e-1605-46f2-8234-b595e38be11a"
},
{
"#id": "02c381cb-33fa-45a6-bff9-ec146357f4bc",
"id": 3,
"itemName": "orange",
"category": "971ef69e-1605-46f2-8234-b595e38be11a"
}
]
},
"971ef69e-1605-46f2-8234-b595e38be11a",
"971ef69e-1605-46f2-8234-b595e38be11a"
]
}
In this JSON,
there is "#id": "971ef69e-1605-46f2-8234-b595e38be11a"
which is Object Id for Category Entity.
And there is "#id": "75a3a7e5-ce66-4f6d-a04c-d04145d92b21"
which is Object Id for Item Entity.
Category has 3 items So Catagory's object Id is also showing 2 more times.
Like this: "971ef69e-1605-46f2-8234-b595e38be11a","971ef69e-1605-46f2-8234-b595e38be11a"
That Generated JSON serialization is as expected.
But I just need to hide that Object reference "#id" in that json. This "#id" is by default created by #JsonIdentityInfo.
So I need configuration for #JsonIdentityInfo to hide Object Id "#id".
I have tried #JsonIgnore but failed.
I have googled 2 days for solution but failed.Can anyone give me a solution or a suggestion?
I am trying out examples for Spring Data REST however the JSON object returned in my testing does not return the column names (which were earlier 'PUT') and just returns the links to the objects. What could be wrong?
Scenario:
Entity: 'User'
#Entity
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String guid;
private String fullName;
private String email;
}
Repository: UserRepository (Exposed as REST service)
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends JpaRepository<User, Long> {
}
REST 'PUT' request to create a USER object:
REST GET Call to get the JSON response for the User object (the problem)
No id, Guid or email is returned in the JSON response.
Removing lombok's #Data annotation made all basic properties appear in the JSON response. Looks like a side effect of lombok.
I had the same problem, but I was not using autogenerated Lombok classes.
I found a solution. The problem in what I was experiencing was that only links appear in the json output of a Spring Data Repository.
For example:
{
"_embedded": {
"users": [{
"_links": {
"self": {
"href": "http://localhost:8080/users/1"
},
"user": {
"href": "http://localhost:8080/users/1"
}
}
}, {
"_links": {
"self": {
"href": "http://localhost:8080/users/2"
},
"user": {
"href": "http://localhost:8080/users/2"
}
}
}, {
"_links": { ...
The Solution:
Add getters and setters to the Entity, one for each variable you want to show in the response. Once you add getters and setters, you will be able to run the application again, and get json responses that contain values for each of your variables.
In one of the other answers, the workaround was to remove Lombok #Data annotation. Lombok was not generating getters and setters for the class using that annotation in time, so, in turn, none of the returned JSON responses contained any initialized values.
The outcome looks better:
{
"_embedded" : {
"users" : [ {
"username" : "admin",
"firstName" : "Administrator",
"lastName" : "Administrator",
"roles" : [ { "roleName" : "ROLE_ADMIN", "description" : "Administrator"
} ],
"_links" : { "self" : { "href" : "http://localhost:8080/users/1" },
"user" : { "href" : "http://localhost:8080/users/1" }
}
}, {
"username" : "bobby", ...