With Micronaut REST API CRUD operation on table with foreign keys not working
We created a micronaut application with JPA & entity manager. We have table called Employee and State, so we created entity classes for both and referenced state_id in employee table.
With all the implementation we are able to fetch the employee details with state id and name but when we create new employee it is throwing errors.
Employee model:
package io.usermgmt.models;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "employee_id")
private Integer employeeId;
@Column(name = "employee_name")
private String employeeName;
@Column(name = "address")
private String address;
@Column(name = "city")
private String city;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "state_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private State state;
@Column(name = "zip_code")
private String zipCode;
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getAddress(){return address;}
public void setAddress(String address){this.address=address;}
public String getCity(){return city;}
public void setCity(String city){this.city=city;}
public Integer getStateId(){return state.getStateId();}
public String getStateName(){return state.getStateName();}
public String getZipCode(){return zipCode;}
public void setZipCode(String zipCode){this.zipCode=zipCode;}
}
State model:
package io.usermgmt.models;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.Proxy;
import javax.persistence.Column;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "state")
@Proxy(lazy = false)
public class State {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "state_id")
private Integer stateId;
@Column(name = "state_name")
private String stateName;
@OneToMany(mappedBy = "state", fetch = FetchType.LAZY)
@JsonIgnore
private Set<Employee> employees = new HashSet<>();
public Integer getStateId() { return this.stateId; }
public void setStateId(Integer stateId){
this.stateId = stateId;
}
public String getStateName(){ return this.stateName; }
public void setStateName(String stateName){
this.stateName = stateName;
}
}
EmployeeRepositoryImpl
package io.usermgmt.repositories.impl;
import io.usermgmt.models.Client;
import io.usermgmt.repositories.IEmployeeRepository;
import io.micronaut.configuration.hibernate.jpa.scope.CurrentSession;
import io.micronaut.spring.tx.annotation.Transactional;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Optional;
@Singleton
public class EmployeeRepositoryImpl implements IEmployeeRepository {
@PersistenceContext
private EntityManager entityManager;
public EmployeeRepositoryImpl(@CurrentSession EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
@Transactional
public Employee createEmployee(@NotNull Employee employee) {
entityManager.persist(employee);
return employee;
}
@Override
@Transactional
public Employee updateEmployee(@NotNull Employee employee) {
return entityManager.merge(employee);
}
@Override
@Transactional(readOnly = true)
public List<Employee> getAllEmployees() {
String qlString = "SELECT c FROM Employee c";
TypedQuery<Employee> query = entityManager.createQuery(qlString, Employee.class);
return query.getResultList();
}
@Override
@Transactional(readOnly = true)
public Optional<Employee> getEmployeeById(@NotNull Integer id) {
return Optional.ofNullable(entityManager.find(Employee.class, id));
}
}
getAllEmployees() is working fine which is getting State details also. But while creating it is below errors.
ERROR i.m.h.s.netty.RoutingInBoundHandler - Unexpected error occurred: Error encoding object [io.usermgmt.models.Employee@1d91f388] to JSON: (was j
ava.lang.NullPointerException) (through reference chain: io.usermgmt.models.Employee["stateId"])
io.micronaut.http.codec.CodecException: Error encoding object [io.usermgmt.models.Employee@1d91f388] to JSON: (was java.lang.NullPointerException) (through reference chain: io.app
ter.clientmgmt.models.Employee["stateId"])
at io.micronaut.jackson.codec.JsonMediaTypeCodec.encode(JsonMediaTypeCodec.java:176)
at io.micronaut.jackson.codec.JsonMediaTypeCodec.encode(JsonMediaTypeCodec.java:182)
at io.micronaut.http.server.netty.RoutingInBoundHandler.encodeBodyAsByteBuf(RoutingInBoundHandler.java:1359)
at io.micronaut.http.server.netty.RoutingInBoundHandler.encodeBodyWithCodec(RoutingInBoundHandler.java:1305)
at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$subscribeToResponsePublisher$16(RoutingInBoundHandler.java:1240)
at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:63)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.lambda$onNext$0(InstrumentedSubscriber.java:80)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onNext(InstrumentedSubscriber.java:84)
at io.reactivex.internal.operators.flowable.FlowableSwitchMap$SwitchMapSubscriber.drain(FlowableSwitchMap.java:307)
at io.reactivex.internal.operators.flowable.FlowableSwitchMap$SwitchMapInnerSubscriber.onSubscribe(FlowableSwitchMap.java:366)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onSubscribe(InstrumentedSubscriber.java:75)
at io.reactivex.internal.operators.flowable.FlowableJust.subscribeActual(FlowableJust.java:34)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedCallableFlowable.subscribeActual(RxInstrumentedCallableFlowable.java:65)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.reactivex.internal.operators.flowable.FlowableSwitchMap$SwitchMapSubscriber.onNext(FlowableSwitchMap.java:129)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.lambda$onNext$0(InstrumentedSubscriber.java:80)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onNext(InstrumentedSubscriber.java:84)
at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.onNext(FlowableSubscribeOn.java:97)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.lambda$onNext$0(InstrumentedSubscriber.java:80)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onNext(InstrumentedSubscriber.java:84)
at io.micronaut.http.context.ServerRequestTracingPublisher$1.lambda$onNext$1(ServerRequestTracingPublisher.java:60)
at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:52)
at io.micronaut.http.context.ServerRequestTracingPublisher$1.onNext(ServerRequestTracingPublisher.java:60)
at io.micronaut.http.context.ServerRequestTracingPublisher$1.onNext(ServerRequestTracingPublisher.java:52)
at io.reactivex.internal.util.HalfSerializer.onNext(HalfSerializer.java:45)
at io.reactivex.internal.subscribers.StrictSubscriber.onNext(StrictSubscriber.java:97)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.lambda$onNext$0(InstrumentedSubscriber.java:80)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onNext(InstrumentedSubscriber.java:84)
at io.reactivex.internal.operators.flowable.FlowableSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FlowableSwitchIfEmpty.java:59)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.lambda$onNext$0(InstrumentedSubscriber.java:80)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onNext(InstrumentedSubscriber.java:84)
at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:68)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.lambda$onNext$0(InstrumentedSubscriber.java:80)
at io.micronaut.reactive.rxjava2.InstrumentedSubscriber.onNext(InstrumentedSubscriber.java:84)
at io.reactivex.internal.operators.flowable.FlowableCreate$NoOverflowBaseAsyncEmitter.onNext(FlowableCreate.java:403)
at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$buildResultEmitter$19(RoutingInBoundHandler.java:1430)
at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:71)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:37)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.internal.operators.flowable.FlowableSwitchIfEmpty.subscribeActual(FlowableSwitchIfEmpty.java:32)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14868)
at io.micronaut.http.context.ServerRequestTracingPublisher.lambda$subscribe$0(ServerRequestTracingPublisher.java:52)
at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:52)
at io.micronaut.http.context.ServerRequestTracingPublisher.subscribe(ServerRequestTracingPublisher.java:52)
at io.reactivex.internal.operators.flowable.FlowableFromPublisher.subscribeActual(FlowableFromPublisher.java:29)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker.run(ExecutorScheduler.java:253)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: io.usermgmt.models.Employee["stateId"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:727)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3905)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsBytes(ObjectMapper.java:3243)
at io.micronaut.jackson.codec.JsonMediaTypeCodec.encode(JsonMediaTypeCodec.java:173)
... 69 common frames omitted
Caused by: java.lang.NullPointerException: null
at io.usermgmt.models.Employee.getStateId(Employee.java:83)
at io.usermgmt.models.$Employee$Introspection$$5.readInternal(Unknown Source)
at io.micronaut.core.beans.AbstractBeanProperty.get(AbstractBeanProperty.java:116)
at io.micronaut.jackson.modules.BeanIntrospectionModule$BeanIntrospectionPropertyWriter.serializeAsField(BeanIntrospectionModule.java:539)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
... 75 common frames omitted
data we are passing through poastman is:
{
"employeeName": "ATT",
"address": "address",
"city": "HYD",
"isActive": true,
"stateId": 1,
"countryName": "India",
"countryId": 2,
"stateName": "telangana"
}
Can anyone help on this please.
This exception:
ERROR i.m.h.s.netty.RoutingInBoundHandler - Unexpected error occurred: Error encoding object [io.usermgmt.models.Employee@1d91f388] to JSON: (was java.lang.NullPointerException) (through reference chain: io.usermgmt.models.Employee["stateId"])
Is caused by getStateId()
method in Employee
class because you have there:
return state.getStateId();
... which must throw NullPointerException
because you don't test if state
property is null
or not and that property is always null
because you don't have neither default value nor set method for it.
So change the method in this way for example:
public Integer getStateId() {
return state == null ? null : state.getStateId();
}
The same applies for getStateName()
method where is the same problem.
And add also these methods into the Empoyee
class to be able to get and set state
property value:
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
Correct JSON data structure will be:
{
"employeeName": "ATT",
"address": "address",
"city": "HYD",
"state": {
"stateId": 1,
"stateName": "telangana"},
"zipCode": "zip"
}
Properties isActive
, countryName
and countryId
from your JSON example makes no sense as you don't have them in the Employee
entity.