Genson not consuming Hibernate POJO's field annotated with #Transient - java

For implementing a REST API in Java, I'm using:
- Jersey JAX-RS Framework
- Genson parser
- Tomcat8 server
- Hibernate
I have this method in a service class:
#POST
#Consumes("application/json")
public Response createUser(Users user) {
UsersDAO u = new UsersDAO();
if(user.getLogin() == null || user.getPasswd() == null)
return Response.status(Response.Status.BAD_REQUEST).entity("Missing information").build();
try{
u.addUser(user);
}catch(HibernateException e){
return Response.status(Response.Status.BAD_REQUEST).entity("User already exists").build();
}
return Response.status(Response.Status.CREATED).build();
}
The Users class:
public class Users implements Serializable {
#Basic(optional = false)
#Column(name = "passwd")
private String passwd;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "iduser")
private Integer idUser;
#Basic(optional = false)
#Column(name = "login")
private String login;
#JoinColumn(name = "idusersgroup", referencedColumnName = "idusersgroup")
#ManyToOne(optional = true)
private UsersGroups idUsersGroup;
#Transient
private int idGroup;
.
.
.
}
As you can see, I created the idGroup field just to store the id of the UsersGroups object related, so if I want to add the group in the moment of the user creation, I'll just have to include its id in the JSON instead of the UsersGroups object (the relationship is optional, a user can belong to a group or not). The problem is Genson is not adding this field to the Users object when consumes the JSON:
{
"login": "admin1",
"passwd": "12345",
"idgroup": 3
}
If I POST this JSON and then access to the user.getIdGroup(), it returns 0, so I assume that idGroup field isn't being consumed. Could the #Transient annotation has something to do with this issue? Any idea on how to solve it? If the problem is Genson and there's any solution using another JSON parser (like Jackson), I could consider a switch

The issue is that in the json you have idgroup with lowercase while the property in the class is with upper case G. Also make sure you have getters and setters for it or configure Genson to use private properties (you can find a few examples in the docs).
BTW genson is not aware of hibernate annotations.

Related

Include/exclude Attributes in json response from application.yml

