Controller for #OneToMany relationship using Spring Boot - java

I'm trying to figure out with Spring Boot and databases.
So I have 2 entities with #OneToMany relationship:
#Entity
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int teamId;
#Column
private String teamTitle;
#Column
private String teamCity;
#ManyToOne
#JoinColumn(name = "conferenceId", nullable = false)
private Conference teamConference;
public Team() { super(); }
//some getters and setters
}
And the second one:
#Entity
public class Conference {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int conferenceId;
private String conferenceTitle;
#OneToMany(mappedBy = "teamId")
private List<Team> conferenceTeams;
public Conference() {
super();
}
//some getters and setters
}
Jsp page:
<body>
<form:form method="post" modelAttribute="team">
<div>
<form:label path="teamTitle">Title</form:label>
<form:input path="teamTitle" type="text"/>
<form:label path="teamCity">City</form:label>
<form:input path="teamCity" type="text"/>
//DAHELL IS HERE
<div class="form-group">
<label for="conferenceList">Select conference:</label>
<select class="form-control" id="conferenceList">
<c:forEach items="${conference}" var="conf">
<option>${conf.conferenceTitle}</option>
</c:forEach>
</select>
</div>
<button type="submit" class="btn btn-success">Add</button>
</div>
</form:form>
// jquery etc
</body>
And controller class:
#Controller
public class TeamsController {
#Autowired
private TeamDAO teamDAO;
#Autowired
private ConferenceDAO conferenceDAO;
#RequestMapping(value = "/schedule", method = RequestMethod.GET)
public String showSchedule(ModelMap model) {
model.put("conferences", conferenceDAO.findAll());
model.put("teams", teamDAO.findAll());
return "schedule";
}
#RequestMapping(value = "/new-team", method = RequestMethod.GET)
public String addNewTeam(ModelMap model) {
model.addAttribute("conference", conferenceDAO.findAll());
model.addAttribute("team", new Team());
return "new-team";
}
#RequestMapping(value = "/new-team", method = RequestMethod.POST)
public String addTeam(ModelMap model, Team newTeam) {
teamDAO.save(newTeam);
return "redirect:/schedule";
}
}
ConferenceDAO and TeamDAO are just interfaces extended from JpaRepository.
So what I'm trying to understand is how to add new Team. I insert title and city through jsp page and also I should choose which conference this team belongs. But when I press add button I got
There was an unexpected error (type=Internal Server Error, status=500).
No message available
What am I doing wrong? I believe that something with selecting part in jsp page. And I'm 100% sure I'm missing something in my Controller class. Somehow I should save new team to my DB and the Conference column also should show that it contains this new team.
I'd really appreciate guys if you show me the way to dig up.

So yes, comments really helped. So for those who read it - sleep on the problem and read logs before ask dumb questions (as I did :D )
The problem was within jsp page and selection form. I was getting Null for conferenceId and I would know it if I'd read logs.
So comments about checking whole stack trace and changing jsp selection part - worked for me.
Fixed jsp selection is:
<div class="form-group">
<label for="conferenceList">Select conference:</label>
<form:select path="teamConference" id="conferenceList">
<form:options items="${conference}" itemLabel="conferenceTitle"/>
</form:select>
</div>
Thx guys!

Related

Why got Invalid property 'district' of bean class

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;
}

The server encountered an internal error that prevented it from fulfilling this request

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();
}

Send object in JSP as foreign key using Spring MVC and Hibernate

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}

How to bind an object list to checkbox in thymeleaf?

