Spring Data REST getting exception when trying to save the data - spring

I new to using Spring data rest , so even though this might be a very basic thing I would appreciate it if someone could point what is the mistake that i made or point me in the right direction .
I have an EmployeeRepository which contains the employee entities.
public interface EmployeeRepository extends CrudRepository<Employee,Long>{
#Query("select emp from Employee emp where emp.employeeId=?1")
Employee findById(String employeeId);
#Query("select emp from Employee emp where emp.officialEmailId = ?1")
Employee findByOfficialEmailId(String officialEmailId);
}
Below is a part of the employee entity delcaration .
public class Employee extends AbstractAuditingEntity{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
#Size(min = 3, max = 30)
#NotBlank
#Column(unique=true,nullable=false)
private String employeeId;
#Size(min=3,max = 25)
#NotBlank
#Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
private String lastName;
#Size(min=3,max = 25)
#Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
#NotBlank
private String firstName;
#Size(min=3,max = 25)
#Field(index=Index.YES, analyze=Analyze.NO, store=Store.YES)
private String middleName;
}
In my service class I needed to get list of employee entities based on a condition and set a common property to all those .I tried it and it did not work so I tried something much simpler , I'm trying to set the middle name of all employees to a common value and save it to the db . Here is the code for it
#Transactional
public void tempFunction(){
Iterable<Employee> empList = employeeRepository.findAll();
for(Employee e : empList){
e.setMiddleName("newMiddleName");
employeeRepository.save(e);
}
}
I'm getting the below exception 2016-02-20 17:59:08.545 ERROR 533 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction] with root cause
java.lang.NullPointerException: null
And the stack trace shows the root cause to be a NullPointerException which I know is not present anywhere near where I'm invoking this code from either in my controller or service . Could someone point if there is any mistake in my code , thanks in advance .

Related

Duplicate or Null error when trying to send POST request for JSON Object with Foreign Key using Spring JPA

