How to display an object that contains another object in thymeleaf - spring

Hi I struggling to build a proper view in case, when I want to display the product entity, which is in a many-to-many relation with the category. The problem starts with the nested category. I have an error: Exception evaluating SpringEL expression: "category.name" (productList: 31) I will be very grateful for your help in solving this problem
View:
<tbody>
<tr data-th-each="product : ${products}">
<!--<td><input hidden="hidden" name="id" th:value="${product.id}" /></td>-->
<td th:text="${product.name}"></td>
<td th:text="${product.price}"></td>
<td th:each="category : ${product.categories}"></td>
<td th:text="${category.name}"></td>
<td th:text="${product.description}"></td>
<td th:text="${product.shippingWeight}"></td>
<td th:text="${product.quantity}"></td>
<td>delete</td>
</tr>
</tbody>
Entities:
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "products")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_id")
private Long id;
private String name;
private BigDecimal price;
#ManyToMany(cascade = CascadeType.ALL,mappedBy = "products")
private List<Category> categories=new ArrayList<>();
private double shippingWeight;
private boolean isAvailable;
private String description;
private int quantity;
#Transient
private MultipartFile image;
Category:
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "categories")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "category_id")
private Long id;
private String name;
private String description;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "join_category_product", joinColumns = {#JoinColumn(name = "category_id", referencedColumnName = "category_id")},
inverseJoinColumns = {#JoinColumn(name = "product_id", referencedColumnName = "product_id")})
private List<Product> products=new ArrayList<>();
#Transient
private MultipartFile image;
Controller:
#GetMapping("/productList")
public String productList(Model model) {
List<Product> product = productService.getProducts();
model.addAttribute("products",product);
return "productList";

You need to make sure that the Thymeleaf can understand the scope of your variables. In your post, you have category.name outside of the for loop. If categories is a property of product, you can do something like:
<tr th:each="product : ${products}">
<td th:text="${product.name}"></td>
<td th:text="${product.price}"></td>
<td>
<th:block th:each="category : ${product.categories}">
<th:block th:text="${category.name}">[category name]</th:block>
<br>
</th:block>
</td>
<td th:text="${product.description}"></td>
<td th:text="${product.shippingWeight}"></td>
<td th:text="${product.quantity}"></td>
<td>delete</td>
</tr>
If a product doesn't have a category, note that the above will still create a <td>. This is likely what you want.
Aside: it would also make sense to supply default values between your tags. This way, you can open up the file directly in a browser (without a container) and still see how it would lay out. This is a huge reason to use Thymeleaf. I included this with [category name] example above.
Otherwise, you can do the shorter version:
<td>[[${product.name}]]</td>

Related

How to output many to many in Java Spring boot JPA?

I have 2 entities Board and Tag. They have a many to many relationship joined by the table board_tag_table. When I add the tags to the board, it saves the correct pair id to the join table. However when I try to output the Board using Thymeleaf my tags show up as blank.
#Table(name="board_table")
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
#ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinTable(name = "board_tag_table",
joinColumns = {
//primary key of Board
#JoinColumn(name = "id", referencedColumnName = "id")
},
inverseJoinColumns = {
//primary key of Tag
#JoinColumn(name = "tag_id", referencedColumnName = "tag_id")
})
private Set<Tag> tags = new HashSet<>();
}
#Table(name="tag_table")
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer tag_id;
private String tagname;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "tags")
private Set<Board> boards = new HashSet<>();
}
<tr th:each="board : ${list}">
<td th:text="${board.id}"></td>
<td>
<a th:text="${board.title}" th:href="#{/board/view(id=${board.id})}"></a>
</td>
<td th:text="${board.tags}"> </td>
I tried creating a seperate repository and entity for the join table but keep getting an error.

spring boot and thymeleaf

