Update the response without null value - java

I have the following entities provided below,
#Entity(name = "Employee")
#Table(name = "employee")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "creation_on" ,updatable = false)
private Date creationOn;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "phoneNumber")
private String phoneNumber;
#Column(name = "age")
private Integer age;
#Column(name = "state")
#Enumerated(EnumType.STRING)
private EmployeeStates employeeState;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity(name = "address")
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "street")
private String street;
#Column(name = "state")
private String state;
#Column(name = "country")
private String country;
#Column(name = "zip")
private int zip;
#OneToOne(mappedBy = "address")
private Employee employee;
}
The POST call is provided below,
#PostMapping(value = "/create")
public ResponseEntity<Object> createEmployee(#Valid #RequestBody EmployeeDto employeeDto) {
try{
Employee employee = employeeService.createEmployee(employeeDto);
if(employee != null){
return new ResponseEntity<>(employee, new HttpHeaders(), HttpStatus.CREATED);
}
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.UNPROCESSABLE_ENTITY,
MessageConstant.EMPLOYEE_NOT_CREATE_MSG), new HttpHeaders(), HttpStatus.UNPROCESSABLE_ENTITY);
}
catch (Exception ex) {
log.error(MessageConstant.INTERNAL_SERVER_ERROR_MSG + ex.getMessage());
return new ResponseEntity<>(ApiResponseMessage.getInternalServerError(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
When the response is coming, its below:
{
"id": 2,
"creationOn": "2021-02-27T15:05:23.585+00:00",
"firstName": "string",
"lastName": "string",
"email": "string",
"phoneNumber": "string",
"age": 0,
"employeeState": "ADDED",
"address": {
"id": 3,
"street": "string",
"state": "string",
"country": "string",
"zip": 0,
"employee": null
}
}
How do I not see the "employee": null in the response?

You need to use #JsonInclude(Include.NON_EMPTY) or #JsonInclude(Include.NON_NULL) on your Entity classes.
Like
#Entity(name = "Employee")
#Table(name = "employee")
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonInclude(Include.NON_EMPTY)
public class Employee {
}
#JsonInclude(Include.NON_EMPTY) -
Value that indicates that only properties with null value,or what is considered empty, are not to be included.Definition of emptiness is data type specific; see belowfor details on actual handling.
#JsonInclude(Include.NON_NULL) - Value that indicates that only properties with non-nullvalues are to be included.

I need to ignore JSON value from the child entity as below:
#OneToOne(mappedBy = "address")
#JsonIgnore
private Employee employee;
This makes all fine.

Related

Why #Column(unique = true) does not work?

I have following code:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
#NotNull
#Column(name = "LOGIN", unique = true)
private String login;
#Column(name = "PASSWORD")
private String password;
#Column(name = "ROLE")
private UserRole role;
#Column(name = "E_MAIL", unique = true)
private String email;
#Convert(converter = UserStrategyConverter.class)
#Column(name = "STRATEGY")
private UserStrategy userStrategy;
#Column(name = "SUBSCRIPTION")
private Boolean subscription;
#Column(name = "MONEY")
private BigDecimal money;
My problem: When I put this object from postman in json:
{
"firstName": "Daniel",
"lastName": "xxx",
"password": "daniel",
"role": "ROLE_USER",
"email": "test#test.pl",
"subscription": false,
"money": "1200"
}
It create object in entity. Problem is because I can multiply this object again and again instead of unique = true in columns (email and login). Can anyone explain me why?
Hibernate will take into account the constraint unique = true only at the time of schema generation.
During schema generation the following constraint will be added:
alter table User
add constraint UK_u31e1frmjp9mxf8k8tmp990i unique (email)
If you do not use the schema generation there is no sense of using unique = true.

ModelMapper issues converting DTO Objects to Entity

I have a simple spring-boot app where Product needs to be stored and conversion between DTO and Entity needs to happen. I am using the ModelMapper dependency. User can attach a ProductCategory to the Product or leave it empty. Similarly Product can have multiple ReplaceNumber or empty. If I dont attach category it gives error. If I attach category it saves the product with the attached category. If I leave the replaceNumbers array empty it saves. If I fill it it gives errors. Errors are described below.
ProductCategory
#Entity
#Table(name = "product_categories")
public class ProductCategory
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Column(name = "name", nullable = false)
#Size(max = 20)
private String name;
public ProductCategory()
{
}
public ProductCategory(String name)
{
this.name = name;
}
}
ReplaceNumber
#Entity
#Table(name = "replace_numbers")
public class ReplaceNumber
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 20)
private String partNumber;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "product_id", nullable = false)
private Product product;
public ReplaceNumber()
{
}
public ReplaceNumber(String partNumber)
{
this.partNumber = partNumber;
}
}
Product
#Entity
#Table(name = "products", indexes = {#Index(name= "part_number_index", columnList = "part_number", unique = true)})
public class Product
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Column(name = "part_number", nullable = false)
#Size(max = 20)
private String partNumber;
#NotBlank
#Size(max = 255)
private String description;
#OneToMany(
mappedBy = "product",
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
#Fetch(FetchMode.SELECT)
private List<ReplaceNumber> replaceNumbers = new ArrayList<>();
#ManyToOne
#JoinColumn(name = "product_category_id", referencedColumnName = "id")
private ProductCategory category;
}
Following are the DTO Classes that need to be converted.
ReplaceNumberRequest
public class ReplaceNumberRequest
{
#NotBlank
#Size(max = 20)
private String partNumber;
public String getPartNumber()
{
return partNumber;
}
public void setPartNumber(String partNumber)
{
this.partNumber = partNumber;
}
}
ProductCategoryResponse
public class ProductCategoryResponse
{
private Long id;
private String name;
public ProductCategoryResponse()
{
}
public ProductCategoryResponse(String name)
{
this.name = name;
}
}
ProductRequest
public class ProductRequest
{
#NotBlank
#Size(max = 20)
private String partNumber;
#NotBlank
#Size(max = 255)
private String description;
private List<ReplaceNumberRequest> replaceNumbers = new ArrayList<>();
private ProductCategoryResponse category;
}
ProductService
#Service
public class ProductService
{
#Autowired
ProductRepository productRepository;
public Product create(ProductRequest productRequest)
{
Product product = new Product();
org.modelmapper.ModelMapper modelMapper = new org.modelmapper.ModelMapper();
modelMapper.map(productRequest, product);
return productRepository.save(product);
}
}
If I post the following JSON from Postman
{
"partNumber": "443455783",
"description": "443434",
"replaceNumbers": [],
"category": ""
}
It goes for saving the empty category and produces the following error.
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : org.walana.GP.model.Product.category -> org.walana.GP.model.ProductCategory; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : org.walana.GP.model.Product.category -> org.walana.GP.model.ProductCategory
If I post the following JSON from Postman
{
"partNumber": "443455783",
"description": "443434",
"replaceNumbers": [
{
"partNumber": "123455"
},
{
"partNumber": "343435"
}
],
"category": {
"id": 1,
"name": "Mounting"
}
}
It gives following error.
could not execute statement; SQL [n/a]; constraint [part_number_index]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