Here's my parent entity:
#Entity(name = "DrivingInstructor")
#Table(name = "driving_instructor")
#Getter
#Setter
#NoArgsConstructor
public class DrivingInstructor {
#Id
#Column(name = "driving_instructor_id")
private long drivingInstructorId;
#Column(name = "driving_instructor_name")
#Size(max = 128)
private String drivingInstructorName;
#Column(name = "specialization")
#Size(max = 200)
private String specialisation;
}
And here's my supposed child entity:
#Entity(name = "DrivingStudent")
#Table(name = "driving_student")
#Getter
#Setter
#NoArgsConstructor
public class DrivingStudent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "driving_student_id")
private long drivingStudentId;
#Column(name = "driving_student_name")
#Size(max = 128)
private String drivingStudentName;
#ManyToOne(cascade = CascadeType.ALL, targetEntity = DrivingInstructor.class)
#JoinColumn(name = "driving_instructor_id", referencedColumnName = "driving_instructor_name", insertable = false, updatable = false)
private DrivingInstructor drivingInstructor;
}
Here's the relevant chunk of my service class for inserting/saving an instance of a DrivingStudent into the database:
#RequestMapping(path = "api/v0/driving-school")
#RestController
#AllArgsConstructor
public class DrivingStudentRestController {
private final DrivingStudentServiceImpl drivingStudentServiceImpl;
#PostMapping
Long insertOrUpdateDrivingStudent(#Valid #RequestBody DrivingStudent drivingStudent) {
return drivingStudentServiceImpl.insertOrUpdateDrivingStudent(drivingStudent);
}
}
DrivingStudentServiceImpl is just an abstraction layer for Repository class that extends JpaRepository<DrivingStudent, Long>, so insertOrUpdateDrivingStudent() is practically just using the save() method from CrudRepository.
An instance of DrivingInstructor is already pre-inserted with drivingInstructorId of 1, and so I tried to execute a POST request via Postman using this JSON object:
{
"drivingStudentName": "Peter Parker",
"drivingInstructor": {"drivingInstructorId": 1}
}
And I'm getting this exception:
2021-08-27 20:03:37.554 ERROR 16108 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper :
ERROR: duplicate key value violates unique constraint "driving_instructor_pkey"
Detail: Key (driving_instructor_id)=(1) already exists.
2021-08-27 20:03:37.590 ERROR 16108 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] :
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is
org.springframework.dao.DataIntegrityViolationException:
could not execute statement; SQL [n/a];
constraint [driving_instructor_pkey];
nested exception is org.hibernate.exception.ConstraintViolationException:
could not execute statement] with root cause
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "driving_instructor_pkey"
Detail: Key (driving_instructor_id)=(1) already exists.
I also tried revising my RestController's PostMapping to look like this, but still nothing changes:
#RequestMapping(path = "api/v0/driving-school")
#RestController
#AllArgsConstructor
public class DrivingStudentRestController {
private final DrivingInstructorRepository drivingInstructorRepository;
private final DrivingStudentServiceImpl drivingStudentServiceImpl;
#PostMapping
Long insertOrUpdateDrivingStudent(#Valid #RequestBody DrivingStudent drivingStudent) {
Optional<DrivingInstructor> drivingInstructor = drivingInstructorRepository.findById(drivingStudent.getDrivingInstructor().getDrivingInstructorId());
if (drivingInstructor.isPresent()) {
drivingStudent.setDrivingInstructor(drivingInstructor.get());
return drivingStudentServiceImpl.insertOrDrivingStudent(drivingStudent);
}
return null;
}
}
The error I am getting then changed to:
2021-08-27 21:36:58.622 ERROR 11388 --- [nio-8080-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper :
ERROR: null value in column "driving_instructor_number" of relation "driving_student" violates not-null constraint
Detail: Failing row contains (Peter Parker, null).
2021-08-27 21:36:58.632 ERROR 11388 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] :
Servlet.service() for servlet [dispatcherServlet] in context with path []
threw exception [Request processing failed;
nested exception is org.springframework.dao.DataIntegrityViolationException:
could not execute statement; SQL [n/a];
constraint [driving_instructor_number" of relation "driving_student];
nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
org.postgresql.util.PSQLException: ERROR: null value in column "driving_instructor_number" of relation "driving_student" violates not-null constraint
Detail: Failing row contains (Peter Parker, null).
There are stuff I've tried but most exceptions simply end up with either of those two. All I really wanted to do was insert an instance of DrivingStudent into the database using POST request, with a foreign key connecting it to a DrivingInstructor instance, and then of course, be able to retrieve those data.
I am able to do insert data manually into the database using the statement:
INSERT INTO driving_student VALUES ('Peter Parker', 1);
And I am able to retrieve that data in JSON format using GET method. So far, my only problem really is how to deal with the POST method.
Ok, I just changed/simplified the annotations in DrivingStudent's drivingInstructor JoinColumn field from this:
#ManyToOne(cascade = CascadeType.ALL, targetEntity = DrivingInstructor.class)
#JoinColumn(name = "driving_instructor_id", referencedColumnName = "driving_instructor_name", insertable = false, updatable = false)
private DrivingInstructor drivingInstructor;
to this:
#ManyToOne
#JoinColumn(name = "driving_instructor_id")
private DrivingInstructor drivingInstructor;
and it somehow worked... I have no idea why though.

Spring WebClient Post body not getting passed

I am trying to use WebClient to Post a loan object to another microservice which saves this object in a DB. So theoretically the body (JSON loan object) should just be passed on to the API of the DB service. Somehow, I can't figure out how to accomplish this.
This is the API that accepts the JSON loan object:
Mapping: localhost:8081/loans
#PostMapping
public <T extends Loan> void addLoan(#Valid #NonNull #RequestBody T loan) {
loanService.createLoan(loan);
}
It then calls the loanService which should pass on the loan object to the DB-service API
public <T extends Loan> T createLoan(T loan) {
ParameterizedTypeReference<T> typeReference = new ParameterizedTypeReference<T>(){};
T a = client.post().uri("/loans").body(BodyInserters.fromValue(loan)).retrieve().bodyToMono(typeReference).block();
return a;
}
This is the API of that DB service:
Mapping: localhost:8080/api/v1/loans
#PostMapping
#ResponseBody
public <T extends Loan> T createLoan(#RequestBody T loan) {
return loanService.createLoan(loan);
}
And here is its service:
public <T extends Loan> T createLoan(T Loan) {
return (T) loanRepository.save(Loan);
}
If I just pass a loan object directly to the DB service API, everything works fine. But if I pass it to the other API, I get the following error:
"status": 500,
"error": "Internal Server Error",
"trace": "org.springframework.web.reactive.function.client.WebClientResponseException$InternalServerError: 500 Internal Server Error from POST http://localhost:8080/api/v1/loans/\n\tat org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:201)\n\tSuppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: \nError has been observed at the following site(s):\n\t|_ checkpoint ⇢ 500 from POST http://localhost:8080/api/v1/loans/ [DefaultWebClient]\nStack trace:\n\t\tat org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:201)\n\t\tat org.springframework.web.reactive.function.client.DefaultClientResponse.lambda$createException$1(DefaultClientResponse.java:216)\n\t\tat reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106)\n\t\tat reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)\n\t\tat reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:99)\n\t\tat reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127)\n\t\tat reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)\n\t\tat reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:295)\n\t\tat reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)\n\t\tat reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1784)\n\t\tat reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159)\n\t\tat reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)\n\t\tat reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:259)\n\t\tat reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)\n\t\tat reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:383)\n\t\tat reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:396)\n\t\tat reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:452)\n\t\tat reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:664)\n\t\tat reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:94)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\t\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\t\tat io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)\n\t\tat io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)\n\t\tat io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)\n\t\tat io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\t\tat io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)\n\t\tat io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:795)\n\t\tat io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:480)\n\t\tat io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)\n\t\tat io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)\n\t\tat io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)\n\t\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\t\tat java.base/java.lang.Thread.run(Thread.java:832)\n\tSuppressed: java.lang.Exception: #block terminated with an error\n\t\tat reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99)\n\t\tat reactor.core.publisher.Mono.block(Mono.java:1679)\n\t\tat de.rwth.swc.lab.ws2021.daifu.businesslogic.services.LoanService.createLoan(LoanService.java:39)\n\t\tat de.rwth.swc.lab.ws2021.daifu.businesslogic.api.LoanController.addLoan(LoanController.java:28)\n\t\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\t\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)\n\t\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\t\tat java.base/java.lang.reflect.Method.invoke(Method.java:564)\n\t\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)\n\t\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)\n\t\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)\n\t\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893)\n\t\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:807)\n\t\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\t\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061)\n\t\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961)\n\t\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\t\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\t\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:652)\n\t\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\t\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\t\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\t\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\t\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\t\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\n\t\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\n\t\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\n\t\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\t\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\n\t\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\t\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\n\t\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\t\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\n\t\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)\n\t\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\t\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)\n\t\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)\n\t\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\t\tat java.base/java.lang.Thread.run(Thread.java:832)\n",
"message": "500 Internal Server Error from POST http://localhost:8080/api/v1/loans/",
"path": "/loans/"
This is the server-side error:
Servlet.service() for servlet [dispatcherServlet] in context with path [/api/v1] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : de.rwth.swc.lab.ws2021.daifu.dataservice.data.models.loans.PrivateLoan.customer; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : de.rwth.swc.lab.ws2021.daifu.dataservice.data.models.loans.PrivateLoan.customer] with root cause org.hibernate.PropertyValueException: not-null property references a null or transient value : de.rwth.swc.lab.ws2021.daifu.dataservice.data.models.loans.PrivateLoan.customer
And finally, this is the POST-body:
{
"amount": 10000.00,
"balance": -2000.00,
"customer": {"id": 1},
"interest": 0.06,
"status": "TIMELY",
"reason": "Some reaseon",
"type": "privateLoan"
}
The error says the "not-null property references a null or transient value" but the exact same request works for a direct POST-request to the 2nd API which doesn't make sense to me.
Here is the loan class:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Getter
#Setter
#NoArgsConstructor
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = CarLoan.class, name = "carLoan"),
#JsonSubTypes.Type(value = ConstructionLoan.class, name = "constructionLoan"),
#JsonSubTypes.Type(value = Mortgage.class, name = "mortgage"),
#JsonSubTypes.Type(value = PrivateLoan.class, name = "privateLoan"),
#JsonSubTypes.Type(value = PropertyLoan.class, name = "propertyLoan")
})
#ApiModel(
discriminator = "type",
subTypes = {CarLoan.class, ConstructionLoan.class, Mortgage.class, PrivateLoan.class, PropertyLoan.class}
)
public abstract class Loan {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id")
#ApiModelProperty(required = false, hidden = true)
protected Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customer_id", nullable = false)
#JsonBackReference(value = "customer-loans")
protected Customer customer;
#OneToMany(mappedBy = "loan", cascade = CascadeType.ALL)
#JsonManagedReference(value = "loan-loanRates")
private Set<LoanRate> loanRates;
#NonNull
protected Double amount;
#NonNull
protected Double interest;
#NonNull
protected Double balance;
#NonNull
protected LoanStatus status;
public enum LoanStatus {
TIMELY("timely"),
GRACE_PERIOD("grace period"),
DEFAULT("default"),
DEFICIT("deficit"),
IRRECOVERABLE_DEBT("irrecoverable debt"),
CLOSED("closed");
#Getter
private String stringRepresentation;
private LoanStatus(String s) {
this.stringRepresentation = s;
}
}
public <T extends Loan> boolean isOfSameInstance(T otherLoan) {
return (this.getClass().equals(otherLoan.getClass()));
}
}
Let me know if I should post anything else.
Thanks in advance.
The problem is due to the models being used in the projects. As your are reusing the model classes of the one webservice which offers the CRUD api for the backend, you are also reusing the jackson's #JsonManagedReference and #JsonBackReference. This leads to null values for the models being defined as the back reference, such as the customer in you loan class. Jackson does not serialize such tagged objects to JSON in order to not run into a stackoverflow due to infinite recursion. Thus, when you serialize a loan model in your service and send the request to the other service, jackson nulls the back reference, e.g. customer in the loan model and the 2nd webservice therefore receives an invalid loan model, since a loan model is required to have a customer not to be null.
I suggest to either remove the jackson annotations from the models in the service you develop, which would required copy pasted model classes (on the one side the classes using the required jackson annotations in the web service, and on the other side the classes not using these in the other web service). However, this solution has the typical disadvantages of duplicated code. The more elegant but more complicated solution will be to implement a custom jackson serializer and deserializer by specializing jackson's StdSerializer<Loan> and StdDeserializer<Loan>. These custom serializers and deserializers should override its serialize(T value, JsonGenerator gen, SerializerProvider provider) respectively its deserialize(JsonParser, DeserializationContext) method such that the #JsonManagedReference and #JsonBackReference, as well as, if being used, #JsonIgnore annotations in the model are being ignored.
It might be sufficient to just implement a custom serializer. However, I guess that you will also run into problems when receiving a response from the other web service when not using a custom deserializer.
This error may happen if the customer object in the loan you are trying to save is null or not yet added to the database (even though it is set in loan). You should check before saving the Loan in the DB if customer is null or not. If not, and if it is a customer not yet in the database, you should consider adding it first or specify CascadeType.PERSIST in the relation type annotation. In any case, it would be better if you post the entire model that both services are using.