I need help with spring enter image description hereboot and thymeleaf because it doesn't show me the user roles
because it shows me this information and not the roles
this is my database
enter image description here
my controller
#GetMapping("/listar")
public String listar(Model model) {
List<User> listUsers= userService.findAll();
if(listUsers !=null) {
model.addAttribute("titulo", "Lista de usuarios y roles");
model.addAttribute("listUsers", listUsers);
return "user/listar";
}
return "redirect:/escuela/";
}
My HTML
<tr th:each="user: ${listUsers}">
<td th:text="${user.nombre}"></td>
<td th:text="${user.aPaterno}"></td>
<td th:text="${user.aMaterno}"></td>
<td th:text="${user.curp}">]</td>
<td th:text="${user.puesto}"></td>
<td th:text="${user.email}"></td>
<td th:text="${user.roles}"></td>
</tr>
my entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Size(min = 2, max = 20)
#NotBlank
private String nombre;
#Size(min = 2, max = 20)
#NotBlank
private String aPaterno;
#Size(min = 2, max = 20)
#NotBlank
private String aMaterno;
#Size(min = 18, max = 18)
#Column(length = 18, nullable = false, unique = true)
private String curp;
#Size(min = 2, max = 20)
#NotBlank
private String puesto;
#Email
#Column(length = 45, nullable = false, unique = true)
private String email;
#Size(min = 2, max = 20)
#Column(length = 20, nullable = false)
#NotBlank
private String password;
#Valid
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<Role>();
gettters and setters.....
I don't have much spring boot experience, can someone help me
You are getting this because the ${user.roles} is a list, so what's displayed is this list's (and consequently the Role's) default toString() method,
You need to loop in your user.roles and print, for example, the role's name like below:
<tr th:each="user: ${listUsers}">
<td th:text="${user.nombre}"></td>
<td th:text="${user.aPaterno}"></td>
<td th:text="${user.aMaterno}"></td>
<td th:text="${user.curp}">]</td>
<td th:text="${user.puesto}"></td>
<td th:text="${user.email}"></td>
<td>
<table>
<tr th:each="role: ${user.roles}">
<td th:text="${role.name}"></td>
</tr>
</table>
</td>
</tr>
For the sake of complicity you could also override the toString() method in your Role class and let your HTML as is, i.e.
<td th:text="${user.roles}"></td>
Since you haven't posted Role class below is a "guess" of what it might be and the toString() method is overridden to display the name field.
Role class
#Entity
public class Role {
private String name;
// rest of properties
// getters and setters
#Override
public String toString() {
return this.name;
}
}

Haw can i show the details of each with using many to many relationship using spring and thymeleaf

