Display items into Spring MVC table - spring

I have this Endpoint:
#Controller
public class FileImportsController {
private EntityImportRequestsService entityImportRequestsService;
#Autowired
public FileImportsController(EntityImportRequestsService entityImportRequestsService) {
this.entityImportRequestsService = entityImportRequestsService;
}
#GetMapping("/imported_files")
public String viewHomePage(Model model) {
List<EntityImportRequestsTable> listProducts = entityImportRequestsService.findAll();
System.out.println("Size is " + listProducts.size());
model.addAttribute("listProducts", listProducts);
return "index";
}
}
Entity:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
#Entity
#Table(name = "EntityImportRequests")
public class EntityImportRequestsTable implements Serializable {
#Id
#Column(name = "requestId")
private String requestId;
#Column(name = "transactionGroupId")
private Integer transactionGroupId;
#Column(name = "requestXmlSourceFile")
private String requestXmlSourceFile;
#Column(name = "createdOn")
private LocalDateTime createdOn;
}
Web page index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Files Manager</title>
</head>
<body>
<div align="center">
<h1>Files List</h1>
<table border="1" cellpadding="10">
<tr th:each="item : ${listProducts}">
<td th:text="${item.requestId}"/>
<td th:text="${item.transactionGroupId}"/>
<td th:text="${item.requestXmlSourceFile}"/>
<td th:text="${item.createdOn}"/>
</tr>
</table>
</div>
</body>
</html>
When I open the page data is not displayed. What is the proper way to display the data rows from listProducts?
When I make a call to the rest controller directly I get this output:
Hibernate: select entityimpo0_.requestId as requesti1_0_, entityimpo0_.createdOn as createdo2_0_, entityimpo0_.requestXmlSourceFile as requestx3_0_, entityimpo0_.transactionGroupId as transact4_0_ from integration.EntityImportRequests entityimpo0_
Size is 69
As you can see the list is not empty. I have 69 items.

Try something like this.
Just make sure that your listProducts is not empty in your controller.
<tr th:each="product: ${listProducts}">
<td th:text="${product.requestId}" />
<td th:text="${product.name}" />
<td th:text="${product.price}" />
</tr>
Or whatever fields your product entity has next to the name, price, etc.

Related

Insert multiple rows to MySQL with JPA

I am a newbie to spring boot. I have two entities: course1 and course2 with each having a field code, for course code. I have been able to generate select form fields due to entries on couse1 database table so that input to course2 will be selected. However, whenever I make a selection on the form and post, all the selected course codes will enter a single field on course2 database table instead of each entering a separate row. Hence, my problem is, I want to insert multiple rows into a database table using JPA. Following is what I did and will be grateful if any body helps
Course1 entity:
`
#NoArgsConstructor
#AllArgsConstructor
#Setter
#Getter
#Entity
#Table(name = "course_one")
public class CourseOne {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "course_one_id")
private Long courseOneId;
#Column(name = "course_code")
private String code;
#OneToOne(mappedBy = "courseOne")
private CourseTwo courseTwo;
`
Course1 entity contains more fields describing each course though.
Course2 entity:
`
#NoArgsConstructor
#AllArgsConstructor
#Setter
#Getter
#Entity
#Table(name="course_two")
public class CourseTwo{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "course_two_id")
private Long courseTwoId;
private String code;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "course_one_id")
private CourseOne courseOne;
`
Course2 repository
`
public interface CourseTwoRepository extends CrudRepository<CourseTwo, Long> {
}
`
At the service layer, the method I used to insert into the database table for course2 entity is:
`
public List<CourseTwo> saveCourseTwo(CourseTwo courseTwo) {
return(List<CourseTwo>) courseTwoRepo.saveAll(List.of(courseTwo));
}
`
This is my form:
<form action="#" th:action="#{/saveCourseTwo}" th:object="${courseTwo}" method="post">
<div class="overflow-scroll" style="border: 2px; height:dashed; height:300px; width:800px">
<div class="table-responsive">
<table class="table table-sm table-striped table-hover" style="width: 100%">
<thead class="green white-text">
<tr>
<th>Select</th>
<th>Code</th>
<th>Title</th>
<th>Units</th>
</tr>
</thead>
<tbody>
<tr th:each="select : ${listCourseOne}">
<td><input type="hidden" th:value="${select.courseOneId}" name="courseOne"/></td>
<td><input type="checkbox" class="form-check-input" name="code" th:value="${select.code}" />
<td th:text="${select.code}"></td>
<td th:text="${select.title}"></td>
<td th:text="${select.units}"></td>
</tr>
</tbody>
</table>
</div>
</div>
<button type="submit" class="btn btn-success">Save</button>
</form>
However, the method inserts all the selected course codes into a single field. I will be happy if anyone helps.
I also tried to create wrapper multiple instances of CourseTwo as follows:
List<CourseTwo> lisOfCourse =new ArrayList<>(courseTwo);
List<CourseTwo> courseTwoList = new ArrayList<>();
for(int i=0; i<lisOfCourse.size(); i++){
CourseTwo courseTwo1 = new CourseTwo();
courseTwo1.setCourseTwoId(courseTwo1.courseTwoId());
courseTwo1.setCode(courseTwo1.getCode());
courseTwoList.add(courseTwo1);
}
return (List<CourseTwo>) courseTwoRepo.saveAll(courseTwoList);
However, I get the following error: No primary or single unique constructor found for interface java.util.List