Spring Data query over two documents

I have an m:n connection in my MongoDB, MessageUserConnection is the class between User and Message.
Now I will get all MessageUserConnections where MessageUserConnection#confirmationNeeded is true, read is false and Message#receiveDate is not older than the last week.
Is there any possibility to do this with Spring Data?
Thanks a lot!
public class MessageUserConnection {
#Id
private String id;
#DBRef
private User userCreatedMessage;
#DBRef
private Message message;
private Boolean confirmationNeeded;
private Boolean read;
}
public class Message {
#Id
private String id;
private String title;
private String message;
private DateTime receiveDate;
}
[EDIT]
I have tried it by my own:
#Query("FROM MessageUserConnection AS muc WHERE muc.confirmationNeeded = ?0 AND muc.message.receiveDate = ?1")
List<MessageUserConnection> findMessageUserConnectionByConfirmationNeededAndReceiveDate(final Boolean confirmationNeeded, final DateTime receiveDate);
and I get the following exception:
Caused by: com.mongodb.util.JSONParseException:
FROM MessageUserConnection AS muc WHERE muc.confirmationNeeded = "_param_0" AND muc.message.receiveDate = "_param_1"
Does anyone know what I am doing wrong here?
Thanks a lot!
[EDIT]
I run into another problem. My query currently looks like this.
#Query("{$and : [{'confirmationNeeded' : ?0}, {'message.receiveDate' : ?1}]}")
where confirmationNeeded is a boolean and message.receiveDate is Joda#DateTime. With this query I get the following exception:
org.springframework.data.mapping.model.MappingException: Invalid path reference message.receiveDate! Associations can only be pointed to directly or via their id property!
Does that mean that I only can join to message.id?
Thanks a lot!

