How to send inherited Object with JSON in Spring Controller? - java

I work with Hibernate and everything is fine except this case. If I have class and inherited classes , when I send Object like that with JSON in Spring Controller , always I get error 400 BadRequest.
All fields are circulated well and everything is stored well if I send an object that is not a subclass.
This is exmp.:
#Entity
#javax.persistence.Table(name="person")
#Inheritance(strategy=InheritanceType.JOINED)
public class Person{
#Id
#GeneratedValue
#Column(name="id_person")
protected int id_person;
.....
#Entity
#javax.persistence.Table(name="client")
#PrimaryKeyJoinColumn(name="id_person")
public class Client extends Person{
#Column(name="address")
protected String address;
....
#Entity
#javax.persistence.Table(name="individual")
#PrimaryKeyJoinColumn(name="id_person")
public class Individual extends Client {
#Column(name="personal_number")
protected String personalNumber;
.....
My method inController:
#RequestMapping(value="/individualPerson", method=RequestMethod.POST)
public ResponseEntity<List<Individual>> posttest(HttpServletRequest request, HttpServletResponse response, #RequestBody Individual indiv)
Do I need some additional annotations or settings when I send inherited objects?
Also, Hibernate is automatically created all the tables with a primary key that is annotated in these classes.

If you use JSON format, Jackson library by default doesn't need any special annotations. Spring supports it out of the box, so generally you don't need to specify anything else than:
#RequestMapping(value="/individualPerson", method=RequestMethod.POST, produces="application/json")
Other option is to rely on HTTP content negotiation, which is supported by Spring and don't specify any format in produces attribute

You would make two changes if you have Jackson library in your path:
Mark #ResponseBody annotation on the method or on the return type.
Add produces property set to org.springframework.http.MediaType.APPLICATION_JSON on #RequestMapping

Related

Working with custom annotations in the Spring Boot app

I got the response body from the HttpServletResponse with ContentCachingResponseWrapper used.
Now, I introduced custom annotations for the entities fields in my app and I need response body returned without the fields that I have annotated.
I do not want to affect my application behavior.
For example:
#Entity
public class User implements Serializable{
....
#Column
private String firstname;
#MyCustomAnnotation
#Column
private String lastname;
...
The real response body for the client should look like:
{"firstname":"John", "lastname":"Smith"}
And response body that I need for my later work is:
{"firstname":"John", "lastname":"MyCustomAnnotation"}
or
{"firstname":"John"}
or similar.
I was wondering if it is possible to do such a thing? Can I get one version of the response(filtered) and send client real version of the response?
I would like to avoid changing of the application code (methods in the controllers, model etc.), if it's possible. I would like to do all my work in the class where I am dealing with the HttpServletResponse.
What you probably need are json views, or if you simply want to ignore a field in your response - then #JsonIgnore

Using Data annotation on Java DTO class

I have confusion in using Data annotation to DTO class. Here is my sample class.
#Data
public class MyClass {
private Long id;
private String name;
}
I've read online that using Data annotation specifically on DTO class will allow the values of the fields to be changed by use of the generated setters.
Should I remove the lombok Data annotation? And implement the getters and setters manually.
Thanks :)
I would avoid #Data for DTOs since it has too many pitfalls. First of all as you mentioned it is mutable which you don't really want for a DTO. And despite it being mutable, it implements equals() and hashCode() which is only asking for trouble.
You can use #Value for an immutable DTO. For an incoming DTO you may need to add lombok.anyConstructor.addConstructorProperties to your lombok.config, which will allow libraries like jackson to deserialize to your POJO without a default constructor.
The annotation #Data comes from the Project Lombok which is designed to use reflection via annotations mostly. This annotation assures generation of all the setters, getters, a constructor with all the required arguments and overridden Object::toString, Object::equals and Object::hashCode methods.
Briefly said, this annotation "completes" a simple POJO object and generates all the boilerplate without a need to use IDE.
They named the annotation #Data because they support the idea of the understanding objects as data containers only.
As far as I understand, the generation happens only for the missing getters/setters (let's speak about them for brevity). The generated getters/setters are in their pure form as you know:
public int getId() { return this.id; }
public void setId(int id) { this.id = id; }
You can use more verbose setter/getter performing validation or anything similar which override the generated ones. You can both use #Data annotation and write your ones manually.
DTO is used to transmit data information, some information is actually we do not want users to be able to change or access, such as the user password, we do not want to pass to the front end when the user can see the encrypted password, or we do not want users to modify the password while changing their information, and what works in this serialization process is setter and getter, and data annotations that automatically generate getters and setters for all fields.
For example
#Data
class User{
private String userName;
private String pwd;
}
This class, will have all setter and getter. When you trans to web, you will see
{userName: "123", pwd: "xxx"}
This is terrible.
But if you use DTO
class User{
private String userName;
private String pwd;
public String getUserName(){
return userName;
}
}
They only see
{userName: "123"}
By default the #Data lombok annotation will generate setters and getters for all fields in the class.
If you want an immutable data transfer object, annotate it as #Value instead.
If you want a mixure of some immmutable values and some mutable values in your MyClass type, for instance you might want the id field to be immutable and the rest mutable, you would use the #Setter annotation on the field you want to be immutable, specifying an AccessLevel of NONE. For instance:
#Data
public class MyClass {
#Setter(AccessLevel.NONE)
private Long id;
private String name;
}
This will generate a getter but no setter for the id, and a getter and setter for the name.

hibernate DTO with a deviant database

I am building a spring boot application which uses REST services to deliver content to the front-end
But my DTO does not correspond to my database.
Here is a diagram of the database
And my DTO should look something like this
public class GlobeEntity extends BaseEntity {
// for all definition years
private List<Instance> instances;
class Instance {
// CountryInstance.definitionYear
private String definitionYear;
// for all countries
private List<Country> countries;
class Country {
// Country.countryId
String id;
// Country.externalIdentifier
String externalIdentifier;
// CountryInstanceCompatibility.total
String com;
// CountryInstanceUtility.total
String uti;
}
}
}
Is this possible to do with hibernate annotations?
For the DTO, what is turned into JSON when you return that class from a controller depends on your JSON serializer you have configured in Spring. Then you make use of the JSON annotations to have more control over what is returned.
Depending on how you do things, you may just choose to convert between an entity and a DTO, and have a class for each. Although this is a bit annoying because it does add allot of overhead of having more classes and other classes depending on the two, it can be come a real real mess if you use the same class for both DTO and Entity representations when they are very different. For instance if you use a method in your controller, it's not clear wether this is an entity or a DTO. There are many cases when you have a controller you return a response which is not a resource in a database.

Restrict JSON attributes for #RequestBody

This may be a simple task, but I couldn't find a way to do it. Basically, I need to disallow some parameters at the time of using #RequestBody annotation in my controller.
Here is my model:
#Data
public class MyModel {
private int id;
private String name;
}
What I want to do is at the time of response, I want both of the properties to be serialized to JSON, but at the time of create or update, I prefer not to receive id as part of #RequestBody deserialization.
Right now, if I pass id in the JSON body, Spring initializes a MyModel object with its id set to the passed value.
Reason? The ID cannot be generated until the model is created, so the app shouldn't allow the ID to be set. On update, the ID needs to be passed in the URL itself e.g. (PUT /mymodels/43). This helps following the REST principles appropriately.
So, is there any way to achieve this functionality?
Update 1:
Right now, I am stuck with using a request wrapper. I created a new class MyModelRequestWrapper with only name as its property, and have used it with the #RequestBody annotation.
How you do this depends on what version of Jackson you are using. It's basically possible by a combination of the annotations #JsonIgnore and #JsonProperty on relevant fields/getters/setters.
Have a look at the answers here: Only using #JsonIgnore during serialization, but not deserialization

Spring REST multiple #RequestBody parameters, possible?

I've implemented a Spring RESTful web service. Using Jackson JSON for Object Mapping. I have a method that accepts two parameters.
public Person createPerson(
#RequestBody UserContext userContext,
#RequestBody Person person)
How would the client construct a request where in multiple JSON objects are to be passed in the body?
Is this possible?
-- Sri
I'm pretty sure that won't work. There may be a workaround, but the much easier way would be to introduce a wrapper Object and change your signature:
public class PersonContext{
private UserContext userContext;
private Person person;
// getters and setters
}
public Person createPerson(#RequestBody PersonContext personContext)

Categories