How to work with a many-to-many relationship with extra attributes using Spring Boot, Spring MVC, Spring Data JPA and Thymeleaf

I'm tryingto build an insert, update and delete forms for a many-to-many relationship with one extra attribute, but I'm having some problems. When I use a simple #ManyToMany annotation everything works since Spring MVC and Thyemeleaf automatically take care of everything. Now, I need to have an extra attribute in the join table/entity. This attribute will be managed by the business logic and it doesn't need to be present in the view. I'm implementing my scenario creating manually the join entity rather than using the #Embedded annotation.
What I have so far will be shown below. Note that I'm omitting the import statements and I'm reproducing a very simpler version that I actually have.
Entities
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode( onlyExplicitlyIncluded = true )
public class Race {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
#EqualsAndHashCode.Include
private Long id;
#NotNull
#NotEmpty
private String name;
#OneToMany( mappedBy = "race", cascade = CascadeType.ALL, orphanRemoval = true )
private Set<TruckFromRace> trucksFromRace;
}
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode( onlyExplicitlyIncluded = true )
public class Truck {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
#EqualsAndHashCode.Include
private Long id;
#NotNull
#Min( value = 1 )
private Integer number;
#OneToMany( mappedBy = "truck", cascade = CascadeType.ALL, orphanRemoval = true )
private Set<TruckFromRace> trucksFromRace;
}
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode( onlyExplicitlyIncluded = true )
public class TruckFromRace {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
#EqualsAndHashCode.Include
private Long id;
#ManyToOne
#JoinColumn( name = "truck_id" )
#ToString.Exclude
#JsonProperty( access = Access.WRITE_ONLY )
private Truck truck;
#ManyToOne
#JoinColumn( name = "race_id" )
#ToString.Exclude
#JsonProperty( access = Access.WRITE_ONLY )
private Race race;
#NotNull
private Integer currentLap = 0;
}
Repositories
public interface RaceRepository extends CrudRepository<Race, Long> {
}
public interface TruckFromRaceRepository extends CrudRepository<TruckFromRace, Long> {
}
public interface TruckRepository extends CrudRepository<Truck, Long> {
}
Controller
#Controller
#RequestMapping( "/races" )
public class RaceController {
#Autowired
private RaceRepository raceRepo;
#Autowired
private TruckRepository truckRepo;
#Autowired
private TruckFromRaceRepository trucksFromRaceRepo;
#GetMapping( "/list" )
public String listar( Model model ) {
model.addAttribute( "races", raceRepo.findAll() );
return "/forms/races/list";
}
#GetMapping( "/prepareInsert" )
public String prepareInsert( Race race, Model model ) {
model.addAttribute( "trucks", truckRepo.findAll() );
return "/forms/races/insert";
}
#PostMapping( "/insert" )
public String insert( #Valid Race race,
BindingResult result,
Model model,
#RequestParam( required = false ) Long[] trucksFromRace ) {
if ( result.hasErrors() ) {
model.addAttribute( "trucks", truckRepo.findAll() );
return "/forms/races/insert";
}
raceRepo.save( race );
if ( trucksFromRace != null ) {
for ( Long truckId : trucksFromRace ) {
Truck t = truckRepo.findById( truckId ).get();
TruckFromRace tr = new TruckFromRace();
tr.setTruck( t );
tr.setRace( race );
trucksFromRaceRepo.save( tr );
}
}
return "redirect:/races/list";
}
#GetMapping( "/prepareUpdate/{id}" )
public String prepareUpdate( #PathVariable( "id" ) Long id,
Model model ) {
Race race = raceRepo.findById( id )
.orElseThrow( () -> new IllegalArgumentException( "Invalid id:" + id ) );
model.addAttribute( "trucks", truckRepo.findAll() );
model.addAttribute( "race", race );
return "/forms/races/update";
}
#PostMapping( "/update/{id}" )
public String update( #PathVariable( "id" ) Long id,
#Valid Race race,
BindingResult result,
Model model,
#RequestParam( required = false ) Long[] trucksFromRace ) {
if ( result.hasErrors() ) {
model.addAttribute( "trucks", truckRepo.findAll() );
race.setId( id );
return "/forms/races/update";
}
// remove all trucks from this race
// only works if the if above is commented
race.getTrucksFromRace().clear();
raceRepo.save( race );
// try to associate new ones
/*if ( trucksFromRace != null ) {
for ( Long truckId : trucksFromRace ) {
Truck t = truckRepo.findById( truckId ).get();
TruckFromRace tr = new TruckFromRace();
tr.setTruck( t );
tr.setRace( race );
trucksFromRaceRepo.save( tr );
}
}*/
return "redirect:/races/list";
}
}
Templates
forms/races/list.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Races</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"/>
</head>
<body>
<h4>Races</h4>
<table>
<thead>
<tr>
<th>Name</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="race : ${races}">
<td th:text="${race.name}"></td>
<td><a th:href="#{/races/prepareUpdate/{id}(id=${race.id})}">Update</a></td>
<td><a th:href="#{/races/prepareDelete/{id}(id=${race.id})}">Delete</a></td>
</tr>
</tbody>
</table>
New Race
<br/>
Index
</body>
</html>
forms/races/insert.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>New Race</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"/>
</head>
<body>
<h4>New Race</h4>
<form action="#" th:action="#{/races/insert}" th:object="${race}" method="post">
<div>
<label for="name">Name</label>
<input type="text" th:field="*{name}" id="name" placeholder="Name"/>
<small th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></small>
</div>
<h4>Trucks</h4>
<table>
<thead>
<tr>
<th>Number</th>
<th>Will compete</th>
</tr>
</thead>
<tbody>
<tr th:each="truck : ${trucks}">
<td th:text="${truck.number}"></td>
<td>
<input class="form-check-input" th:field="*{trucksFromRace}" type="checkbox" th:value="${truck.id}"/>
</td>
</tr>
</tbody>
</table>
<input type="submit" value="Save"/>
<br/>
Back
</form>
</body>
</html>
forms/races/update.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>New Race</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"/>
</head>
<body>
<h4>Update Race</h4>
<form action="#" th:action="#{/races/update/{id}(id=${race.id})}" th:object="${race}" method="post">
<div>
<label for="name">Name</label>
<input type="text" th:field="*{name}" id="name" placeholder="Name"/>
<small th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></small>
</div>
<h4>Trucks</h4>
<table>
<thead>
<tr>
<th>Number</th>
<th>Will compete</th>
</tr>
</thead>
<tbody>
<tr th:each="truck : ${trucks}">
<td th:text="${truck.number}"></td>
<td>
<input class="form-check-input" th:field="*{trucksFromRace}" type="checkbox" th:value="${truck.id}"/>
</td>
</tr>
</tbody>
</table>
<input type="submit" value="Save"/>
<br/>
Back
</form>
</body>
</html>
My insertion is working fine, althoug I think that the controller code could be simpler, but I have two problems with the update operation. I'm omitting the delete operation because it will be based in the update operation.
When I try to update a race, clicking in the update link in the list of races, the form is being loaded erroneously. Let me explain. Firstly I save a new race and it works as expected:
But, when I try to update it, I have this erroneous result in my view:
So, I would like to know what I need to do to solve this.
My second problem is related to the update method in the RaceController class. What I'm experiencing is described in some comments inside the code. I'm pretty rusty with JPA... My last time doing these kinds of implementations in real scenarios was more than 10 years ago.
Thanks!
Many to many link table only take care of two table in many to many , it may not handle extra attribute.
In my way the possible solution is to create/entity
Which hold two tables and one attribute.
Previous:
Table1 and Table2 in m2m relationship.
Solution
Table 3 has one to many relationship with table 1 and table 2 , also table 3 has an other extra attribute.

