JSP Error messages don't show when using Spring Data JPA - spring

This is my repository:
#Repository
public interface ProductRepo extends CrudRepository<Product,Integer> {
public List<Product> findAll();
public Product findById(int id);
}
My Entity Bean:
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_id")
private Integer id;
#NotEmpty
#Column(name = "product_name")
private String productName;
Controller:
#PostMapping("/add-product")
String addProduct(#Validated #ModelAttribute("product")Product product, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
System.out.println("has errors: " + bindingResult.toString());
}
System.out.println( "adding Product ..." );
productService.save(product);
return "redirect:/products/success";
This is my view
<form:form action="add-product" method="post" modelAttribute="product">
<label for="productName">Product Name</label>
<form:input path="productName" id="productName" type="text" placeholder="Add product name"/>
<form:errors path="productName" />
...
It just works fine when using Hibernate SessionFactory to store to database, like this:
// A shorter way to save customer
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(customer);
but when replacing it with Spring Data JPA, it starts throwing exceptions and return 500 html pages instead of just rendering the error field as it used to be.
There are 4 exceptions thrown:
javax.validation.ConstraintViolationException: Validation failed for classes [com.luv2code.springdemo.entity.Product] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must not be empty', propertyPath=productName, rootBeanClass=class com.luv2code.springdemo.entity.Product, messageTemplate='{javax.validation.constraints.NotEmpty.message}'}
]
javax.persistence.RollbackException: Error while committing the transaction
org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:77)
org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:71)
org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536)
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
org.springframework.web.util.NestedServletException: 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
Previously, without using JPA, the validation error message shows in the view <form:errors />, not thrown as an exception with 500 http error code. What do I miss here?

The behavior you see has nothing to do with Spring Data JPA but the fact that you switched from plain hibernate (judging from your code snippets) to JPA.
When using JPA and using JSR-303 those will work together to prevent invalid entities from being entered in the database by throwing the validation exception. When using plain Hibernate this doesn't happen (at least the exceptions don't propagate).
Which was due to you have written your request handling method. In case of errors in the model you just do a System.out and happily continue the method, what you should have been doing there is return to the original view instead (I assume products/add-product).
#PostMapping("/add-product")
String addProduct(#Validated #ModelAttribute("product")Product product, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
System.out.println("has errors: " + bindingResult.toString());
return "products/add-product";
}
System.out.println( "adding Product ..." );
productService.save(product);
return "redirect:/products/success";
}
Basically you failed at handling the case of errors properly leading to exceptions being thrown due to the JPA and javax.validation working in a united fashion.

Related

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.

CRUDRepository unable to save modified entities

I'm trying to fetch some data from the database, update a field with some other entity and save it back to the DB, of course I've made sure that both the first entity and the entity that is going to be inserted are retrieved and fine, it is just thrown upon the save function invokation.
Here's the exception
[err] org.springframework.dao.DataIntegrityViolationException: Attempt to persist detached object "repository.entities.RequestEntity-0". If this is a new instance, make sure any version and/or auto-generated primary key fields are null/default when persisting.; nested exception is <openjpa-2.4.3-r422266:1833086 nonfatal store error> org.apache.openjpa.persistence.EntityExistsException: Attempt to persist detached object "repository.entities.RequestEntity-0". If this is a new instance, make sure any version and/or auto-generated primary key fields are null/default when persisting.
FailedObject: repository.entities.RequestEntity-0
The entity
#Entity
#Table(name="REQUEST")
public class RequestEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="REQUEST_ID")
private long requestId;
some other fields ....
//bi-directional many-to-one association to MStatus
#ManyToOne
#JoinColumn(name="STATUS")
private MStatus mStatus;
getters and setters here as well ..
}
And lastly, here's the code
private void doStuff() throws Exception {
List<RequestEntity> requestsList = requestRepo
.findByMStatusStatusContaining("TEXT");
RequestEntity requestItem;
if (requestsList.size() > 1 || requestsList.isEmpty()) {
throw new Exception("No requests found");
} else {
requestItem = requestsList.get(0);
}
requestItem.setMApprovalStatus(mapprovalStatus.findOne("TEXT_TWO"));
requestRepo.save(requestItem);
}

sequence does not exist, hibernate and JPA 2.1