how to send only id instead of object in requestbody?

I have two entities. Customer which is mapped in one to many relation with the CustomerDepartment. CustomerDepartment table has a column to store customer Id.
I want to map them in such a way that Customer Object store a list of Customer Department, and the Customer Department stores the id of the customer it belongs to.
The code that is working compels me to send the all the customer details while creating or updating a customer Department.
Is there a way I can only send the id of the customer and it maps itself?
I have tried changing from -
#JsonBackReference
#ManyToOne
#JoinColumn(name = "customer_no", nullable = false)
private Customer customer;
to this -
#JsonBackReference
#ManyToOne(targetEntity = Customer.class)
#JoinColumn(name = "customer_no", nullable = false)
private Integer customer;
which gives me the requestbody I want but it does not work giving the following error -
2019-08-03 04:59:08 ERROR CustomerController:72 - org.springframework.orm.jpa.JpaSystemException: Error accessing field [private java.lang.Integer com.enquero.pulse.entity.Customer.customerNo] by reflection for persistent property [com.enquero.pulse.entity.Customer#customerNo] : 1; nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Integer com.enquero.pulse.entity.Customer.customerNo] by reflection for persistent property [com.enquero.pulse.entity.Customer#customerNo] : 1
Working Code:
Customer:-
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#DynamicUpdate
#Entity
#Table(name = "customer")
public class Customer extends Auditable<Integer>{
#Id
#Column(name = "customer_no")
private Integer customerNo;
#NotBlank
#Column(name = "customer_name")
private String customerName;
#Column(name = "industry")
private String industry;
#Column(name = "country")
private String country;
#Column(name = "state")
private String state;
#Column(name = "city")
private String city;
#Column(name = "postal_code")
private String postalCode;
#Column(name = "address_line1")
private String addressLine1;
#Column(name = "address_line2")
private String addressLine2;
#Column(name = "address_line3")
private String addressLine3;
#Column(name = "payment_term")
private String paymentTerm;
#Column(name = "customer_segment")
private String customerSegment;
#JsonFormat(pattern="dd-MMM-yyyy")
#Column(name = "engagement_start_on")
private Date engagementStartOn;
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private List<CustomerDepartment> customerDepartments;
}
CustomerDepartment:-
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#DynamicUpdate
#Entity
#Table(name = "customer_department")
public class CustomerDepartment extends Auditable<Integer>{
#Id
#Column(name = "dept_id", updatable = false, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer deptId;
#Column(name = "dept_name")
private String deptName;
#Column(name = "primary_contact")
private String primaryContact;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "customer_no", nullable = false)
private Customer customer;
}
Current RequestBody:-
{
"createdBy": 0,
"creationDate": "2019-08-02T23:05:33.993Z",
"customer": {
"addressLine1": "string",
"addressLine2": "string",
"addressLine3": "string",
"city": "string",
"country": "string",
"createdBy": 0,
"creationDate": "2019-08-02T23:05:33.993Z",
"customerDepartments": [
null
],
"customerName": "string",
"customerNo": 0,
"customerSegment": "string",
"engagementStartOn": "string",
"industry": "string",
"lastUpdateDate": "2019-08-02T23:05:33.993Z",
"lastUpdatedBy": 0,
"paymentTerm": "string",
"postalCode": "string",
"state": "string"
},
"deptId": 0,
"deptName": "string",
"lastUpdateDate": "2019-08-02T23:05:33.994Z",
"lastUpdatedBy": 0,
"primaryContact": "string"
}
expected requestbody:-
{
"createdBy": 0,
"creationDate": "2019-08-02T23:05:33.993Z",
"customer": 1, //id instead of json
"deptId": 0,
"deptName": "string",
"lastUpdateDate": "2019-08-02T23:05:33.994Z",
"lastUpdatedBy": 0,
"primaryContact": "string"
}
Have you considered a unidirectional #OneToMany: https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#associations?
For example on CustomerDeparment change
#JsonBackReference
#ManyToOne
#JoinColumn(name = "customer_no", nullable = false)
private Customer customer;
}
to
#JsonBackReference
#ManyToOne
#Column(name = "customer_no")
private int customer;
...and on Customer change
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private List<CustomerDepartment> customerDepartments;
}
to
#JsonManagedReference
#OneToMany(cascade = CascadeType.ALL)
private List<CustomerDepartment> customerDepartments;
}
As a bit of an aside, I honestly find Hibernate relationships to sometimes be more a hindrance than a help. As an alternative, you may wish to consider dropping the explicit relationship properties, using "regular" columns (#Column(name="customer_no") private int customer') and just writing queries in your repo classes (ex. findByCustomerNo(int customNumber)) to meet your requirements.

Can't save entity in hibernate

I have created simple CRUD service. With 4 entities: Customer, Provider, Product, Deal.
Customer and Provider entities has composed id AppId with the following structure:
#Getter
#Setter
#Embeddable
#NoArgsConstructor
public class AppId implements Serializable {
private String app;
private String id;
//...
}
Here is business logic I want:
Providers entity cascades and creates Product entities.
When the customer makes deal with provider I need to create entity Deal, which doesn't cascade any other entities.
It just has fields which refer to provider, customer and product of the deal.
I created some providers and customers.
Then I tried to create deal, but I got fields customer and provider null.
Here are my entities definitions:
Provider:
#Entity
#Getter
#Setter
#ToString
#NoArgsConstructor
#Table(name = "provider")
public class Provider implements Serializable {
#EmbeddedId
#Column(name = "appid")
private AppId appId;
#Column(name = "name")
private String name;
#Column(name = "firstname")
private String firstName;
#Column(name = "lastname")
private String lastName;
#Column(name = "latitude")
private float latitude;
#Column(name = "longitude")
private float longitude;
#Column(name = "work_date")
private Date workDate;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "provider_product"
, joinColumns = {
#JoinColumn(name = "provider_app"),
#JoinColumn(name = "provider_id")
}
, inverseJoinColumns = #JoinColumn(name="product_id"))
private Set<Product> products;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "app", referencedColumnName = "app", updatable = false, insertable = false),
#JoinColumn(name = "id", referencedColumnName = "id", updatable = false, insertable = false)
})
private List<Deal> dealList = new ArrayList<>();
}
Customer:
#Entity
#Getter
#Setter
#ToString
#NoArgsConstructor
#Table(name = "customer")
public class Customer implements Serializable {
#EmbeddedId
#Column(name = "appid")
private AppId appId;
#Column(name = "firstname")
private String firstName;
#Column(name = "lastname")
private String lastName;
public Customer(AppId appId, String firstName, String lastName) {
this.appId = appId;
this.firstName = firstName;
this.lastName = lastName;
}
}
Product:
#Entity
#Getter
#Setter
#ToString
#NoArgsConstructor
#Table(name = "product")
public class Product implements Serializable {
#Id
#GeneratedValue
private long id;
#Column(name = "name")
private String name;
#Column(name = "cost")
private long cost;
}
Deal:
#Entity
#Getter
#Setter
#ToString
#NoArgsConstructor
#Table(name = "deal")
public class Deal implements Serializable {
#Id
#GeneratedValue
private long id;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "provider_app", referencedColumnName = "app", insertable = false, updatable = false),
#JoinColumn(name = "provider_id", referencedColumnName = "id", insertable = false, updatable = false)
})
private Provider provider;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "customer_app", insertable = false, updatable = false),
#JoinColumn(name = "customer_id", insertable = false, updatable = false)
})
private Customer customer;
#ManyToMany
#JoinTable(name = "deal_product"
, joinColumns = #JoinColumn(name="deal_id", insertable = false, updatable = false)
, inverseJoinColumns = #JoinColumn(name="product_id", insertable = false, updatable = false))
private Set<Product> product;
// deal is complete when provider entered deal id
#Column(name = "closed")
private boolean closed = false;
}
By removing insertable = false for customer and provider fields in the Deal entity, everything works fine.
{
"id": 5,
"provider": {
"appId": {
"app": "vk",
"id": "123"
},
"name": null,
"firstName": null,
"lastName": null,
"latitude": 0,
"longitude": 0,
"workDate": null,
"products": null,
"dealList": []
},
"customer": {
"appId": {
"app": "vk",
"id": "123"
},
"firstName": null,
"lastName": null
},
"product": [
{
"id": 2,
"name": "Temp",
"cost": 100
}
],
"closed": false
}
I could get the following response.
insertable = false on a field means when you are saving the entity you won't be saving the value for that field and will set the field explicitly somewhere.
insertable = true doesn't mean you will create a new Customer or Provider, that is handled by CascadeType

