I'm trying to create a web form that allows a user to add an item to a database. Problem is when I submit said form I get the following error:
Field error in object 'products' on field 'id': rejected value [null]; codes [typeMismatch.products.id,typeMismatch.id,typeMismatch.int,typeMismatch]; arguments
Here's my jsp page:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Add Item Page</title>
</head>
<body>
<h1> Add an Item </h1>
<form action="/addItem" method="post">
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" type = "text" id="name" name="name" required minlength="2" autocomplete="off">
</div>
<div class="form-group">
<label for="price">Price</label>
<input class="form-control" type = "text" id="price" name="price" required>
</div>
<div class="form-group">
<label for="quantity">Quantity</label>
<input class="form-control" type="number" id="quantity" name="quantity" required>
</div>
<div class="form-group">
<label for="foodGroup">Food Group</label>
<input class="form-control" type="text" id="foodGroup" name="foodgroup" required>
</div>
<button type="submit" class="btn btn-primary" value="submit">Add Product</button>
</form>
Home
</body>
Here's the relevant part of my Products class (the rest is typical getter/setter/toString. I also left out the package line at the top):
import javax.persistence.*;
#Entity
#Table(name="products")
public class Products
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
String name;
String foodgroup;
int quantity;
Float price;
String image;
private Products() {}
public Products(int id, String name, int quantity, float price, String foodgroup, String image)
{
this.id = id;
this.name = name;
this.quantity = quantity;
this.foodgroup = foodgroup;
this.price = price;
this.image = image;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
and finally the controller:
#Controller
public class MainController
{
#Autowired
private ProductsDAO productsDAO;
#GetMapping("/")
public ModelAndView showHome()
{
ModelAndView mav = new ModelAndView("index");
return mav;
}
#RequestMapping("/showProducts")
public ModelAndView showProducts()
{
List<Products> leListOfProducts = productsDAO.findAll();
ModelAndView mav = new ModelAndView("showProducts", "product", leListOfProducts);
return mav;
}
#RequestMapping("/addItem")
public ModelAndView showAddItemPage()
{
return new ModelAndView("addItem");
}
#PostMapping("/addItem")
public ModelAndView addProduct(Products product)
{
productsDAO.create(product);
return new ModelAndView("redirect:/index");
}
}
Here's a pic of the table columns and their settings.
I really appreciate any and all help ya'll can provide!
Best,
JBird
I suspect that your constructor is the issue there. Try removing the id in the constructor, as it is being generated by 3rd party means. Also, I don't think you will ever need the setId() method for any sane reason.
Related
I'm creating todo-app with spring-boot.
After adding a task, once I enter a check on checkbox provided next to each todo and then click the done-button, the error occurs.'Required request parameter 'id' for method parameter type Integer is present but converted to null' has occurs.
below is codes.
Todo.java
#Entity
#Data
#Table(name = "todos")
public class Todo {
#Id
#Nullable
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Nullable
#Column(name = "user_id", nullable = false)
private Integer userId;
#NotNull
#NotBlank
#Column(name = "title")
private String title;
#NotNull
#NotBlank
#Column(name = "description")
private String description;
#NotNull
#NotBlank
#Column(name = "due_date")
private String dueDate;
#Column(name = "priority")
private Integer priority;
#NotNull
#Column(name = "is_completed")
private Boolean isCompleted = false;
public Todo() {
}
public Todo(Integer userId, String title, String description, String dueDate, Integer priority, Boolean isCompleted) {
this.userId = userId;
this.title = title;
this.description = description;
this.dueDate = dueDate;
this.priority = priority;
this.isCompleted = isCompleted;
}
}
TodoController.java
#Controller
public class TodoController {
#Autowired
TodoRepository todoRepository;
#Autowired
TodoService todoService;
#GetMapping("/todo/{id}")
public String home(Model model, User user, Todo todo, #PathVariable("id")Integer id) {
Integer userId = user.getId();
if(userId == null) {
return "redirect:/todo/{id}";
}
List<Todo> list = todoRepository.find(userId);
model.addAttribute("list", list);
model.addAttribute("todo", new Todo(user.getId(), todo.getTitle(), todo.getDescription(), todo.getDueDate(), todo.getPriority(), todo.getIsCompleted()));
return "home";
}
#PostMapping("/todo/{id}")
public String createTodo(#Validated Todo todo,BindingResult result, User user) {
if(result.hasErrors()){
return "redirect:/todo/{id}";
}
Todo userTodo = new Todo(user.getId(), todo.getTitle(), todo.getDescription(), todo.getDueDate(), todo.getPriority(), todo.getIsCompleted());
todoService.addTodo(userTodo);
return "redirect:/todo/{id}";
}
#PostMapping("/todo/update/{id}")
public String doneTodo(#RequestParam(name="id")Integer todoId) {
Todo updateTodo = todoService.findById(todoId);
updateTodo.setIsCompleted(true);
todoService.addTodo(updateTodo);
return "redirect:/todo/{id}";
}
#PostMapping("/todo/edit/{id}")
public String editTodo() {
System.out.println("edit");
return "redirect:/todo/{id}";
}
}
TodoService.java
#Service
public class TodoService {
#Autowired
TodoRepository todoRepository;
public List<Todo> searchAll() {
return todoRepository.findAll();
}
public void addTodo(Todo todo) {
todoRepository.save(todo);
}
public Todo findById(Integer id) {
Optional<Todo> updateTodo = todoRepository.findById(id);
return updateTodo.orElseGet(updateTodo::get);
}
}
home.html
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link th:href="#{/css/home.css}" rel="stylesheet"/>
<title>Todo</title>
</head>
<body>
<h1>Todo</h1>
<div>
<form method="post" th:action="#{/todo/}+${id}" th:object="${todo}">
<p th:errors="*{title}" class="todo-error-message"></p>
<input th:field="*{title}" class="add-input" type="text" placeholder="title">
<input th:field="*{description}" class="add-input" type="text" placeholder="details">
<input th:field="*{dueDate}" class="add-input-third" type="date"><br/>
<div class="add-form">
<span>priority</span>
<input th:field="*{priority}" type="radio" name="priority" value="3" checked>3
<input th:field="*{priority}" type="radio" name="priority" value="2">2
<input th:field="*{priority}" type="radio" name="priority" value="1">1
</div>
<button type="submit" class="add-btn">add</button>
</form>
</div>
<h2>LIST</h2>
<form method="post" th:action="#{/todo/edit/}+${id}" th:each="list:${list}" th:object="${todo}">
<div th:if="${!todo.isCompleted}">
<input type="checkbox" th:id="${todo.id}" th:value="${todo.id}" th:field="*{isCompleted}" form="done-todo">
<input type="hidden" name="userId" th:value="${list.userId}">
<input type="text" name="title" th:value="${list.title}">
<input type="text" name="description" th:value="${list.description}">
<input type="date" name="dueDate" th:value="${list.dueDate}">
<input type="submit" value="update">
</div>
</form>
<form method="post" id="done-todo" th:action="#{/todo/update/}+${id}">
<input type="hidden" name="id" th:value="${todo.id}">
<input type="submit" value="done">
</form>
<div>
<button type="button" class="btn btn-primary p-3" data-toggle="collapse" data-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample">
show done todos
</button>
<div class="collapse" id="collapseExample">
<form class="border p-3" th:action="#{/todo/delete/}+${id}" th:each="todo:${todo}">
<div th:if="${todo.isCompleted}">
<input type="text" name="title" th:value="${todo.title}">
<input type="submit" value="delete">
</div>
</form>
</div>
</div>
<form th:action="#{/logout}" method="post">
<button class="logout-button" type="submit" value="logout">logout</button>
</form>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
</body>
</html>
What I want to do
I aim to change the value of added todo from false to true after entering a check on checkbox provided next to each todo and then click the done-button.
I aim to show todos of which value is true.(true means 'done')
Does anyone know how to fix this?
I couldn't find the way to fix this and I’m so badly stuck at this task.
I am working on a quiz app. I want to the user to be able to create a quiz with 10 questions (although I am open to this being variable length). If I create more than 1 question with #ModelAttribute, instead of getting more than 1 QuestionAnswerInfo objects, I get one with each field separated by commas. This does not seem to be a List, but simply Strings separated by commas.
I want each question to come in and be handled separately. What is the best way to approach this?
This is the best answer I have found but I can't seem to make sense of it in my context: Send multiple objects of same class from jsp to spring controller
Models
(Abstract Entity is just ID generation)
#Entity
public class Quiz extends AbstractEntity {
public String name;
#OneToMany (cascade = CascadeType.ALL)
#JoinColumn(name = "quiz_question_foreign_id", referencedColumnName = "id")
private List<QuestionAnswerInfo> questions = new ArrayList<>();
public Quiz(){}
public Quiz(String name, ArrayList<QuestionAnswerInfo> questions) {
super();
this.name = name;
this.questions = questions;
}
//Getters and Setters
}
#Entity
public class QuestionAnswerInfo extends AbstractEntity{
private String question;
private String answer;
private String questionType;
private String additionalAnswerInfo;
public QuestionAnswerInfo (){}
public QuestionAnswerInfo(String question, String answer, String questionType, String additionalAnswerInfo) {
super();
this.question = question;
this.answer = answer;
this.questionType = questionType;
this.additionalAnswerInfo = additionalAnswerInfo;
}
//Getters and Setters
Controller
#Controller
public class QuizController {
//Repositories
#RequestMapping("create")
public String displayCreateNewQuiz(Model model) {
model.addAttribute(new Quiz());
model.addAttribute("questions", new QuestionAnswerInfo());
return "create";
}
#PostMapping("create")
public String processCreateNewQuiz(#ModelAttribute Quiz newQuiz, #ModelAttribute QuestionAnswerInfo questions,
Model model) {
newQuiz.getQuestions().add(questions);
quizRepository.save(newQuiz);
return "index";
}
View
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h1>Create a Quiz</h1>
<form method="post">
<div>
<label th:for="name">Quiz Name</label>
<input th:field="${quiz.name}"/>
</div>
<br>
<p>Question 1</p>
<div>
<label th:for="question">Add a Question</label>
<input th:field="${questions.question}"/>
</div>
<div>
<label th:for="answer">Add an Answer</label>
<input th:field="${questions.answer}"/>
</div>
<div>
<label th:for="questionType">Question Type</label>
<input th:field="${questions.questionType}"/>
</div>
<div>
<label th:for="additionalAnswerInfo">Add Additional Information</label>
<input th:field="${questions.additionalAnswerInfo}"/>
</div>
<p>Question 2</p>
<div>
<label th:for="question">Add a Question</label>
<input th:field="${questions.question}"/>
</div>
<div>
<label th:for="answer">Add an Answer</label>
<input th:field="${questions.answer}"/>
</div>
<div>
<label th:for="questionType">Question Type</label>
<input th:field="${questions.questionType}"/>
</div>
<div>
<label th:for="additionalAnswerInfo">Add Additional Information</label>
<input th:field="${questions.additionalAnswerInfo}"/>
</div>
<input type="submit" value="Create Quiz"/>
</form>
</body>
</html>
I will eventually have a similar problem with collecting User answers when they take the quiz.
you need to use js to handle multiple questions so that in the request there will be list of questions in the request body.
Could anyone take a look of this error and help me? I spent 2 hours to find problem but I didn't find solution.
Listing data works, but problem is when adding jsp with action="saveCategory.
org.springframework.beans.NotReadablePropertyException: Invalid property 'categoryName' of bean class [java.util.ArrayList]: Bean property 'categoryName' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
JSP:
<c:forEach var="tempInvoiceCategory" items="${invoiceCategory}">
<tr>
<td>${tempInvoiceCategory.categoryName}</td>
<td>
<button type="button" class="btn btn-info">Edit</button>
<button type="button" class="btn btn-danger">Delete</button>
</td>
</tr>
</c:forEach>
<form:form class="forms-sample" action="saveCategory" modelAttribute="invoiceCategory" method="POST">
<div class="form-group">
<label>Nazwa Kategorii</label>
<form:input path="categoryName" type="text" class="form-control"/>
</div>
<button type="submit" class="btn btn-primary mr-2">Submit</button>
</form:form>
Entity:
#Entity
#Table(name="T_INVOICE_CATEGORY")
public class InvoiceCategory {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID_CATEGORY")
private int id;
#Column(name="CATEGORY_NAME")
private String categoryName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
#Override
public String toString() {
return "InvoiceCategory [id=" + id + ", categoryName=" + categoryName + "]";
}
}
Controller:
#Controller
#RequestMapping("/invoice")
public class InvoiceController {
#Autowired
private InvoiceCategoryService invoiceCategoryService;
#GetMapping("/listCategory")
public String listCategory(Model theModel) {
List<InvoiceCategory> invoiceCategory = invoiceCategoryService.getInvoiceCategory();
theModel.addAttribute("invoiceCategory",invoiceCategory);
return "add-invoice-category";
}
#PostMapping("/saveCategory")
public String saveCustomer(#ModelAttribute("invoiceCategory") InvoiceCategory theInvoiceCategory) {
invoiceCategoryService.saveInvoiceCategory(theInvoiceCategory);
return "redirect:/customer/list";
}
}
I think problem is that model attribute invoiceCategory is List<InvoiceCategory> and in same JSP you try build list of items and form which try access to this item, but it is List<InvoiceCategory>:
<form:form class="forms-sample" action="saveCategory" modelAttribute="invoiceCategory" method="POST">
<div class="form-group">
<label>Nazwa Kategorii</label>
<form:input path="categoryName" type="text" class="form-control"/>
</div>
<button type="submit" class="btn btn-primary mr-2">Submit</button>
</form:form>
Try comment this part and run application again.
I have one Controller : personController.java
#Controller
public class personController {
private static final Logger LOG = LoggerFactory.getLogger(OcaController.class);
#RequestMapping(value = "/person", method = {RequestMethod.POST, RequestMethod.GET})
public String ocaContract(#RequestBody String requestPerson) {
return requestPerson;
}
1 JSP : person.jsp
<html>
<head>
</head>
<body>
<form class="form-horizontal" METHOD="POST" ACTION="webmvc/person" ENCTYPE="x-www-form-urlencoded">
<div class="controls">
<input type="text" name="name" id="name" value="" placeholder="">
</div>
<div class="controls">
<input type="text" name="surname" id="surname" value="" placeholder="">
</div>
<input type="submit" value="ok"/>
</form>
</body>
</html>
and one Object Class : Person.java
#XmlRootElement(name="Person")
public class Person {
#XmlElement(required = true)
protected String name;
#XmlElement(required = true, nillable = true)
protected String surname;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
} ...
When I populate the JSP and click on the input button, my controller return this "requestPerson" string :
name=&surname=
Is it a way to have this string as a POJO ? My final result must be at the XML format :
<person>
<name>Lisala</name>
<surname>Lili</surname></person>
I hope you ll can help me because i'm on it since 1 day now and i didn't find an easy way to accomplish this.
You can replace #RequestBody with #ModelAttribute and String to Person
public String ocaContract(#ModelAttribute Person requestPerson) {
I created a form, that has 2 fields (product name and price) and dropdown list of category objects (product's categories).
I have no idea how to make this work, when I have got a of Category objects to be set in Product object.
Product:
public class Product {
private String name;
private Category category;
private int price;
public Product() {
}
public Product(String name, Category category, int price) {
this.name = name;
this.category = category;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Product controller:
#ModelAttribute("categoryList")
public List<Category> categoryList() {
return categoryService.getCategories();
}
#RequestMapping("/products/add")
public ModelAndView addProductForm() {
ModelAndView mv = new ModelAndView("addProduct");
mv.addObject("product", new Product());
return mv;
}
#RequestMapping(value = "/products/add/process", method = RequestMethod.POST)
public ModelAndView addProduct(#ModelAttribute("product") Product product) {
ModelAndView mv = new ModelAndView("products");
System.out.println("added " + product.getName() + " " + product.getPrice());
return mv;
}
The form:
<form class="form-horizontal" action="#"
th:action="#{/products/add/process}" th:object="${product}"
method="post">
<fieldset>
<!-- Form Name -->
<legend>Add product</legend>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="textinput">Product
name</label>
<div class="col-md-4">
<input id="textinput" name="textinput" placeholder="Product name"
class="form-control input-md" required="" type="text"
th:field="*{name}"></input>
</div>
</div>
<!-- Select Basic -->
<div class="form-group">
<label class="col-md-4 control-label" for="selectbasic">Category</label>
<div class="col-md-4">
<select th:field="*{category}">
<option th:each="cat : ${categoryList}" th:value="${cat.getId()}"
th:text="${cat.getName()}"></option>
</select>
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="textinput">Price</label>
<div class="col-md-4">
<input id="textinput" name="textinput" placeholder=""
class="form-control input-md" required="" type="text"
th:field="*{price}"></input>
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="singlebutton"></label>
<div class="col-md-4">
<button id="singlebutton" name="singlebutton"
class="btn btn-success">Add product</button>
</div>
</div>
</fieldset>
</form>
Additional Info from comments
When I submit it (see addProduct method - it's a form handler) I get: java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.example.shop.Category] for property 'category': no matching editors or conversion strategy found]. I simply can't convert String coming from dropdown to an object
Problem is that Spring doesn't have a built-in conversion ability from String to Category. It knows it needs a Category to use the setCategory(Category category) method of Product, but has no way of converting the String it gets from your posted drop down into one. Thus, you need to be a dear and help Spring some by telling it how to do the conversion and define a converter, see Spring docs for more info.
Easiest option is to use Converter SPI:
package com.example.shop.converter;
final class StringToCategoryConverter implements Converter<String, Category> {
public Category convert(String source) {
Category category;
// Put your conversion logic here
return category;
}
}
In your case, I'd guess you'll want to use: CategoryService.getCategory(int id) or a similar method.
Then you need to configure Spring to actually use your converter, here's an XML example of how to do that:
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.example.shop.converter.StringToCategoryConverter" />
</list>
</property>
</bean>