I am using JHipster(spring boot) to generate my project. I would like to hide/show fields in JSON from application.yml. for exemple:
I have the following class
#Entity
#Table(name = "port")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Port implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#Column(name = "id")
private Long id;
#Column(name = "city")
private String city;
#Column(name = "description")
private String description;
//getters & setters
}
My GET method return a response like:
{
"id": 1,
"city": "boston",
"description": "test test"
}
I would like to be able to include/exclude some fields from application.yml (since i don't have application.properties) otherwise to have something like:
//application.yml
include: ['city']
exclude: ['description']
in this exemple my json should look like:
{
"id": 1,
"city": "boston",
}
for exemple if I have 40 fields and I need to hide 10 and show 30 I just want to put the 10 I want to hide in exclude in application.yml without go everytime to change the code. I guess #jsonignore hide fields but I don't know how to do it from application.yml
Sorry for not explaining well. I hope it's clear.
Thank you in advance for any suggestion or solution to do something similar
Spring boot by default uses Jackson JSON library to serialize your classes to Json. In that library there is an annotation #JsonIgnore which is used precisely for the purpose to tell Json engine to egnore a particular property from serialization/de-serialization. So, lets say in your entity Port you would want to exclude property city from showing. All you have to do is to annotate that property (or its getter method) with #JsonIgnore annotation:
#Column(name = "city")
#JsonIgnore
private String city;
You can try to create a hashmap in your controller to manage your HTTP response.
Map<String, Object> map = new HashMap<>();
map.put("id", Port.getId());
map.put("city", Port.getCity());
return map;
Basically you don't expose your Port entity in your REST controller, you expose a DTO (Data Transfer Object) that you value from your entity in service layer using a simple class (e.g PortMapper). PortDTO could also be a Map as suggested in other answer.
Your service layer can then use a configuration object (e.g. PortMapperConfiguration) that is valued from application.yml and used by PortMapper to conditionally call PortDTO setters from Port getters.
#ConfigurationProperties(prefix = "mapper", ignoreUnknownFields = false)
public class PortMapperConfiguration {
private List<String> include;
private List<String> exclude;
// getters/setters
}
#Service
public class PortMapper {
private PortMapperConfiguration configuration;
public PortMapper(PortMapperConfiguration configuration) {
this.configuration = configuration;
}
public PortDTO toDto(Port port) {
PortDTO dto = new PortDTO();
// Fill DTO based on configuration
return dto;
}
}

Spring Data JPA Returning Proxy Object For One Record and Real Object For One Record

I am using spring-data-jpa to read data from db.
JPA Entity
#Entity
#Table(name = "ICM_STATUSES")
public class IcmMdStatuses implements Serializable {
#Id
#Column(name = "STATUS_INTERNAL_IDENTIFIER")
private long statusInternalIdentifier;
#Column(name = "STATUS_IDENTIFIER")
private String statusIdentifier;
#Column(name = "STATUS_NAME")
private String statusName;
#Column(name = "STATE_NAME")
private String stateName;
#Column(name = "IS_ACTIVE")
private String isActive= "Y";
#Column(name = "CREATED_BY")
private String createdBy;
#CreationTimestamp
#Column(name = "CREATE_DATE")
private Date createdDate;
#Column(name = "UPDATED_BY")
private String updatedBy;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "UPDATE_DATE")
private Date updateDate;
}
JPA Repository
public interface StatusRepository extends JpaRepository<IcmMdStatuses, Long> {
Optional<IcmMdStatuses> findByStatusIdentifier(String statusIdentifier);
}
DB Table Data
Problem:
If I try to fetch Open status record. I am getting Hibernate Proxy Object instead of real object.
If I try to fetch Reopen status record. i am getting real object.
Except for Open status, for the rest all statuses i am getting real objects.
I could able to get the real object using Hibernate.unproxy(-)
Problem
MyEntity entity = new MyEntity();
//If i use any other status except Open the below line saves pk of that particular status.
entity.setFromStatus(statusRepository.findByStatusIdentifier("Open"));//saving null in db
entity.setToStatus(statusRepository.findByStatusIdentifier("Reopen"));//saving value 93(PK of Reopen status)
myRepo.save(entity);
Please help to understand what's the actual issue, as i am experiencing this issue only for Open Status, rest all works as expected.
The problem is most probably due to open-session-in-view and the fact that you loaded some other object that refers to this Open status object. Since that other object did not load the Open status object, it created a proxy for it which is then part of the persistence context. The next time you load that object, you retrieve that proxy instead of the real object because Hibernate must maintain the object identity guarantee for a persistence context.

How can I exclude properties when serializing an object to JSON with jackson?

I have a class implementing Serializable, which is mapped to a database table. It looks like this:
#Entity
#Table(name = "users")
public class Users implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
#Column(name = "name", nullable = false)
public String name;
#Column(name = "email", nullable = false)
public String email;
#Column(name = "status", nullable = false)
public String status;
}
For the most part, I want all these properties to be included in the JSON. However, there is a specific case where I want to exclude status, but I can't figure out a good way of doing this with Jackson.
My controller looks something like this:
public class UserController {
private final ObjectMapper mapper;
#Inject
public UserController(ObjectMapper mapper) {
this.mapper = mapper;
}
public CompletionStage<JsonNode> getUserList() {
// Get list of Users and return the JSON; with all User properties included
}
public CompletionStage<JsonNode> getUser(Long userId) {
// Get a single user from JPA, in a promise
return userDatabase.get(userId).thenApply(user -> { // user is type User
// Here, I don't want to include "status" in the JSON.
return mapper.valueToTree(user);
});
}
}
So when I do mapper.valueToTree(user), of course, it includes all properties of User, but I want to exclude status in this specific route/function while keeping it included in all other places its serialized.
I know I can use #JsonIgnore to ignore it always, but can I do this just sometimes?
Some solutions I thought of are:
filter through the properties and get rid of status
Copy user over to an ObjectNode and manually remove status
Neither of these seems ideal though, I feel like there has to be a cleaner approach with Jackson.

JAX-RS RESTful API working with JSON representations and linking resources