I want to display the details of each user here is my class entity
#Entity
#Table(name="user")
public class User implements Serializable{
#Id #GeneratedValue
private Long id;
private String nom ;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="user_user_detail",joinColumns={
#JoinColumn(name="id")},
inverseJoinColumns={#JoinColumn(name="UserId")})
private Collection<UserDetail>userDetail ;
classe user detail
#Entity
#Table(name="userDetail")
public class UserDetail implements Serializable{
#Id #GeneratedValue
private Long UserId;
private String adresse ;
My controller
#RequestMapping(name="/vue", method = RequestMethod.GET)
public String vue(Model model){
User u=new User();
List<User>p=new ArrayList<User>();
p.add(u);
p=ur.findAll();
model.addAttribute("us",p);
return "user" ;
}
My view thymeleaf
<table border="1">
<tr>
<td>Nom</td>
<td>adresse</td>
</tr>
<tr th:each="t:${us}">
<td th:text="${t.Id}"></td>
<td th:text="${t.nom}"></td>
<td th:text="${t.getUserDetail()}"></td>
</tr>
</table>
i would to show the userdetail with
${t.getUserDetail()}">
but the result of details like this [org.sid.entity.UserDetail#1469f77e, org.sid.entity.UserDetail#78a81727]
please answer me quickly
thank's in advance

Spring MVC, load objects attributes in a jsp

I got an issue when trying to load an object attribute in a jsp file.
The model contains a list of objects of type "Evaluation", for each element in the list, all the attributes are correctly loaded except the ones that it has to fetch from another table.
The .jsp file :
<div class="container">
<h1>Liste des Evaluations pour ${etudiant.username}</h1>
<table class="tbl">
<thead>
<th>Module</th>
<th>Note</th>
<th>Remarque</th>
</thead>
<tbody>
<c:forEach items="model.evaluations" var="ev">
<tr>
<td>${ev.examen.module.code}</td> --> Error occurs here
<td>${ev.note}</td>
<td>${ev.remarque}</td>
</tr>.
</c:forEach>
</tbody>
</table>
The Controller :
#RequestMapping(value="/{username}", method=RequestMethod.GET)
public String etudiantEvaluations(
#PathVariable String username, Model model) {
List<Evaluation> evaluations = evalDAO.findAllByEtudiant(username);
Etudiant etudiant = etDAO.findByUsername(username);
model.addAttribute("evaluations", evaluations);
model.addAttribute("etudiant", etudiant);
return "etudiants/listEvaluations";
}
The Evaluation entity :
#Data
#NoArgsConstructor
#EqualsAndHashCode
#Immutable #Entity(name="TEVALUATION")
public class Evaluation {
private enum evaldeliberation {
REUSSITE,
AJOURNEMENT,
REFUS,
ABANDON
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
protected Long id;
#NotNull
#Min(0) #Max(100)
#Column(updatable = true)
private Double note;
#Column(name="deliberation")
private evaldeliberation delib;
#Column(name="remarque")
private String remarque;
#Column(name="module_code")
private String moduleCode;
private Long examenId;
private String etudiantUsername;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "FKExamen",
insertable = false,
updatable = false)
protected Examen examen;
#ManyToOne
#JoinColumn(name = "FKEtudiant",
insertable = false,
updatable = false)
protected Etudiant etudiant;
#OneToMany(mappedBy="evaluation")
protected List<EvalComp> evalComps = new ArrayList<>();
public Evaluation(Double note, Examen examen, Etudiant etudiant) {
super();
this.examen = examen;
this.etudiant = etudiant;
examen.getEvaluations().add(this);
etudiant.getEvaluations().add(this);
}
}
Examen:
#Data
#EqualsAndHashCode
#NoArgsConstructor
#Entity(name="TEXAMEN")
public class Examen {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "examen")
private Set<Evaluation> evaluations = new HashSet<>();
#NotNull
#OneToOne(cascade=CascadeType.PERSIST)
#JoinColumn(name="FKmodule")
protected Module module;
public Examen(Module module) {
this.module = module;
}
}
The Query:
#Query("SELECT ev FROM TEVALUATION ev JOIN FETCH ev.examen ex JOIN FETCH ex.module m WHERE ev.etudiant=?1")
List<Evaluation> findAllByEtudiantId(String username);
The getters and setters are generated by Lombok(also tried without it).
Any idea how can I load the attributes ?
Thanks in advance.

Spring 4 MVC Form: Create Object with list of sub objects

I looking for help in understanding how to create a new object through a form which has the user select multiple sub-objects (which will come pre-populated) and available to select with a checkbox.
OrderController.java
#RequestMapping(value = { "/order" }, method = RequestMethod.GET)
public String order(ModelMap model) {
List<Exam> exams = examService.findAllExams();
List<Document> documents = documentService.findAllDocuments();
model.addAttribute("exams", exams);
model.addAttribute("documents", documents);
return "order"; // jsp page reference
}
Order.java
#Entity
#Table(name="\"order\"")
public class Order implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "order_id", unique = true, nullable = false)
private Integer id;
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(name = "uuid", unique = true, nullable = false)
private String uuid;
#Temporal(TemporalType.DATE)
#Column(name = "order_date", unique = true, nullable = false)
private Date orderDate;
#Column(name="order_status", nullable=false)
private String orderStatus;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
private Set<OrderExam> orderExams = new HashSet<OrderExam>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
private Set<OrderDocument> orderDocuments = new HashSet<OrderDocument(0);
//getters & setters
}
OrderExam.java
#Entity
#Table(name="order_exam")
public class OrderExam implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "order_exam_id", unique = true, nullable = false)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "order_id", nullable = false)
private Order order;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "exam_id", nullable = false)
private Exam exam;
#Column(name="exam_amount", nullable=true)
private Integer examAmount;
#Column(name="answer_sheet_amount", nullable=true)
private String answerSheetName;
#Column(name="students_per_csv", nullable=true)
private String studentsPerCSV;
#Column(name="pas", nullable=true)
private Boolean pearsonAnswerSheet;
//getters & setters
}
Exam.java
#Entity
#Table(name="exam")
public class Exam implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "exam_id", unique = true, nullable = false)
private Integer id;
#NotEmpty
#Column(name="name", unique=true, nullable=false)
private String name;
#NotEmpty
#Column(name="code", unique=true, nullable=false)
private String code;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "exam")
private Set<OrderExam> exams = new HashSet<OrderExam>(0);
//getters & setters
}
As you can see I am passing in a list of exams and documents which will populate a form with available options (can be seen in the image below (exams anyway)). The user needs to be able to select multiple rows, so that a single order has multiple exams and documents associated to it.
My order.jsp is a little much to post the entire thing here but here is the part I have which is displayed in the image above.
Order.jsp
<form:form method="POST" modelAttribute="order" class="form-horizontal form-label-left">
<c:forEach items="${exams}" var="exam">
<tr>
<th scope="row"><input type="checkbox" class="flat"></th>
<td><input id="middle-name" type="text" name="middle-name" readonly="readonly" value="${exam.name} - ${exam.code}" class="form-control col-md-7 col-xs-12"></td>
<td><input id="middle-name" type="text" name="middle-name" value="0" class="form-control col-md-3 col-xs-12"></td>
<td><input id="middle-name" type="text" name="middle-name" value="0" class="form-control col-md-3 col-xs-12"></td>
<td><input id="middle-name" type="text" name="middle-name" value="0" class="form-control col-md-3 col-xs-12"></td>
<c:choose>
<c:when test="${exam.name == 'Algebra 2 (Common Core)'}">
<th scope="row"><input type="checkbox" class="flat"></th>
</c:when>
<c:otherwise>
<th scope="row"></th>
</c:otherwise>
</c:choose>
</tr>
</c:forEach>
<!-- Other Stuff Goes Here -->
</form:form>
So in short, would someone be willing to show me how to set up the form in the way I described above? Thanks in advance.
Your question is a bit broad however you could try this as below. I have only covered exams. Principal for documents will be the same.
You will need a couple of new classes to capture the submitted form inputs:
Order Form to Capture Selections
public class OrderForm{
private List<ExamWrapper> allAvailableExams = new ArrayList<>();
private XOptionPrintWrapper selectedWrapper;
public OrderForm(){
}
//getters and setters
}
Exam Wrapper : Decorates an Exam with a 'selected' property
public class ExamWrapper{
private boolean selected;
private Exam exam;
public ExamWrapper(Exam exam){
this.exam = exams;
}
//getters and setters
}
Change Contoller to
public class OrderController{
//Exams model populated by the method below
//moved as we also need it populated on POST
#RequestMapping(value = { "/order" }, method = RequestMethod.GET)
public String order(ModelMap modelMap) {
//only needed on GET so put in model here
List<XOptionPrintWrapper> availableWrappers = //someList;
modelMap.put("availableWrappers", availableWrappers);
return "order";
}
//handles for submit
//model atribute is automatically populated by the framework
#RequestMapping(value = { "/order" }, method = RequestMethod.POST)
public String order(#ModelAttribute("orderForm") OrderForm orderForm) {
//process selected exams
return "nextView";
}
//on get populates the initial model for display
//on post create an instance which the form params will be bound to
#ModelAttribute("orderForm")
public OrderForm getOrderForm(){
OrderForm orderForm = new OrderForm();
List<Exam> exams = examService.findAllExams();
for(Exam exam : exams){
orderForm.getAllAvailableExams.add(new ExamWrapper(exam));
}
return orderForm;
}
}
In JSP use Sping support for binding to indexed properties:
<form:form method="POST" modelAttribute="orderForm" class="form-horizontal form-label-left">
<c:forEach items="${orderForm.allAvailableExams}" var="exam" varStatus="status">
<tr>
<th scope="row"><input name="allAvailableExams[${status.index}].selected"
type="checkbox" class="flat"></th>
</tr>
</c:forEach>
<form:select path="selectedWrapper">
<form:options items="${availableWrappers}"
itemValue="somePropertyOfXOptionPrintWrapper "
itemLabel="somePropertyOfXOptionPrintWrapper " />
</form:select>
</form>
I obviously haven't been able to try all of this but think it should all be okay.

Resources