Spring web mvc Collection in entity class - java

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.

Related

How to post list of objects eg:- List<products> through a table which is inside a form in thymeleaf?

this is my html:
when I am seeing the preview in intelliJ of the same page, it is showing the right page but as soon as I run my program the and click on addInvoice button it is just showing the fields which are above the table only ....the table inputs are not showing in browser.The problem is inside<tr th:each product...>
<input type="hidden" th:field="*{primaryKey}"
placeholder="primaryKey" class="form-control mb-4 col-4">
<input type="text" th:field="*{invoiceNo}"
placeholder="invoiceNo" class="form-control mb-4 col-4">
....
....
<table border="1" class="table table-striped table-responsive-md">
<thead>
<tr>
<th>SNo</th>
<th>Product</th>
<th>Description</th>
<th>Price</th>
<th>Qty</th>
<th>Tax%</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr th:each="product,listOfProductsStat:*{listOfProducts}">
<td th:text="${listOfProductsStat.count}">1</td>
<td>
<input type="text" th:field="*{listOfProducts[__${listOfProductsStat.index}__].productName}">
</td>
<td>
<textarea class="form-control rounded-0" name="description" id="description" rows="3"
maxlength="500"
placeholder="Description"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].description}"></textarea>
</td>
<td>
<input type="number" name="price" id="price" class="form-control"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].price}" placeholder="Price">
</td>
<td>
<input type="number" name="qty" class="form-control" id="qty" placeholder="Qty"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].qty}">
</td>
<td>
<input type="number" name="taxPercent" class="form-control" id="taxPercent" placeholder="Tax(%)"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].taxPercent}">
</td>
<td>
<input type="number" name="total" class="form-control" id="total" placeholder="Total"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].total}">
</tr>
</tbody>
</table>
this is my entities:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class InvoiceInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long primaryKey;
// private int SNo;
private String invoiceNo;
private String billTo;
private String status;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date invoiceDate;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dueDate;
private double grandTotal;
#OneToMany(mappedBy = "invoiceInfo",cascade = CascadeType.ALL)
private List<Product> listOfProducts=new ArrayList<>();
}
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
private String productName;
private String description;
private double price;
private int qty;
private int taxPercent;
private double total;
// private double subTotal;
// private int totalTax;
// private String invoiceNote;
#ManyToOne
#JoinColumn(name = "invoice_info_primary_key")
private InvoiceInfo invoiceInfo;
this is my controller:
#Controller
public class InvoiceInfoController {
#Autowired
private InvoiceInfoService invoiceInfoService;
#GetMapping("")
public String InvoiceInfoListPage(Model model) {
model.addAttribute("InvoiceInfoList", invoiceInfoService.getAllInvoiceInfo());
model.addAttribute("products",invoiceInfoService.getAllProducts());
return "Home";
}
#GetMapping("/openNewInvoiceInfoForm")
public String openNewInvoiceInfoForm(Model model) {
InvoiceInfo invoiceInfo = new InvoiceInfo();
model.addAttribute("invoiceInfo", invoiceInfo);
return "new_invoiceInfo";
}
#PostMapping("saveInvoice")
public String saveInvoiceInfo(#ModelAttribute("invoiceInfo") InvoiceInfo invoiceInfo) {
invoiceInfoService.saveInvoiceInfo(invoiceInfo);
return "redirect:/";
}
#GetMapping("/editInvoiceForm/{primaryKey}")
public String editInvoiceForm(#PathVariable(value = "primaryKey") long primaryKey , Model model) {
InvoiceInfo invoiceInfo= invoiceInfoService.getInvoiceByPrimaryKey(primaryKey);
model.addAttribute("invoiceInfo",invoiceInfo);
return "update_invoiceInfo";
}
#GetMapping("/deleteInvoiceByPrimaryKey/{primaryKey}")
public String deleteInvoiceByPrimaryKey(#PathVariable(value = "primaryKey") long primaryKey){
this.invoiceInfoService.deleteInvoiceByPrimaryKey(primaryKey);
return "redirect:/";
}
}
the problem:
<tr th:each="product,listOfProductsStat:*{listOfProducts}">
my code is not even showing the <td>enter image description here tags in browser ..
How should I associate the listOfProducts instance in InvoiceInfo class with the form inputs and save it in Product table one by one?
Please help!