Sometimes it's confusing how I should link resources within a RESTful API, consider for example the entities:
Profile (Users can create business profiles with address, details, etc..)
Plan (Already persisted in app's DB, created by administrators)
The request to create a Profile looks like:
POST /profiles
{
"name": "Business name",
"address": "The address",
"phone": "0000000000"
}
Now it is required that a Profile belongs to a Pricing Plan. So is it a good idea to do POST request like this with JSON?
POST /profiles
{
"name": "Business name",
"address": "The address",
"phone": "0000000000"
"plan": {
"id": 1
}
}
and then load the plan by the provided id and associate it with the profile being created:
#POST
#Path("/profiles")
public Response createProfile(Profile profile) {
// load plan resource from DB
Plan plan = em.find(Plan.class, profile.getPlan().getId())
// associate
profile.setPlan(plan);
// persist profile
em.perist(profile);
}
The Profile entity:
#Entity
public class Profile implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "plan_id", nullable = false)
private Plan plan;
private String name
...
// getters and setters
}
The Plan entity:
#Entity
public class Plan implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(nullable = false)
private String name;
#NotNull
#Column(nullable = false, columnDefinition = "text")
private String description;
#NotNull
#Column(nullable = false, precision = 8, scale = 2)
private BigDecimal price;
#NotNull
#Column(nullable = false)
private Integer days;
#OneToMany(fetch = FetchType.LAZY, mappedBy="plan", cascade = CascadeType.ALL)
private List<Profile> profiles;
...
}
In other words i am asking what I should pass to the request body in order to link a reference entity.
I would like to believe that something like this is more reasonable:
POST /plans/1/profiles
but according to the REST and JSON semantics what would be the best option?
I can also think of other ways such as providing the Plan id as a query param:
POST /profiles?planId=1
I would say you need to do the following:
Create profile with
POST: /profile
Assign plan with
PUT: /profile/<profile_id>
{
"name": <optional>,
"plan_id": <optional>,
...
}
First thing is you separate create and update (POST/PUT). Another is you state profile ID for update in URL.
You can set parameters you need to update in PUT request body and update only parameters which are set. Think it's fine with REST concept.

Jersey ClientResponse Get List of Composite Entities

I am trying to get a Result of a List, basically a list of entities using Jersey RESTful API (Server and Client)
UserRESTClient client = new UserRESTClient();
ClientResponse response = client.getUsersByType(ClientResponse.class, String.valueOf(userType));
List<User> participants = response.getEntity(new GenericType<List<User>>() {
});
However, the above code does not work if Entity User has a Composite Object, if for instance,
public class User {
private UserId userId;
}
public class UserId {
private int id;
private int categoryId;
}
In this case, the JSON is deserialized by Jersey and returned null for the field type UserId inside Class User. I inspected the JSON returned and everything seems good at the RESTful Server end, but the nested JSON response is not clearly processed at the Client.
Any help would be greatly appreciated. I am not sure if it because of the Jackson preprocessor.
Following is the actual Code Snippet. It involves two classes Participant and ParticipantPK (primary for each Participant).
#Entity
#Table(name = "conference_participant")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Participant.findAll", query = "SELECT p FROM Participant p"),
public class Participant implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected ParticipantPK participantPK;
}
#Embeddable
public class ParticipantPK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "conference_id")
private int conferenceId;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 150)
#Column(name = "participant_sip_uri")
private String participantSipUri;
public ParticipantPK() {
}
public ParticipantPK(int conferenceId, String participantSipUri) {
this.conferenceId = conferenceId;
this.participantSipUri = participantSipUri;
}
And the Code for retrieving ClientResponse,
List<Participant> participants = response.getEntity(new GenericType<List<Participant>>() {
});
However, the ParticipantPK (Composite PK) is null.
You only pasted a code snippet so I don't know if this part is excluded, but in my code I didn't have setters for the fields. I had getters, but no setters.
Without the setters, my composite objects themselves were non-null, but the members of those objects were themselves null.
I tried to reproduce it, but using the same data structures worked for me. What version of Jersey are you using? Is User class annotated with #XmlRootElement or are you using the POJO mapping feature?

Categories