Getting all the time: Invalid property of bean class: Bean property is not readable

I always get this error, nonetheless whether I create the getter/setter manually or doing it via Lombok.
So, I guess the error has nothing to do with the Getters/Setters but I cannot find the answer to my problem.
This is my error message:
Invalid property 'person' of bean class [java.util.ArrayList]: Bean property 'person' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
This is my Enttiy class:
#Data
#Getter
#Setter
#Entity
#Table(name="incomeoutgo", schema = "public")
public class IncomeOutgo extends AbstractPersistable<Long> {
#Version
#NotNull
#Column(name ="id")
private Long id;
#Column(name="dayofweek")
private Date dayofweek;
#Column(name="location")
private String location;
#Size(min = 5, max = 50)
#Column(name ="person")
private String person;
#Min(0)
#Column(name ="version")
private Integer version;
#Column(name="income")
private int income;
#Column(name="outgo")
private int outgo;
}
And this is my Controller class
#RequiredArgsConstructor
#Controller
#RequestMapping(value = "/incomeoutgo")
public class IncomeOutgoController {
private static final String INCOMEOUTGO_VIEW = "incomeoutgo";
private final IncomOutgoService incomeoutgoService;
#GetMapping
public String showShop(Model model) {
List<IncomeOutgo> incomeOutgoList = incomeoutgoService.getIncomeOutgoList();
model.addAttribute(INCOMEOUTGO_VIEW, incomeOutgoList);
return INCOMEOUTGO_VIEW;
}
#PostMapping("/incomeoutgo")
public String addUser(#ModelAttribute("incomeoutgo") IncomeOutgo incomeoutgo, BindingResult bindingResult) {
incomeoutgoService.addIncomeOutgo(incomeoutgo);
return "incomeoutgo";
}
}
And last but not least my Thymeleaf template:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Incomes / Expenses</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<table>
<tr>
<th>Id</th>
<th>Day of Week</th>
<th>Name</th>
<th>Location</th>
<th>Income</th>
<th>Expense</th>
</tr>
<tr th:each="incomeoutgo : ${incomeoutgo}">
<td th:text="${incomeoutgo.id}">id</td>
<td th:text="${incomeoutgo.dayofweek}">dayofweek</td>
<td th:text="${incomeoutgo.person}">person</td>
<td th:text="${incomeoutgo.location}">location</td>
<td th:text="${#numbers.formatCurrency(incomeoutgo.income)}">"${#numbers.formatCurrency(incomeoutgo.income)}"</td>
<td th:text="${#numbers.formatCurrency(incomeoutgo.outgo)}">"${#numbers.formatCurrency(incomeoutgo.outgo)}"</td>
</tr>
</table>
<form action="#" th:action="#{/incomeoutgo}" th:object="${incomeoutgo}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{person}" /></td>
<td th:if="${#fields.hasErrors('person')}" th:errors="*{person}">Name Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
The error is explained: Invalid property 'person' of bean class [java.util.ArrayList]. You're trying to call getPerson() on a java.util.ArrayList (which doesn't have that property). I suspect it's in your form:
<form ... th:object="${incomeoutgo}">
You've added incomeoutgo to your model as a List<IncomeOutgo>, but you're trying to treat it as an IncomeOutgo instead. You need to create a single object and use that instead when trying to bind form field values.
model.addAttribute(INCOMEOUTGO_VIEW, incomeOutgoList);
model.addAttribute("page", new IncomeOutgo());
.
.
.
<form ... th:object="${page}">
<input type="text" th:field="*{person}" />
(Side node, reusing the same variable name multiple times makes the code confusing. You have the model attribute ${incomeoutgo}, the temporary loop variable th:each="incomeoutgo : ${incomeoutgo}" and the bound thymeleaf object th:object="${incomeoutgo}" which may or may not be related.)