I am getting an error saying
`Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
java.sql.SQLSyntaxErrorException: ORA-02289: sequence does not exist`
This error happens when I try to create a user.
#RequestMapping(method = POST)
public UserDto createUser(#RequestBody userDto user) {
Preconditions.checkNotNull(user);
return Preconditions.checkNotNull(service.create(user));
}
I am however able to delete and get just not create nor update. What is also frustrating is I get no error when trying to update, it just doesn't so it.
I am not getting any real lead on where to look. I have tried many different methods to resolve this with no avail.
I found a post that had this:
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQUENCE1")
#SequenceGenerator(name="SEQUENCE1", sequenceName="SEQUENCE1", allocationSize=1)
private int user_id;
At this link: SOF link
It is complaining about this entity which I generated with netbeans and I am currently using Intellij. Any advice would be appreciated.
The code that creates new Campaign entity seems to be incorrect.
public CampaignDto create(CampaignDto campaignDto) {
Campaign campaign = mapper.mapReverse(campaignDto);
System.out.println(campaign.toString());
// Following 2 lines must be added to obtain and use managed Shop entity
Shop existingShop = shopRepository.findOne(campaignDto.getShopId());
campaign.setShop(existingShop);
campaign = campaignRepository.save(campaign);
CampaignDto createdCampaign = mapper.map(campaign);
return createdCampaign;
}
It looks like you might not be setting Campaign.shopId field when creating new Campaign.
#JoinColumn(name = "SHOP_ID", referencedColumnName = "SHOP_ID")
#ManyToOne(optional = false)
private Shop shopId;
You might want to rename this field to just shop to make it clear what it holds as it's not just an identifier.
Depending on how you are persisting new objects you might need to add CascadeType.ALL on #ManyToOne to ensure that a new Shop is persisted together with a new Campaign.
#ManyToOne(optional = false, cascade = CascadeType.ALL)
Go to your application property file and put
hibernate.hbm2ddl.auto=true; It might be helpful Hibernate created this sequence and added a new row

Spring data and hibernate - model validations - exception translation

