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
Related
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.
I have populated a dropdown in JSP from controller by a list using following:
'''
<form:select path="thirdPartyOccupationId" id="thirdPartyOccupationId" class="form-control input-sm">
<c:forEach var="thirdPartyProfession" items="${professionsList}">
<form:option value="${thirdPartyProfession.professionId}" label="${thirdPartyProfession.profession}" />
</c:forEach>
</form:select>
'''
thirdPartyOccupationId is from entity class. I have saved the selected value from this dropdown in database. Now when I reload the page and I don't see the value of thirdPartyOccupationId as selected. The dropdown just shows the list of the values in ascending order.
For example, I saved 5 as the value of thirdPartyOccupationId in db using the dropdown. When I reload the page the value 5 is not the selected value.
Same piece of code is working with a different field, I don't know what I am missing.
Model Classes:
ReportClass.java
#JoinColumn(name = "third_party_occupation_id", referencedColumnName = "profession_id")
#ManyToOne
private Professions thirdPartyOccupationId;
Professions.java
#Id
#Basic(optional = false)
#Column(name = "profession_id")
private String professionId;
#Column(name = "profession")
private String profession;
#OneToMany(mappedBy = "thirdPartyOccupationId")
private Collection<ReportClass> reportClassCollection;
Controller.java
reportClass.setThirdPartyOccupationId(this.serviceManager.getProfessionsService().getByKey("123"));
model.addAttribute("reportClass", reportClass);
List<Professions> professionsList = serviceManager.getProfessionsService().findAll();
model.addAttribute("professionsList", professionsList);
Appreciate any pointers.
As an workaround, I created a new row above to the <c:forEach> loop where intended to see the selected attribute, i.e.
<tr>
<td>
<c:if test="${tipFe !=null}">${tipFe}</c:if>
<c:if test="${tipFe eq null}">-</c:if>
</td>
</tr>
<tr>
<td>
<c:forEach var="ltipfe" items="${ltipfe}">
<option value=${ltipfe.tipfe}>${ltipfe.tipfe}</option>
</c:forEach>
</td>
</tr>
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.)
I'm new to Spring, and I'm working on a Spring MVC + Thymeleaf application that tracks personal expenses. I have a page that shows the list of expenses, and upon clicking "Update" on any given expense, it would show a pre-populated form, on another page, with that expense's information. The problem I'm having is that I want to pass that expense object forward to this second page, but since the data was already fetched from the database in my first page, I wouldn't want to fetch it again from my Spring Data JPA repository.
I think the unusual part of my objective is that I'm trying to pass the object like this:
Controller -> Thymeleaf -> Controller -> Thymeleaf
While the controller has sent an Expenses object to the list of expenses page, I'm trying to resend that same object (or just the one Expense, even better) back to the controller, so it would populate the model in my second form page.
My Expense entity:
package com.williampoletto.expensetracker.entity;
import java.time.LocalDate;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#EqualsAndHashCode(of= "id")
#ToString(exclude="categories")
#Entity
#Table
public class Expense {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY )
private int id;
#NotBlank
private String description;
#NotNull
private double value;
#NotNull
private LocalDate date;
private String note;
#ManyToOne
private User user;
#ManyToMany(cascade=CascadeType.PERSIST)
#JoinTable(
name="expense_category",
joinColumns=#JoinColumn(name="expense_id"),
inverseJoinColumns=#JoinColumn(name="category_id"))
private Set<Category> categories;
public void addCategory(Category category) {
categories.add(category);
}
public void removeCategory(Category category) {
categories.remove(category);
}
public Expense(int id, #NotBlank String description, #NotNull double value, #NotNull LocalDate date,
String note, Set<Category> categories) {
super();
this.id = id;
this.description = description;
this.value = value;
this.date = date;
this.note = note;
this.categories = categories;
}
}
My thymeleaf table:
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Description</th>
<th>Value</th>
<th>Category</th>
<th>Note</th>
<th>Date</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="expense : ${expenses}">
<td th:text="${expense.description}"/>
<td th:text="${expense.value}"/>
<td>
<div th:each="category : ${expense.categories}">
<p th:text ="${category.name}"></p>
</div>
</td>
<td th:text="${expense.note}"/>
<td th:text="${expense.date}"/>
<td>
<form th:action="#{/expenses/showUpdateForm}" th:object="${expense}" method="POST">
<input type="text" name="expense" th:value="${expense.id}">
<input type="text" name="expenseDescription" th:value="${expense.description}">
<input type="text" name="expenseValue" th:value="${expense.value}">
<input type="text" name="expenseDate" th:value="${expense.date}">
<input type="text" name="expenseNote" th:value="${expense.note}">
<input type="text" name="expenseCategories" th:value="${expense.categories}">
<button th:if="${expense.user != null}" type="submit" class="btn btn-info btn-sm">Update</button>
</form>
<a th:href="#{/expenses/delete(expenseId=${expense.id})}"
class="btn btn-danger btn-sm"
onclick="if (!(confirm('Are you sure you want to delete this expense?'))) return false">Delete</a>
</td>
</tbody>
</table>
My /list and /showUpdateForm code for the controller:
#GetMapping("/list")
public String list(#AuthenticationPrincipal UserDetailsImpl userDetails, Model model) {
Set<Expense> expenses = expenseService.findAll(userDetails.getUserId());
model.addAttribute("expenses", expenses);
return "expenses";
}
#PostMapping("/showUpdateForm")
public String showFormForUpdate(#RequestParam("expenseId") int expenseId,
#RequestParam("expenseDescription") String expenseDescription,
#RequestParam("expenseValue") double expenseValue,
#RequestParam("expenseDate") String expenseDate,
#RequestParam("expenseNote") String expenseNote,
#RequestParam("expenseCategories") Set<Category> expenseCategories,
Model model) {
Expense expense = new Expense
(expenseId, expenseDescription, expenseValue, expenseDate, expenseNote, expenseCategories);
model.addAttribute("expense", expense);
return "/expense-form";
}
My final form page:
<form th:action="#{/expenses/save}" th:object="${expense}" method="POST">
<input type="hidden" th:field="*{id}">
<label>Description</label>
<input type="text" th:field="*{description}" class="form-control mb-4 col-4" placeholder="Description">
<label>Value</label>
<input type="text" th:field="*{value}" class="form-control mb-4 col-4" placeholder="Value">
<label>Date</label>
<input type="text" th:field="*{date}" class="form-control mb-4 col-4" placeholder="Date">
<label>Note</label>
<input type="text" th:field="*{note}" class="form-control mb-4 col-4" placeholder="Note">
<label>Categories</label>
<input type="hidden"
th:each="category : *{categories}"
th:value="${category.id}"
th:field="*{categories}"
th:text="${category.name}"/>
<button type="submit" class="btn btn-success col-2 mb-2">Save</button>
</form>
What I have tried:
Instead of sending an object, sending individual values as you can see in my thymeleaf table. The problem with this is that my Expense entity has a LocalDate, User (another entity) and Set attributes, so I had trouble converting these to an Expense object on the controller side. Ideally I would want to write something like this to simply pass on the object, but this example sends a toString:
<form th:action="#{/expenses/showUpdateForm}" th:object="${expense}" method="POST">
<input type="text" name="expense" th:value="${expense}">
</form>
Tried to use an argument to /showUpdateForm to hopefully get the expenses object from the model:
#RequestAttribute("expenses") Set<Expense> expenses
Tried to retrieve the object like this in /showUpdateForm, with the same intent as 2:
Set<Expense> expenses = (Set<Expense>) model.getAttribute("expenses");
Tried to use RedirectAttributes in the controller, which I saw can be useful for passing objects between controllers, but maybe not in my case:
#GetMapping("/showUpdateForm")
public String showFormForUpdate(Model model, RedirectAttributes attributes) {
attributes.addAttribute("expenses");
return "/expense-form";
}
Anyway, I have no idea how to achieve this, I would appreciate any light on the subject! I know that I can easily fix this by simply sending an id from view to controller, then I could perform a repository search of the object with that ID, but that would be cumbersome to the database since the data already exists and was fetched in the previous page.
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 > represents "greater than".
Hope this helps!