Spring ConversionFailedException when adding JPA annotations - java

I am learning Spring and I'm working my way through the tutorials. I have a form submission example and am trying to store the result using Hibernate/JPA. The example works with the following Entity:
package hello;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
//import javax.persistence.Entity;
//import javax.persistence.GeneratedValue;
//import javax.persistence.GenerationType;
//import javax.persistence.Id;
//#Entity
public class Comment {
// #Id
// #GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#NotNull
#Size(min=2, max=150)
private String comment;
#Size(min=2, max=150)
private String name;
#Override
public String toString() {
return String.format("Comment[id=%d, comment='%s', name='%s']", id, comment, name);
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
However if I remove the JPA commented lines the example throws the following exception when validating the form:
2016-11-04 14:59:32.716 WARN 17749 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to bind request element: org.springframework.beans.TypeMismatchException: Failed to convert value of type [java.lang.String] to required type [hello.Comment]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'Comment'; nested exception is java.lang.NumberFormatException: For input string: "Comment field..."
This seems to suggest it is trying to convert the value submitted for the "comment" field of the entity into a Long. Is it a case of providing an annotation to specify that the field is a String? I would have thought it would check the java type for this. Or am I doing something else wrong?
If you need the rest of the code let me know.
Edit: The error stopped when I removed this repository:
package hello;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CommentRepository extends CrudRepository<Comment, Long> {
List<Comment> findByName(String name);
}
Is there something wrong with the "Comment,Long" part? I copied these from the example and looking at the API for CrudRepository it looks correct as the Entity type is Comment and the ID type is Long.
I'm binding the values in a WebMvcConfigurerAdapter in the following method:
#PostMapping("/")
public String checkPersonInfo(#Valid Comment comment, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}

Related

Error 405: Method not Allowed on DELETE and PUT

I followed this spring tutorial and everything worked fine. After this, I decided to add delete and modify funcionalities. I've implemented them, but when I try to use them, I got the following error:
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/delete"}
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/modify"}
The commands that I'm executing:
curl localhost:8080/demo/delete -d name=First
curl localhost:8080/demo/modify -d name=First -d email=abc#gmail.com
//if the name doesn't exist in both methods, it will return "Nonexistent user!"
Below are the following code of:
MainController.java
package com.example.accessingdatamysql;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping(path="/demo")
public class MainController {
#Autowired
private UserRepository userRepository;
#PostMapping(path="/add")
public #ResponseBody String addNewUser (#RequestParam String name,
#RequestParam String email) {
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "Saved\n";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<User> getAllUsers() {
return userRepository.findAll();
}
/* ----------------- NEW METHODS THAT I'VE CREATED ----------------- */
#DeleteMapping(path="/delete")
public String delete(#RequestParam String name) throws Exception {
if(userRepository.findByName(name).equals(null)) {
System.out.println("Nonexistent user!\n");
}
userRepository.deleteByName(name);
return "User successfully deleted!\n";
}
#PutMapping(path="/modify")
public String modify(#RequestParam String name, #RequestParam String email) throws Exception {
if(userRepository.findByName(name).equals(null)) {
System.out.println("Nonexistent user!\n");
}
userRepository.deleteByName(name);
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "User successfully modified!\n";
}
}
User.java
package com.example.accessingdatamysql;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserRepository.java
package com.example.accessingdatamysql;
import org.springframework.data.repository.CrudRepository;
import com.example.accessingdatamysql.User;
public interface UserRepository extends CrudRepository<User, Integer> {
public User findByName(String name);
public void deleteByName(String name);
}
I've no idea what's happening here. I've searched for other similar questions like that with the same error but none of them solved my problem.
You are executing the calls with the method POST rather than DELETE for /demo/delete and PUT for /demo/modify.
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/delete"}
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/modify"}
You are not showing how are you executing those failing calls, but for example, if you are using a GUI client such Postman to simulate the calls, check that you are selecting the proper HTTP method.
And if you are using the curl library in the terminal pay attention to the method set:
curl -X "DELETE" http://your.url/demo/delete ...
curl -X "PUT" http://your.url/demo/modify ...
After following #Dez's answer, the problem was solved but I was getting other error:
No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
I solved it by adding #Transactional on DELETE and PUT methods in MainController class:
#DeleteMapping(path="/delete")
#Transactional //added
public void delete(#RequestParam String name) throws Exception { (...) }
#PutMapping(path="/modify")
#Transactional //added
public void modify(#RequestParam String name, #RequestParam String email) throws Exception { (...) }
And modified the exception throwing condition, that was always returning false:
if(userRepository.findByName(name).equals(null)) {
throw new Exception("Nonexistent user!");
}
to
if(userRepository.findByName(name) == null) {
throw new Exception("Nonexistent user!");
}
This is how #DeleteMapping and #PutMapping work. These annotations are shortcuts to #RequestMapping with method attribute fixed to DELETE or PUT correspondingly. To allow multiple HTTP methods for an endpoint you should list them explicitly:
#RequestMapping(path = "/modify", method = { RequestMethod.DELETE, RequestMethod.POST })
public String modify(...){
...
}

Unable to map a JSON field with Hyphen to the Java Object field

The Java POJO is like this:
import javax.validation.Valid;
import com.fasterxml.jackson.annotation.JsonProperty;
Class MyClass{
#JsonProperty(value = "config-meta-info")
#Valid
private ConfigMetaInformation configMetaInfo;
#JsonProperty(value = "name")
#Valid
private String name;
public MyClass(){}
public MyClass(String name,ConfigMetaInformation configMetaInfo){
this.name=name;
this.configMetaInfo=configMetaInfo;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("config-meta-info")
public ConfigMetaInformation getConfigMetaInfo() {
return configMetaInfo;
}
#JsonProperty("config-meta-info")
public void setConfigMetaInfo(ConfigMetaInformation configMetaInfo) {
this.configMetaInfo= configMetaInfo;
}
}
I am Using the JSON as below:
{
"name":"abc",
"config-meta-info":"someInfo"
}
But when I try to get the Data from the MongoDB document , I am seeing the config-meta-info as null.
Am I missing anything to handle this kebab-case key?
I could be wrong in the case of MongoDB but in other JSON based databases, they don't allow hyphenation in the field/key, underscores are usually preferred. Instead of config-meta-info, try config_meta_info.
The structure which you showed:
{
name:"abc",
config-meta-info:"someInfo"
}
is not a JSON.
Specification RFC-8259 defines all types (6) but what is name or config-meta-info? It can be JavaScript, but not a JSON.
The proper JSON:
{
"name":"abc",
"config-meta-info":"someInfo"
}
And you can use hyphen without limitations.

How to get rid of automatic installation of a new table

Problem: a new table is created once when I make a post request through The bash console. The rest of the queries go to the new table.
Than he does not like those databases which are available. As I understand - they just don't know, but I don't know how to direct it in the right. Although all variables are also named.
A problem was found created due to an Entity annotation in the Message class. Please tell me how to make it added to an existing table, tried #Table(name = "ApiTable") to an existing one, and it generates a new api_table.. Also don't quite understand what needs to be added/changed to accept json post requests.
Application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories("com.example.api")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MainController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping(path="/demo") /
public class MainController {
#Autowired
private UserRepository TestApi;
#PostMapping(path="/add")
public #ResponseBody String addNewUser (#RequestParam String name
, #RequestParam String email) {
Message n = new Message();
n.setName(name);
n.setEmail(email);
TestApi.save(n);
return "Saved";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<Message> getAllUsers() {
return TestApi.findAll();
}
}
Message
import javax.persistence.*;
#Entity
public class Message {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserRepository
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<Message, Integer> {
}
application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost/Test?useUnicode=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
The problem seems to be Spring Boot's default naming strategy which you'd have to replace.
Spring Boot's default naming strategy now seems to include converting camelCase to snake_case so you need to choose a different one (or implement your own).
Here's some more info on the topic: Hibernate naming strategy changing table names

Rest web services not returning XML response and even no log on console in eclipse

Creating an restful application but it is not returning the response in XML. Even there is no log on the console when hitting the URL "http://localhost:8080/message/webapi/messages".
I am returning a list and using #Produces(MediaType.APPLICATION_XML) to return the response in XML.
MessageResource.java
package org.porwal.restful.message.resources;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.porwal.restful.message.model.Message;
import org.porwal.restful.message.service.MessageService;
#Path("/messages")
public class MessageResource {
MessageService ms = new MessageService();
#GET
#Produces(MediaType.APPLICATION_XML)
public List<Message> getMessage(){
return ms.getAllMessage();
}
}
Message.java
package org.porwal.restful.message.model;
import java.util.Date;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement( name = "Message" )
public class Message {
public long id;
public String message;
public Date created;
public String author;
public Message() {
}
public Message(long id, String message, String author) {
this.id = id;
this.message = message;
this.author = author;
this.created = new Date();
}
public long getId() {
return id;
}
#XmlElement (name = "ID")
public void setId(long id) {
this.id = id;
}
public String getMessage() {
return message;
}
#XmlElement (name = "Message")
public void setMessage(String message) {
this.message = message;
}
public Date getCreated() {
return created;
}
#XmlElement (name = "Created")
public void setCreated(Date created) {
this.created = created;
}
public String getAuthor() {
return author;
}
#XmlElement (name = "Author")
public void setAuthor(String author) {
this.author = author;
}
}
This is working if i do not use #XMLRootElement annotation and TEXT_PLAIN is returned well through the URL. I also tried to remove #XmlElement for each fields but no luck. When i remove #XMLRootElement then MessageBodyWriter error can be seen in logs on eclipse console but when includes #XMLRootElement then no logs on eclipse console and URL "http://localhost:8080/message/webapi/messages" throws the error:
Error in case of #XmlRootElement is missing.
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo SEVERE: MessageBodyWriter not found for media type=application/xml, type=class java.util.ArrayList, genericType=java.util.List<org.porwal.restful.message.model.Message>. This exception comes only when i commented the line "//#XmlRootElement( name = "Message" )".
HTTP Status 500 – Internal Server Error
Can someone please tell what i am missing here?
You need to make all your fields in the Message class private. If you leave them as public, then JAXB will treat it as a property, and will consider it duplicate properties as you also haves JavaBean properties (getters/setters).
#XmlRootElement( name = "Message" )
public class Message {
private long id;
private String message;
private Date created;
private String author;
// ...
}
How I figured this out was by using a generic ExceptionMapper
#Provider
public class DebugExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception exception) {
exception.printStackTrace();
return Response.serverError().entity(exception.getMessage()).build();
}
}
You can register this with your application and it will catch unmapped exceptions and you can do whatever you want with it. Here we just print the stack trace. If we don't handle it, it will just get swallowed up and we will never know what happened.
When running the app with the ExceptionMapper, here's the error message I got.
Caused by: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 4 counts of IllegalAnnotationExceptions
Class has two properties of the same name "author"
this problem is related to the following location:
at public java.lang.String com.example.Message.getAuthor()
at com.example.Message
this problem is related to the following location:
at public java.lang.String com.example.Message.author
at com.example.Message
Class has two properties of the same name "created"
this problem is related to the following location:
at public java.util.Date com.example.Message.getCreated()
at com.example.Message
this problem is related to the following location:
at public java.util.Date com.example.Message.created
at com.example.Message
Class has two properties of the same name "id"
this problem is related to the following location:
at public long com.example.Message.getId()
at com.example.Message
this problem is related to the following location:
at public long com.example.Message.id
at com.example.Message
Class has two properties of the same name "message"
this problem is related to the following location:
at public java.lang.String com.example.Message.getMessage()
at com.example.Message
this problem is related to the following location:
at public java.lang.String com.example.Message.message
at com.example.Message
You can clearly see what the problem is. And aside from avoiding this error, this is how encapsulation is supposed to work anyway; the fields should be private and exposed via getters and setters.

Spring Error: No property prod found for type Product [duplicate]

This question already has answers here:
Spring-Data-Jpa Repository - Underscore on Entity Column Name
(5 answers)
Closed 4 years ago.
I am new to Spring and facing some problem.
My entity has a field:
package com.ecommercesystem.ecommerce.entities;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.sql.Blob;
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer ID_product;
#NotNull
private String prod_name;
#NotNull
private String prod_desc;
#NotNull
private float price;
#NotNull
private String prod_brand;
#NotNull
private Blob prod_image;
private int prod_rating;
#ManyToOne
private Category category;
protected Product(){}
public Product(Integer ID_product, #NotNull String prod_name, #NotNull
String prod_desc, #NotNull float price, #NotNull String prod_brand,
#NotNull Blob prod_image, int prod_rating, Category category) {
this.ID_product = ID_product;
this.prod_name = prod_name;
this.prod_desc = prod_desc;
this.price = price;
this.prod_brand = prod_brand;
this.prod_image = prod_image;
this.prod_rating = prod_rating;
this.category = category;
}
public Integer getID_product() {
return ID_product;
}
public void setID_product(Integer ID_product) {
this.ID_product = ID_product;
}
public String getProd_name() {
return prod_name;
}
public void setProd_name(String prod_name) {
this.prod_name = prod_name;
}
public String getProd_desc() {
return prod_desc;
}
public void setProd_desc(String prod_desc) {
this.prod_desc = prod_desc;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getProd_brand() {
return prod_brand;
}
public void setProd_brand(String prod_brand) {
this.prod_brand = prod_brand;
}
public Blob getProd_image() {
return prod_image;
}
public void setProd_image(Blob prod_image) {
this.prod_image = prod_image;
}
public int getProd_rating() {
return prod_rating;
}
public void setProd_rating(int prod_rating) {
this.prod_rating = prod_rating;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
}
My Product Repository is something like this:
package com.ecommercesystem.ecommerce.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import com.ecommercesystem.ecommerce.entities.Product;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
// This will be AUTO IMPLEMENTED by Spring into a Bean called
productRepository
// CRUD refers Create, Read, Update, Delete
#Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
Optional<Product> findByProd_name(String prod_name);
// Optional<Product> findByProd_brand (String prod_brand);
}
and my Product Controller is something like this:
package com.ecommercesystem.ecommerce.controllers;
import com.ecommercesystem.ecommerce.entities.Category;
import com.ecommercesystem.ecommerce.entities.Product;
import com.ecommercesystem.ecommerce.repositories.ProductRepository;
//import com.ecommercesystem.ecommerce.services.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.ArrayList;
import java.util.Optional;
#Controller
public class ProductController {
#Autowired
ProductRepository productRepository;
#GetMapping(path = "/")
public String front() {
return "index";
}
#GetMapping(path = "/search_results")
public String search(#RequestParam(value = "search", required = false)
String search, Model model){
Optional<Product> productName =
productRepository.findByProd_name(search);
if (productName.isPresent()){
if (search.equals(productName.get().getProd_name())){
model.addAttribute("ID_product", productName.get().getID_product());
model.addAttribute("price", productName.get().getPrice());
model.addAttribute("prodName", productName.get().getProd_name());
model.addAttribute("category_name",
productName.get().getCategory().getCategory_name());
// model.addAttribute("prodDesc", productName.get().getProd_desc());
// model.addAttribute("prodImage", productName.get().getProd_image());
// model.addAttribute("prodBrand", productName.get().getProd_brand());
}
}
return "search_results";
}
}
But when i run the program i got this Error Message:
Error Log:
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 10.277 s <<< FAILURE! - in com.ecommercesystem.ecommerce.EcommerceApplicationTests
[ERROR] contextLoads(com.ecommercesystem.ecommerce.EcommerceApplicationTests) Time elapsed: 0.017 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productController': Unsatisfied dependency expressed through field 'productRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'productRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional com.ecommercesystem.ecommerce.repositories.ProductRepository.findByProd_name(java.lang.String)! No property prod found for type Product!
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'productRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional com.ecommercesystem.ecommerce.repositories.ProductRepository.findByProd_name(java.lang.String)! No property prod found for type Product!
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.Optional com.ecommercesystem.ecommerce.repositories.ProductRepository.findByProd_name(java.lang.String)! No property prod found for type Product!
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property prod found for type Product!
Any help is highly appreciated. Thank You!
Spring Data uses camelCase convention when searching for method names
public void setProdName(String prodName) {
this.prodName = prodName;
}
public String getProdName() {
return prodName;
}

Categories

Resources