While run spring boot project, got error in home page Invalid property 'district' of bean class.
I know why this error is coming because district is property of child entity and i can pass parent entity from Home()method in controller. I could pass Person entity in model in Home() method. but district and city property is from Address entity I am working with OneToOne relationship mapping.
My question are below:
Can we get two entity together in th:object in thymeleaf
Can we send Address and Person entity together using Model from controller to view
Stacktrace:
Caused by: org.springframework.beans.NotReadablePropertyException: Invalid property 'district' of bean class [com.rest.RestApiPojo.Entity.Person]: Bean property 'district' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Here down is my code:
Entity
#Entity
#Table(name = "person_master")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long p_id;
private String name;
private String surname;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Address address;
// getter setter
}
#Entity
#Table(name = "address_master")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long a_id;
private String district;
private String city;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
#JoinColumn(name = "p_id")
private Person person;
// getter setter
}
service
#Override
public Person addPersonAddress(Person person) {
return personRepo.save(person);
}
controller
#RequestMapping(value = "/", method = RequestMethod.GET)
public String Home(Model mdl)
{
mdl.addAttribute("persons", new Person());
return "register";
}
#RequestMapping(value = "/personaddress", method = RequestMethod.POST)
public String addPersonAddress(Model mdl, #ModelAttribute("person") Person person, HttpServletRequest req)
{
Address address = person.getAddress(); // get reference of person from parent table and store in child table
address.setDistrict(req.getParameter("district"));
address.setCity(req.getParameter("city"));
address.setPerson(person);
pojoService.addPersonAddress(person);
return "listofperson";
}
Thymeleaf
<form th:action="#{/personaddress}" th:object="${persons}" method="post">
<div class="container">
<h1 style="text-align: center">Add Person</h1>
<div class="row">
<div class="col-sm-12">
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">Person name</label>
<input type="text" class="form-control" name="name" th:field="*{name}">
</div>
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">Person surname</label>
<input type="text" class="form-control" name="surname" th:field="*{surname}">
</div>
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">District</label>
<input type="text" class="form-control" name="district" th:field="*{district}">
</div>
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">City</label>
<input type="text" class="form-control" name="city" th:field="*{city}">
</div>
<input class="btn btn-primary" type="submit" value="Submit">
</div>
</div>
</div>
</form>
Two entities are not required as they have already been mapped in the person class just write 'address.district' and 'address.city' in the thymeleaf, you will get it
This is regarding boilerplate code. you could add #Data to the class coming from Lombok library.
If you are not using lombok add setter and getter
public String getDistrict() {
return district;
}
public void setDistrict(String district) {
this.district= district;
}
Related
Spring/Thymeleaf beginner sorry in advance but I have 2 entities Employee and MeetingInfo. Employee has a oneToMany relationship with MeetingInfo so basically an Employee can have many MessageInfo. Using psvm I can add a new Employee with multiple MessageInfo to my database using something like this:
Employee employee1 = new Employee("Employee 1");
MeetingInfo mInfo1 = new MeetingInfo(LocalDate.of(2021, 1, 1), "First Random message");
MeetingInfo mInfo2 = new MeetingInfo(LocalDate.of(2021, 2, 2), "Second Random message");
MeetingInfo mInfo3 = new MeetingInfo(LocalDate.of(2021, 3, 3), "Third Random message");
employee1.getMeetingInfo().add(mInfo1);
employee1.getMeetingInfo().add(mInfo2);
employee1.getMeetingInfo().add(mInfo3);
employeeRepository.save(employee1);
But how can I do this with a form in thymeleaf? I can add just an employee, but cant add a new MeetingInfo object. When I do I get a passException error.
My new_employee.html
<form action="#" th:action="#{/ines/saveEmployee}" th:object="${employee}"
method="POST">
<input type="text" th:field="*{name}"
placeholder="Employee Name" class="form-control mb-4 col-4">
*** so if I remove between here***
<input type="date" th:field="*{meetingInfo.meetingDate}"
placeholder="Message Date" class="form-control mb-4 col-4">
<input type="text" th:field="*{meetingInfo.message}"
placeholder="Message" class="form-control mb-4 col-4">
*** and here***
*** how can I include a MessageInfo object with a new Employee?***
<button type="submit" class="btn btn-info col-2">Save Meeting</button>
</form>
My Controller
#GetMapping("/showNewEmployeeForm")
public String showNewEmployeeForm(Model model) {
Employee employee = new Employee();
model.addAttribute("employee", employee);
return "meeting/new_employee.html";
}
#PostMapping("/saveEmployee")
public String saveEmployee(#ModelAttribute("employee") Employee employee) {
employeeService.saveMessage(employee);
return "redirect:/ines/employees";
}
Employee
#Entity
#Table(name = "employee")
public class Employee {
#Id
#Column(name = "employee_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long employeeId;
#Column(nullable = false)
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id", referencedColumnName = "employee_id")
private List<MeetingInfo> meetingInfo = new ArrayList<>();
//Constructors, getters and setters
MeetingInfo
#Entity
#Table(name = "meeting_info")
public class MeetingInfo {
#Id
#Column(name = "meeting_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long meetingId;
private String message;
#Column(name = "meeting_date")
private LocalDate meetingDate;
//Constructors, getters and setters
Saving multiple entities with a single request isn't something that you would usually want to do with your Spring Boot app, however, since I understand that this is for practice only, you could do this by using a single DTO object that would hold the information for both entities:
public class EmployeeMeetingDTO {
private String employeeName;
private String meetingMessage;
private LocalDate meetingDate;
}
Your controller could then accept just a single DTO entity from the request:
#PostMapping("/saveEmployee")
public String saveEmployee(#ModelAttribute("employeeDto") EmployeeMeetingDTO employeeDto) {
employeeService.saveMessage(employeeDto);
return "redirect:/ines/employees";
}
And you can separately create both entities in your EmployeeService class. Your Thymeleaf form would then look something like this:
<form action="#" th:action="#{/ines/saveEmployee}" th:object="${employeeDto}"
method="POST">
<input type="text" th:field="*{employeeName}"
placeholder="Employee Name" class="form-control mb-4 col-4">
<input type="date" th:field="*{meetingDate}"
placeholder="Message Date" class="form-control mb-4 col-4">
<input type="text" th:field="*{meetingMessage}"
placeholder="Message" class="form-control mb-4 col-4">
<button type="submit" class="btn btn-info col-2">Save Meeting</button>
</form>
While working on a spring MVC based project I am trying to bind data from JSP to model.
While the string data is binding perfectly for some reason the number data is not binding at all.
I have checked the parameter name it's same in POJO and JSP
Below is my controller code
#RequestMapping(value = "/investor-signup", method = RequestMethod.GET)
public String registration(Model model) {
model.addAttribute("investor", new InvestorRegister());
return "investor-signup";
}
#RequestMapping(value = "/investor-signup", method = RequestMethod.POST)
public String registration(#ModelAttribute("investor") InvestorRegister investor, BindingResult bindingResult, Model model) {
System.out.println(investor.getFULL_NAME());
System.out.println(investor.getMOB_NO());
investorRegisterService.save(investor);
return "redirect:/login";
}
Below is my JSP code
<form:form method="post" action="investor-signup"
modelAttribute="investor" id="contact-form"
style="padding-top: 40px;" role="form" class="formcss" align="left">
<div class="controls">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<div class="col-md-2 ">
<label for="user_name">Full Name *</label>
</div>
<div class="col-md-10 p0">
<input id="user_name" type="text" name="FULL_NAME" class="form-control" required="required" data-error="Full Name is required.">
</div>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<div class="col-md-2">
<label for="user_mobile">Mobile *</label>
</div>
<div class="col-md-6 ">
<input id="user_mobile" type="number" name="MOB_NO" class="form-control" required="required" data-error="Mobile No is required.">
</div>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12" align="center">
<input type="submit" class="btn btn-success btn-send" value="Register">
</div>
</div>
</form:form>
Below is my model POJO
#Entity
#Table(name = "InvestorRegister")
public class InvestorRegister {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_GEN")
#SequenceGenerator(name = "SEQ_GEN", sequenceName = "IID_SEQ")
#Column(name = "ID")
private int ID;
#NotEmpty
#Column(name = "FULL_NAME")
private String FULL_NAME;
#Column(name = "MOB_NO")
private int MOB_NO;
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
public String getFULL_NAME() {
return FULL_NAME;
}
public void setFULL_NAME(String fULL_NAME) {
FULL_NAME = fULL_NAME;
}
public int getMOB_NO() {
return MOB_NO;
}
public void setMOB_NO(int mOB_NO) {
MOB_NO = mOB_NO;
}
}
While the string data is binding perfectly for some reason the number data is not binding at all
I have checked the parameter name it's same in POJO and JSP
Any help is appreciated.
#abhi314, Have you just tried by extracting field separately ?
I mean have you tried any of these just to check value comes from view side or not?
public String registration(#RequestParam("FULL_NAME") String FULL_NAME, #RequestParam("MOB_NO") int MOB_NO) {
//check value comes or not
}
OR
public String registration(#RequestBody InvestorRegister ir) {
//check value comes or not
}
Please do let me know if you get value or not by checking this way
You need to use Wrapper Integer instead of int.
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_GEN")
#SequenceGenerator(name = "SEQ_GEN", sequenceName = "IID_SEQ")
#Column(name = "ID")
private Integer ID;
Also I would suggest to use naming convention standards. Instead of ID declare id or most preferred as investorRegisterId.
After changing from int to Integer please regenerate the getter/setters for the same.
Your actual issue is failing on the concept of Java auto-boxing/unboxing.
I have the following entity persisted through h2 in a JPA spring project
public class Product implements DomainObject{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Version
private Integer version;
private String description;
private BigDecimal price;
private String imageUrl;
public Product() {
}
// getters and setters
}
I have an HTML page with a form to enter new data like this
<form class="form-horizontal" th:object="${product}"
th:action="#{/product}" method="post">
<input type="hidden" th:field="*{id}"/>
<div class="form-group">
<label class="col-sm-2 control-label">Description:</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:field="*{description}"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Price:</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:field="*{price}"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Image Url:</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:field="*{imageUrl}"/>
</div>
</div>
<div class="row">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
This is the method that's handling the post from the controller
#PostMapping("/product")
public String saveOrUpdateProduct(Product product){
productService.saveOrUpdate(product);
return "redirect:/product/"+product.getId();
}
And this is the saveOrUpdate method in the autowired service class that handles interaction with the database
private EntityManagerFactory emf;
#PersistenceUnit
public void setEmf(EntityManagerFactory emf) {
this.emf = emf;
}
#Override
public Product saveOrUpdate(Product domainObject) {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Product savedProduct = em.merge(domainObject);
em.getTransaction().commit();
return savedProduct;
}
When I go to the HTML page with the form and I try to submit I have
Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer';
nested exception is java.lang.NumberFormatException: For input string: "null"
Probably you have to annotate the param product in your controller:
#PostMapping("/product")
public String saveOrUpdateProduct(#RequestBody Product product)
Spring Javadoc for #RequestBody
#Target(value=PARAMETER)
#Retention(value=RUNTIME)
#Documented
public #interface RequestBody
/*Annotation indicating a method parameter should be bound to the body of the
web request. The body of the request is passed through an
HttpMessageConverter to resolve the method argument depending on the
content type of the request.*/
Hope this helps!
The problem had to do with
#PostMapping("/product")
public String saveOrUpdateProduct(Product product){
productService.saveOrUpdate(product);
return "redirect:/product/"+product.getId();
}
I used to call product.getId() from the original un-merged product and it didn't have an Id yet, i solved it returning a saved Product
#PostMapping("/product")
public String saveOrUpdateProduct(Product product){
Product savedProduct = productService.saveOrUpdate(product);
return "redirect:/product/"+savedProduct.getId();
}
Use #JsonIgnoreProperties(ignoreUnknown = true) with your DTO
I'm doing an application using Spring MVC and I have a problem when I try to register an bank account to a specific person in my application. In this case, one Person has many accounts:
There are two classes in this relationship:
Sumarizing:
#Entity
public class Pessoa {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int idPessoa;
#OneToMany(mappedBy = "pessoa", targetEntity = ContaCorretora.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<ContaCorretora> contaCorretora;
#Entity
public class ContaCorretora {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int idConta;
private TipoConta tipoConta;
private TipoRisco tipoRisco;
private String login;
private String senha;
private BigDecimal valorAtual;
#ManyToOne
#JoinColumn(name="idPessoa")
private Pessoa pessoa;
Controller Class:
#RequestMapping("/pessoa")
#Controller
public class ContaCorretoraController {
#Autowired
private ContaCorretoraDAO contaCorretoraDao;
#Autowired
private PessoaDAO pessoaDao;
#RequestMapping("/pessoacorretora/{id}")
public ModelAndView pessoaCorretora(#PathVariable("id") int id, ContaCorretora contaCorretora ) {
contaCorretora = new ContaCorretora();
Map<String, Object> model = new HashMap<String, Object>();
Pessoa pessoa = pessoaDao.find(id);
model.put("contaCorretora", contaCorretora);
model.put("pessoa", pessoa);
model.put("tipoConta", TipoConta.values());
model.put("tipoRisco", TipoRisco.values());
return new ModelAndView("pessoa/contacorretora", "model", model);
}
#RequestMapping(value="/pessoacorretora/finalizar", method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
contaCorretoraDao.gravar(contaCorretora);
return new ModelAndView("redirect:pessoa/contacorretora");
}
I really don't know if the controller class is correct, but probably the problem is there. Nevertheless, I'll paste the JSP code as well:
<form:form action="${s:mvcUrl('CCC#gravar').build() }" method="post" commandName="pessoa" enctype="multipart/form-data" >
<div class="form-group" >
<label>Conta</label>
<select name="tipoConta">
<c:forEach items="${model.tipoConta}" var="tipoConta">
<option value=${tipoConta}>${tipoConta}</option>
</c:forEach>
</select>
</div>
<div class="form-group" >
<label>Risco</label>
<select name="tipoRisco">
<c:forEach items="${model.tipoRisco}" var="tipoRisco">
<option value=${tipoRisco}>${tipoRisco}</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<label>Login</label>
<form:input path="login" cssClass="form-control" />
</div>
<div class="form-group">
<label>Senha</label>
<form:input path="senha" cssClass="form-control" />
</div>
<div class="form-group">
<label>Valor Atual</label>
<form:input path="valorAtual" cssClass="form-control" />
</div>
<button type="submit" class="btn btn-primary">Cadastrar</button>
</form:form>
When I try to open the website (..../pessoa/pessoacorretora/6) - 6 is a valid ID, this is the error:
HTTP Status 500 - java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'pessoa' available as request attribute
type Exception report
message java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'pessoa' available as request attribute
description The server encountered an internal error that prevented it from fulfilling this request.
Actually I really don't know how to handle with a relationship of two classes when I want to do CRUD actions.
The problem is that spring can't bind the commandName="pessoa" in your JSP form.
Are you sure that the value of Pessoa returnd by :
Pessoa pessoa = pessoaDao.find(id); // is not Null ??
try this as a test :
model.put("pessoa", pessoa == null ? new Pessoa() : pessoa );
another way to solve this is to add it through Model Attribute annotation on you controller:
#ModelAttribute("pessoa ")
public Pessoa defaultInstance() {
return new Pessoa();
}
I want to create a bank account register related to a specific Person in a OneToMany relationship.
I have the clas Pessoa (Person):
#Entity
public class Pessoa {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int idPessoa;
private String nome;
#OneToMany(mappedBy = "pessoa", targetEntity = ContaCorretora.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<ContaCorretora> contaCorretora;
...and the class ContaCorretora (Bank account):
#Entity
public class ContaCorretora {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int idConta;
private TipoConta tipoConta;
private TipoRisco tipoRisco;
private String login;
private String senha;
private BigDecimal valorAtual;
#ManyToOne
#JoinColumn(name="idPessoa")
private Pessoa pessoa;
I'm using this method in Controller for start the process of registration:
#RequestMapping(value = "pessoacorretora/{id}")
public ModelAndView pessoaCorretora(#PathVariable("id") int id, ContaCorretora contaCorretora ) {
Map<String, Object> model = new HashMap<String, Object>();
Pessoa pessoa = pessoaDao.find(id);
model.put("pessoa", pessoa);
model.put("tipoConta", TipoConta.values());
model.put("tipoRisco", TipoRisco.values());
return new ModelAndView("corretora/contacorretora", "model", model);
}
Sumarizining, I have a specific page for recording bank accounts. So, I created this form:
<form:form action="${s:mvcUrl('CC#gravar').build() }" method="post" commandName="contaCorretora" enctype="multipart/form-data" >
<div class="form-group" >
<label>Conta</label>
<select name="tipoConta">
<c:forEach items="${model.tipoConta}" var="tipoConta">
<option value=${tipoConta}>${tipoConta}</option>
</c:forEach>
</select>
</div>
<div class="form-group" >
<label>Risco</label>
<select name="tipoRisco">
<c:forEach items="${model.tipoRisco}" var="tipoRisco">
<option value=${tipoRisco}>${tipoRisco}</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<label>Login</label>
<form:input path="login" cssClass="form-control" />
</div>
<div class="form-group">
<label>Senha</label>
<form:input path="senha" cssClass="form-control" />
</div>
<div class="form-group">
<label>Valor Atual</label>
<form:input path="valorAtual" cssClass="form-control" />
</div>
<form:hidden path="pessoa" cssClass="form-control" value="${pessoa}"/>
<button type="submit" class="btn btn-primary">Cadastrar</button>
</form:form>
When I use the form in this way, I receive the error "description The request sent by the client was syntactically incorrect." I figured out that the problem is in this line, because when I delete, the form post ok:
<form:hidden path="pessoa" cssClass="form-control" value="${pessoa}"/>
Nevertheless, if I delete this line, the program doesn't save the idPessoa as a foreign key, this field is null. I would like to know how to pass an entire object in my JSP form. The post method is:
#RequestMapping(method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
contaCorretoraDao.gravar(contaCorretora);
return new ModelAndView("pessoa/listageral");
}
all the DAO's methods are okay.
You only need to send the primary key of pessoa to the form.
Change the form attribute
<form:hidden path="pessoa.idPessoa" cssClass="form-control" value="${model.pessoa.idPessoa}"/>
Before persisting the ContaCorretora, make sure you get the Pessoa object from db.
#RequestMapping(method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
contaCorretora.setPessoa(pessoaDao.find(contaCorretora.getPessoa().getIdPessoa()));
//I escaped null check and not found exceptions, you should apply some logic to take care of that
contaCorretoraDao.gravar(contaCorretora);
return new ModelAndView("pessoa/listageral");
}
Using Entities as form model is not a good approach. Persistance layer should not be on the MVC layer.
In order to pass Java object from your Controller
#RequestMapping(method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
ModelAndView mav = new ModelAndView("pessoa/listageral");
// retrieve object from DAO
Object myObj = Dao.find(id);
// Put object into model map
mav.addObject("myObj", myObj);
// return model and view
return mav;
}
In JSP you can refer to object using ExpressionLanguage syntax
${myObj}