I am having a lot of difficulty with binding checkbox inputs with a object list.Issue is when I add th:field="*{userRole}" on input field type checkbox then I am getting Bad request as response on web browser.
Here is my code:
User Model class:
public class User implements Serializable {
private Integer id;
private String username;
private String password;
private boolean enabled;
private List<UserRole> userRole = new ArrayList<UserRole>(0);
}
UserRole Model class:
public class UserRole implements Serializable {
#Id
#GeneratedValue
private Integer userRoleId;
#ManyToMany(mappedBy = "userRole")
private List<User> users;
#Column(name = "role", nullable = false, length = 45)
private String role;
}
Model Attribute:
#ModelAttribute(value = "user")
public User userModel() {
return new User();
}
GET Method:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Map<String, Object> model) {
List<UserRole> roles = roleService.getAllRoles();
model.put("roles", roles);
return "index";
}
My POST method:
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addUser(#ModelAttribute("user") User userModel) {
}
Index page Form :
<form role="form" action="#" th:action="#{/add}" th:object="${user}" method="post">
<div class="form-group">
<label for="email">User Name:</label>
<input type="text" class="form-control" id="username" th:field="*{username}" required autofocus>
</div>
<ul>
<li th:each="item : ${roles}">
<input type="checkbox" th:field="*{userRole}" th:value="${item}" />
<label th:text="${item.role}">Test</label>
</li>
</ul>
<input type="submit" value="Submit" />
</form>
when I click submit, browser shows as a Bad request, But without th:field="*{userRole}" I can submit the form. Any idea to resolve this issue?
---Update---
issue was Thymeleaf unable to bind object directly. So added new List of String for binding.
private List<String> userRoles = new ArrayList<>(0);
and then changed the form as #Roel mentioned.
<li th:each="item, stat : ${roles}">
<label th:text="${stat.index}">Test</label>
<input type="checkbox" th:field="*{userRoles[__${stat.index}__]}" th:value="${item.userRoleId}" />
<label th:text="${item.role}">Test</label>
</li>
Thanks.
You are trying to add item as the value to a List. It's like trying to say ArrayList<Integer> list = 5;
What you need is to add the item to the list:
li th:each="item, stat : ${roles}">
<input type="checkbox" th:field="*{userRole[__${stat.index}__]}" th:value="${item}" />
<label th:text="${item.role}">Test</label>
</li>
I'm unsure if this will directly solve your problem as thymeleaf has issues with simply "adding" an object to a list. Thymeleaf generally works with basic types like strings and integers. But at least I pointed out where your problem lies. Try fiffling around with this a little bit. Try using this line instead, at least this will definitely work:
<input type="checkbox" th:field="*{userRole[__${stat.index}__].role}" th:value="${item.role}" />

Spring web mvc Collection in entity class

Lets say i have 2 entities, Dish and Ingrediƫnt.
As we all know a Dish consists out of multiple Ingredients, so let's say:
Dish.java:
#Entity
public class Dish {
#Id
#GeneratedValue
protected long id;
private String name;
#OneToMany(mappedBy = "dish", cascade = CascadeType.PERSIST)
private Collection<Ingredient> ingredients;
//getters & setters
}
Ingrediƫnt.java :
#Entity
public class Ingredient {
#Id
#GeneratedValue
protected long id;
private String name;
//getters &setters
}
If we then have a DishController.java :
#Controller
public class DishController {
Service service;
public DishController() throws ServiceException {
service = new ShoppingFacade("JPA");
}
#RequestMapping("/showDishOverview")
protected ModelAndView getDishes() throws ServiceException {
Collection<Dish> dishes = service.getAllDishes();
return new ModelAndView("dish/dishOverview", "dishes", dishes);
}
#RequestMapping(value = "/showDishForm", method = RequestMethod.GET)
protected ModelAndView showDishForm(#RequestParam(value = "id") long dishId)throws ServiceException{
Dish dish = shoppingFacade.getDishById(dishId);
return new ModelAndView("dish/dishForm", "dish", dish);
}
#RequestMapping(value = "/editDish", method = RequestMethod.POST)
protected String updateDish(#ModelAttribute("dish") Dish newDish) throws ServiceException{
service.updateDish(newDish);
return "forward:/showDishOverview.htm";
}
}
Now lets say that we select one dish by its id:
${dish.getName()}
Now the showDishForm method in our controller will be called.
which will send us to dishForm.jsp:
<form method="post" action="editDish.htm">
<table>
<tr>
<td><label>Name </label></td>
<td><input type="text" name="name" placeholder="${dish.getName()}" value="${dish.getName()}"/></td>
</tr>
<tr>
<td><label>People</label></td>
<td><input type="text" name="people" placeholder="${dish.getPeople()}" value="${dish.getPeople()}"/></td>
</tr>
<tr>
<td colspan="2">
<input type="hidden" name="ingredients" value="${dish.getIngredient()}" />
<button type="submit" name="id" value="${dish.getId()}">Save</button>
</td>
</tr>
</table>
</form>
How will Spring MVC pass the info from my view to the controller?
Cause I get it working as long as I don't pass any Collections.(comment out <input type="hidden" name="ingredients" value="${dish.getIngredient()}" />
)
If i try to pass a Collection i get the message :
HTTP STATUS 400 - The request sent by the client was syntactically
incorrect
Spring uses Converters, for all standard classes these are already implemented.
For custon classes like this List, you have to write your own converter.

Categories