How do I bind a table in a form to a HashSet in Spring Boot?

I am not quite sure how to approach this problem. I have a table rendered by datatables populated with product information. I am trying to figure out a form where I can insert the quantity and discount values and then save either the selected fields or fields where the quantity > 0. Then bind the table data to a set or a list.
However, I am not quite sure how to write the bindings. I have read over this question here but it didn't seem quite the same in that I don't think I could use an index as there could a large amount of products and therefore potentially a large amount of nulls in the response. Any help would be appreciated.
Product.java
#Entity
#Table(name="products")
public class Product {
#Getter
#Setter
#Id
private Long productId;
#Getter
#Setter
private Long productName;
}
QuoteForm.java
public class QuoteForm {
#Getter
#Setter
private Long quoteId;
#Getter
#Setter
private Long contactId;
#Getter
#Setter
private Set<ProductEntry> products;
}
ProductEntry.java
public class ProductEntry {
#Getter
#Setter
private TempProduct product;
#Getter
#Setter
private Long quantity;
#Getter
#Setter
private float discount;
}
form.html
<form id="productTable" th:object="${quoteForm}">
<input type="number" th:field="*{contactId}"></input>
<table id ="productList"
class="table table-striped table-bordered"
cellspacing="0"
width="100%">
<thead>
<tr>
<th>Name</th>
<th>Quantity</th>
<th>Discount</th>
</tr>
</thead>
<tbody>
<tr th:each="product : ${productData.getProducts()}">
<td name="?">
<a th:text="${product.getProductName()}"
th:href="#{|/product/records/${product.getProductId()}|}">
</a>
</td>
<td>
<input type="number"
name="?"
th:field="*{products.quantity}">
</input>
</td>
<td>
<input type="number" name="?" th:field="*{products.discount}"></input>
</td>
</tr>
</tbody>
You can do binding using "model.addAttribute()". Something like this in your controller:
#RequestMapping("/example")
public String formParse(Model model) {
//some logic
Set<ProductEntry> productList = //code to get();
model.addAttribute("productList", productList);
//some more logic
return "yourView";
}
Here "productList" is the name which you have assigned to "?".
And as for checking the quantity you can do that in the form itself, with some thing like:
<div th:if="*{products.quantity>0}" th:field="*{products.quantity}"></div>
Here &gt represents "greater than".
Hope this helps!