Controller for #OneToMany relationship using Spring Boot

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!

Save input field in Thymeleaf to Set<>

I would like to save the inputs to the Set interface. I have class Client.java:
#Table(name = "client")
public class Client {
#OneToOne(cascade = CascadeType.ALL)
private ShippingAddress shippingAddress;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Address> shippingAddresses = new HashSet<>();
}
Class ShippingAddress.java:
#Table(name = "address")
public class ShippingAddress {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
Long id;
String street;
String zip;
}
And it's my create form:
<form th:action="#{/add}" method="post" th:object="${client}">
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress.street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress.zip}"/></td>
</tr>
It works fine, but I can only save one street and one zip. I tried to improve it in this way to be able to save more data:
<form th:action="#{/add}" method="post" th:object="${client}">
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress[0].street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress[0].zip}"/></td>
</tr>
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress[1].street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress[1].zip}"/></td>
</tr>
But I get information:
Invalid property 'shippingAddress[0]' of bean class [model.Client]: Property referenced in indexed property path 'shippingAddress[0]' is neither an array nor a List nor a Set nor a Map; returned value was [ShippingAddress(id=null, street=null, zip=null, state=null, city=null, country=null)]
To add values to Set I should use the add method? But how to implement it with Thymeleaf?
Method in Controller (save data):
#Transactional
#RequestMapping(value = "add", method = RequestMethod.POST)
public String saveClient(#ModelAttribute Client client) {
clientRepository.save(client);
return "redirect:/";
}
Method in Controller (open create form)
#RequestMapping("/create")
public String newClient(Model model) {
model.addAttribute("client", new Client());
return "create";
}

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}

Java Spring: how to validate only specific fields from my entity

I'm new in Spring and I have a problem with form validation. In my user edit form I want to validate only specific fields, not all fields annotated in my entity.
For example, if I have my UserEntity with fields:
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#Column(name = "user_id")
#GeneratedValue
public int user_id;
#NotEmpty
#Column(name = "userlogin")
public String userlogin;
#NotEmpty
#Column(name = "userpass")
public String userpass;
#NotEmpty
#Column(name = "name")
public String name;
#Email
#NotEmpty
#Column(name = "email")
public String email;
#NumberFormat(style = Style.NUMBER)
#NotEmpty
#Column(name = "phone")
public String phone;
and when I have register form, I need to validate all fields, and that's working fine.
But when I have edit user form, I want to edit and validate only 'name', 'email' and 'phone', I don't want to change 'userlogin' and 'userpass'.
But edit form won't pass successfully, because BindingResult validating all fields.
Here's my edit form:
<springForm:form action="/mywebapp/user/edit" commandName="user" method="POST">
<table>
<tr>
<td>Name:</td>
<td><springForm:input path="name" value="${user.name}" /></td>
<td><springForm:errors path="name" cssClass="error" /></td>
</tr>
<tr>
<td>E-mail:</td>
<td><springForm:input path="email" value="${user.email }" /></td>
<td><springForm:errors path="email" cssClass="error" /></td>
</tr>
<tr>
<td>Phone:</td>
<td><springForm:input path="phone" value="${user.phone}" /></td>
<td><springForm:errors path="phone" cssClass="error" /></td>
</tr>
<tr>
<td><input type="submit" value="Edit" /></td>
</tr>
</table>
</springForm:form>
Here is my controller method:
#RequestMapping(value="user/edit", method=RequestMethod.POST)
public String doUserEdit(#Valid #ModelAttribute("user") UserEntity user, BindingResult result, Principal principal) {
if(result.hasErrors()) {
return "user/edit";
}
UserEntity u = this.userService.getUser(principal.getName());
this.userService.editUser(u.getUser_id(), user);
return "redirect:/user";
}
result.hasError() always return true, because it validating also 'userlogin' and 'userpass'.
How to ignore other fields in edit form and validate only that fields that I want to?
I usually create a separate form class which is only suited for form submission processing and put all the necessary validation there:
public class UserUpdateForm {
#NotEmpty
private String name;
#Email
private String email;
#NotEmpty
#NumberFormat(style = Style.NUMBER)
private String phone;
//Getters and setters here
}
The idea is that you untie your model class from representations (form) classes. The only downside to that is that you'll have to handle transformations between the form objects and model objects somehow. Something like dozer might help though.

Categories