JPA CriteriaBuilder: Predicate with a Serializable field throws an exception

I have an entity with a persistent field declared as Serializable.
I would like to build a query with the CriteriaBuilder, that filters the results by the Serializable field.
The database is Oracle, and the field type is RAW(255) as hbm2ddl defined it.
If i write the query with a plain JPQL TypedQuery, everything works fine (the Serializable field is the one with the name "entityId"):
TypedQuery<Change> query = em.createQuery("FROM Change c WHERE c.entityClass = :class AND c.entityId = :id", Change.class);
query.setParameter("class", Person.class.getName());
query.setParameter("id", new Integer(2287));
query.getResultList();
However, the very same query with criteria builder does not work:
final CriteriaBuilder builder = em.getCriteriaBuilder();
final CriteriaQuery<Change> criteriaQuery = builder.createQuery(Change.class);
final Root<Change> from = criteriaQuery.from(Change.class);
final CriteriaQuery<Change> select = criteriaQuery.select(from);
final List<Predicate> predicates = new ArrayList<>();
predicates.add(builder.equal(from.get("entityClass"), Person.class.getName()));
predicates.add(builder.equal(from.get("entityId"), new Integer(2287)));
select.where(predicates.toArray(new Predicate[predicates.size()]));
final TypedQuery<Change> query = em.createQuery(select);
query.getResultList();
It throws the following exception after invoking getResultList():
[2013-05-21 16:12:45,960] [com.mycompany.myproduct.server.Main.startServer(Main.java:56)] [ERROR] [main] - Error starting Server: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)
...
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
...
Caused by: java.sql.SQLSyntaxErrorException: ORA-00932: inconsistent datatypes: expected BINARY got NUMBER
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
...
Change.java:
#Entity
#Table(name = "T_REVISION_CHANGE")
#SequenceGenerator(name = "seq_revision_change", sequenceName = "SEQ_REVISION_CHANGE", allocationSize = 1)
public class Change {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_revision_change")
private Integer id;
#Column(name = "ENTITY_CLASS")
private String entityClass;
#Column(name = "ENTITY_ID")
private Serializable entityId;
}
I tried to manually serialize the Integer but the same kind of exception was thrown saying that a Serializable instance was expected instead of a byte array... :)
Any comment would be much appreciated.