Thymeleaf form validation with spring MVC

This has been asked a few times but all of them did not answer my question. I have been trying to get different functionalities to work with Thymeleaf for two days now and been very unsuccessful. I have only been able to get it to work with spring-boot, however right now I am using spring-MVC.
First I will show you my dependencies
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Create a New Account</title>
<link th:href="#{/resources/css/loginForm.css}" href="/resources/css/loginForm.css" rel="stylesheet"
type="text/css"/>
</head>
<body>
<form action="#" th:action="#{/createNewAccount}" th:object="${user}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{username}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{username}">Name Error</td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" th:field="*{password}" /></td>
<td th:if="${#fields.hasErrors('age')}" th:errors="*{password}">Password Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
Now so you can see the errors my intellij IDE is showing:
User.java
package com.practice.domain;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* Created by DrewJocham on 8/30/15.
*/
public class User {
#NotNull
#Size(min = 2, max = 30)
private String username;
#NotNull
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
The field name you call hasErrors with needs to match the field name in the object. Like so:
<td th:if="${#fields.hasErrors('username')}" th:errors="*{username}">Name Error</td>
Note that hasErrors('name') became hasErrors('username'), and:
<td th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Password Error</td>
Note that hasErrors('age') became hasErrors('password').
As for the errors being highlighted in Intellij, I think they're misleading, and are related to this open issue: https://youtrack.jetbrains.com/issue/IDEA-132738

Resources