Spring MVC/Jackson - Nested Entity Deserialization Weirdness

I'm having a weird problem with Jackson serialization - I have a Role entity have a nested Permission entity which, in turn, contains a nested Metadata entity. When these entities are retrieved from a Spring MVC #RestController as a list, Jackson serializes the Permission collection into a JSON array. The problem is that sometimes the element placed in this array is just the id of the Permission rather than a serialized representation of the object.
Role.class:
#Entity
#Table(name = "t_db_roles")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Role.class)
public class Role implements GrantedAuthority {
private final static Logger log = LoggerFactory.getLogger(Permission.class);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "auto_id")
private int id;
#Column(name = "role", length = 50)
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "t_db_role_permissions",
joinColumns = {#JoinColumn(name = "roleid", referencedColumnName = "auto_id")},
inverseJoinColumns = {#JoinColumn(name = "permid", referencedColumnName = "auto_id")}
)
private Set<Permission> permissions;
// getters and setters omitted
}
Permission.class:
#Entity
#Table(name = "t_db_permissions")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Permission.class)
public class Permission implements GrantedAuthority {
private final static Logger log = LoggerFactory.getLogger(Permission.class);
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "auto_id")
private int id;
#Column(name = "name")
private String name;
#OneToOne(mappedBy = "permission")
private Metadata metadata;
}
Metadata.class
#Entity
#Table(name = "t_report_data")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Metadata.class)
public class Metadata {
#Id
#Column(name = "id", insertable = false, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "file_name")
private String fileName;
#Column(name = "human_name")
private String humanName;
#Column(name = "links_to")
#JsonIgnore
private Integer linksTo;
#Column(name = "is_subreport")
#JsonIgnore
private Boolean isSubreport;
#OneToOne(cascade = javax.persistence.CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "permid")
private Permission permission;
}
The controller:
#RestController
public class RoleRestController {
private final static Logger log = LoggerFactory.getLogger(PermissionRestController.class);
private RoleService roleService;
private MetadataService metadataService;
#Autowired
public void setRoleService(RoleService service) {
this.roleService = service;
}
#Autowired
public void setMetadataService(ReportMetadataService service) { this.metadataService = service; }
#RequestMapping(value = "/admin/roles/", method = RequestMethod.GET)
public List<Role> getRoles() {
return roleService.getRoles();
}
}
I'm fairly sure that the problem is in serialization - echoing the List<Role> to the console works as expected, but here is the JSON returned (note the first element of the permissions array is an integer rather than a JSON object):
{
"id": 10,
"name": "ROLE_TESTER",
"permissions": [
14,
{
"id": 7,
"name": "ViewDailySettlementSummaryGL",
"metadata": {
"id": 41,
"fileName": "acct_summary_gl.rptdesign",
"humanName": "Daily Settlement Summary GL",
"permission": 7
},
"authority": "ViewDailySettlementSummaryGL"
},
{
"id": 6,
"name": "ViewDailySettlementSummary",
"metadata": {
"id": 24,
"fileName": "acct_summary_os.rptdesign",
"humanName": "Daily Settlement Summary",
"permission": 6
},
"authority": "ViewDailySettlementSummary"
}
],
"authority": "ROLE_TESTER"
}
I can work around this by handling Role serialization manually, but since the SpringMVC/Jackson serialization works for other classes in the project it seems like there must be a problem in these classes that i'm overlooking. Any ideas?

Categories