JPA unable to assign a new persisted entity in a many to one relationship

I have to JPA Entities defined with a bidirectional relationship many to one, hereby:
#Entity
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="DEPARTAMENTO_ID_GENERATOR",sequenceName="DEPARTAMENTO_SEQ")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="DEPARTAMENTO_ID_GENERATOR")
#Column(name="DEP_ID")
private long id;
#Column(name="DEP_DESC")
private String desc;
//bi-directional many-to-one association to Academico
#OneToMany(mappedBy="department")
private Set<Proffesor> proffesors;
//getters and setters
}
#Entity
#Table(name="ACADEMICOS")
public class Proffesor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="ACADEMICOS_ID_GENERATOR", sequenceName="ACADEMICOS_SEQ")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="ACADEMICOS_ID_GENERATOR")
#Column(name="ACD_ID")
private long id;
#ManyToOne(cascade={CascadeType.PERSIST,CascadeType.MERGE})
#JoinColumn(name="ACD_DEPADSCRITO_DEP")
private Department department;
// getters and setters.
}
After in a transactional Spring service I have the next code to manipulate the entities in this way.
#Transactional (propagation=Propagation.REQUIRED)
public void createDepartmentWithExistentProffesor(String desc,Long idAvaiableProf) {
// new department
Department dep = new Department();
dep.setDesc(desc);
HashSet<Proffesor> proffesors = new HashSet<Proffesor>();
dep.setProffesors(proffesors);
// I obtain the correct attached Proffesor entity
Proffesor proffesor=DAOQueryBasic.getProffesorById(idAvaiableProf);
// I asign the relationship beetwen proffesor and department in both directions
dep.addProffesors(proffesor);
// Persists department
DAODataBasic.insertDepartment(dep);
// The id value is not correct then Exception ORA-0221
System.out.println("SERVICIO: Departamento creado con id: " + dep.getId());
}
As I said in the comments the id of the new Department persisted is not a real database id inside the transaction, then it is produced an exception
Exception in thread "main" org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
........
Caused by: java.sql.BatchUpdateException: ORA-02291: integrity restiction (HIBERNATE_PRB.FK_ACD2DEP) violated - primary key don't found
I've tried in a test, persist the new departmen entity with no relationship with Proffesor and I've seen that the id of the new department persisted entity has not a valid value inside the transaction but out of the transaction already the id has a correct value.
But I need the correct value inside the transaction.
Can anybody help me?
Thank you in advance.
try this
#OneToMany(mappedBy="department",cascade = CascadeType.PERSIST)
private Set<Proffesor> proffesors;

Resources