I use spring-data and hibernate. Now I would like to apply some validation to my model. In most cases I would like to apply simple validation like null-checking etc. But in some cases I would like to apply more strict validation, such as email-validation. I found very useful feature in Hibernate validator - the #Email annotation. It works very well but here is the problem:
If i try to save a model with null value, then the following exception is thrown:
org.springframework.dao.DataIntegrityViolationException
But if I try to save a model with non-null but non-email value (let's say asdfgh), then the following exception is thrown:
javax.validation.ConstraintViolationException
I would love to see only one type of exception in both cases, because in both cases the model didn't pass the validation and I would like just to worry about only one exception type in my exception-handling code.
I tried to add PersistenceExceptionTranslationPostProcessor to my bean configuration, but it looks like it does not change anything.
Do you have an idea how to "unify" this exceptions?
Model:
#Entity
public class ValidationModel {
...
#Email
#Column(nullable = false)
private String email;
...
}
Repository:
public interface ValidationModelRepository extends JpaRepository<ValidationModel, Long> {
}
#Column(nullable = false) is not a validation check. It's a JPA constraint.
To validate that a value is not null, use #NotNull.

OneToMany Create Fails with InvalidDataAccessApiUsageException

I am fairly new to Hibernate and have been using the manual & online forums, but I am stumped on this issue. I’m using Spring 3.2 with Hibernate 4 & Annotations. I have a parent (PledgeForm) & child (PledgeFormGiftLevel) table that is one-to-many.
Domain/Models:
Parent
#Entity
#Table(name="PLEDGE_FORMS")
#SuppressWarnings("serial")
public class PledgeForm implements Serializable {
static final Logger log = Logger.getLogger(PledgeForm.class);
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="pledge_form_seq")
#SequenceGenerator(name="pledge_form_seq", sequenceName="PLEDGE_FORM_SEQ")
#Column(name="ID", unique=true, nullable=false)
private Integer id;
….
#OneToMany(mappedBy="pledgeForm", fetch=FetchType.EAGER, cascade=CascadeType.ALL)//********1
private List<PledgeFormGiftLevel> pledgeFormGiftLevels = new ArrayList<PledgeFormGiftLevel>();
….
public List<PledgeFormGiftLevel> getPledgeFormGiftLevels() {
return this.pledgeFormGiftLevels;
}
public void setPledgeFormGiftLevels(List<PledgeFormGiftLevel> pledgeFormGiftLevels) {
this.pledgeFormGiftLevels = pledgeFormGiftLevels;
}
//I do not think the following method is needed, but I decided to try it just in case
public void addPledgeFormGiftLevels(PledgeFormGiftLevel pledgeFormGiftLevels) {
pledgeFormGiftLevels.setPledgeForm(this);
getPledgeFormGiftLevels().add(pledgeFormGiftLevels);
}
Child
#Entity
#Table(name="PLEDGE_FORM_GIFT_LEVELS")
#SequenceGenerator(name="pledge_form_gift_level_seq", sequenceName="PLEDGE_FORM_GIFT_LEVEL_SEQ")
#SuppressWarnings("serial")
public class PledgeFormGiftLevel implements Serializable {
static final Logger log = Logger.getLogger(PledgeFormGiftLevel.class);
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="pledge_form_gift_level_seq")
#Column(name="ID", unique=true, nullable=false)
private Integer id;
…
#ManyToOne(fetch=FetchType.EAGER)//yes?
#JoinColumn(name="PLEDGE_FORM_ID", referencedColumnName="ID", insertable=true, updatable=true)//yes?
private PledgeForm pledgeForm = new PledgeForm();
…
public PledgeForm getPledgeForm() {
return pledgeForm;
}
public void setPledgeForm(PledgeForm pledgeForm) {
this.pledgeForm = pledgeForm;
}
Controller (there is a graphic, so I have code to pull in the file):
#Controller
#SessionAttributes("pledgeForm")
public class PledgeFormController {
#Autowired
org.unctv.service.PledgeFormManager Service;
…
#RequestMapping(value = "/saveJdbcPledgeForm", method = RequestMethod.POST, params="save")
public ModelAndView save(
#ModelAttribute("pledgeForm")
#Valid PledgeForm pledgeForm, BindingResult result,
#RequestParam("logoImg") MultipartFile file,
#RequestParam(value="removeLogoImg", required=false) String removeLogoImg) throws Exception {
ModelAndView mav = null;
mav = new ModelAndView("pledgeFormSearch");//Name of the JSP
if (removeLogoImg != null) {
pledgeForm.setLogoFilename(null);
pledgeForm.setLogoImg(null);
pledgeForm.setLogoContentType(null);
} else if (file != null && file.getBytes().length > 0) {
pledgeForm.setLogoFilename(file.getOriginalFilename());
pledgeForm.setLogoImg(file.getBytes());
pledgeForm.setLogoContentType(file.getContentType());
}
Service.save(pledgeForm);
mav.addObject("pledgeForm", pledgeForm);//JSP Form's Command Name (pledgeForm);
mav.addObject("cmdName", "pledgeForm");
mav.addObject("actionType", "Save");
return mav;
}
Service:
#Service("simplePledgeFormManager")
#Transactional(readOnly=true)
public class SimplePledgeFormManager implements PledgeFormManager {
#Autowired
private HibernatePledgeFormDao hibernatePledgeFormDao;
…
#Transactional(readOnly=false)
public void save(PledgeForm pledgeForm) throws Exception {
hibernatePledgeFormDao.save(pledgeForm);
}
DAO:
#Repository("PledgeFormDAO")
public class HibernatePledgeFormDao implements PledgeFormDao {
static final Logger log = Logger.getLogger(HibernatePledgeFormDao.class);
#Autowired
private SessionFactory sessionFactory;
...
#Override
public void save(PledgeForm pledgeForm) throws Exception {
sessionFactory.getCurrentSession().saveOrUpdate(pledgeForm);
}
Using the code above, parent/child records can be selected and updated fine. When I display the “trace” messages from hibernate, the update does have this trace message about the child, though:
[2013-12-06 10:31:24,648] TRACE Persistent instance of: org.unctv.domainmodel.PledgeFormGiftLevel
[2013-12-06 10:31:24,649] TRACE Ignoring persistent instance
[2013-12-06 10:31:24,649] TRACE Object already associated with session: [org.unctv.domainmodel.PledgeFormGiftLevel#1]
The create always gives this error if there is a child record:
object references an unsaved transient instance - save the transient instance before flushing: org.unctv.domainmodel.PledgeForm; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: org.unctv.domainmodel.PledgeForm
When I look at the hibernate logs, I see that it updates the parent & the child based on transient objects. Then it tries to flush & finds a persistent copy of the child, so it rolls back everything.
[2013-12-06 10:34:13,615] TRACE Automatically flushing session
[2013-12-06 10:34:13,615] TRACE Flushing session
[2013-12-06 10:34:13,615] DEBUG Processing flush-time cascades
[2013-12-06 10:34:13,615] TRACE Processing cascade ACTION_SAVE_UPDATE for: org.unctv.domainmodel.PledgeForm
[2013-12-06 10:34:13,615] TRACE Cascade ACTION_SAVE_UPDATE for collection: org.unctv.domainmodel.PledgeForm.pledgeFormGiftLevels
[2013-12-06 10:34:13,615] TRACE Cascading to save or update: org.unctv.domainmodel.PledgeFormGiftLevel
[2013-12-06 10:34:13,616] TRACE Persistent instance of: org.unctv.domainmodel.PledgeFormGiftLevel
[2013-12-06 10:34:13,616] TRACE Ignoring persistent instance
[2013-12-06 10:34:13,616] TRACE Object already associated with session: [org.unctv.domainmodel.PledgeFormGiftLevel#51]
[2013-12-06 10:34:13,616] TRACE Done cascade ACTION_SAVE_UPDATE for collection: org.unctv.domainmodel.PledgeForm.pledgeFormGiftLevels
[2013-12-06 10:34:13,616] TRACE Done processing cascade ACTION_SAVE_UPDATE for: org.unctv.domainmodel.PledgeForm
[2013-12-06 10:34:13,617] DEBUG Dirty checking collections
[2013-12-06 10:34:13,617] TRACE Flushing entities and processing referenced collections
[2013-12-06 10:34:13,617] DEBUG Collection found: [org.unctv.domainmodel.PledgeForm.pledgeFormGiftLevels#51], was: [<unreferenced>] (initialized)
[2013-12-06 10:34:13,618] DEBUG rolling back
[2013-12-06 10:34:13,618] DEBUG rolled JDBC Connection
The Hibernate documentation shows this as even simpler than I my code is, but I had to add the fetch & cascade values. I’ve played with changing the fetch & cascade values & placement (starting with the Hibernate documentation & then adding on), but everything else I try still causes the create to fail & often causes the update to fail too.
Many forum posts that I find show flush() or evict(). I am not certain if it is Hibernate 4 or annotations (#Transactional, I think) I’m using, but I do not see a place for that in my code. From the Hibernate trace logs, I can see that flushing is occurring automatically with in the saveOrUpdate() method.
I also tried dropping the tables & sequences & starting fresh.
Any advice about getting the create to work is appreciated. If you can point me to specific documentation that I missed, that is appreciated as well.
Thanks,
Bonnie
I noticed that equals and hashcode have not been overridden in the entities. These methods are used to compare objects to determine their equality. Hibernate may not be able to determine if an existing instance of the entity exists without these methods being overridden. Try providing implementations for hashcode and equals.
If your using Eclipse, press CTRL + SHIFT + S, H to bring up the dialog for creating the hashcode and equals methods. Pick fields that contain values that are relatively unchanged and then generate the methods.
Also be sure that you are managing both sides of the entity as discussed in the above comments:
public ModelAndView save(
#ModelAttribute("pledgeForm")
#Valid PledgeForm pledgeForm, BindingResult result,
#RequestParam("logoImg") MultipartFile file,
#RequestParam(value="removeLogoImg", required=false) String removeLogoImg) throws Exception {
ModelAndView mav = null;
mav = new ModelAndView("pledgeFormSearch");//Name of the JSP
//Manage both sides of the entity
List<PledgeFormGiftLevel> levels = pledgeForm.getPledgeFormGiftLevels();
for(PledgeFormGiftLevel level: levels){
level.setPledgeForm(pledgeForm);
}
if (removeLogoImg != null) {
pledgeForm.setLogoFilename(null);
pledgeForm.setLogoImg(null);
pledgeForm.setLogoContentType(null);
} else if (file != null && file.getBytes().length > 0) {
pledgeForm.setLogoFilename(file.getOriginalFilename());
pledgeForm.setLogoImg(file.getBytes());
pledgeForm.setLogoContentType(file.getContentType());
}
Service.save(pledgeForm);
mav.addObject("pledgeForm", pledgeForm);//JSP Form's Command Name (pledgeForm);
mav.addObject("cmdName", "pledgeForm");
mav.addObject("actionType", "Save");
return mav;
}

Resources