Hi I need a little help with my code. I tried solutions online but I couldn't fix my bug. I working in java and spring with mysql and tymeleaf. My error is short:
Invalid property 'projection' of bean class [com.bakulic.CinemaTicketShop.model.dto.requests.CreateOrUpdateProjectionDTO]: Bean property 'projection' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
And I found that my problem is in the html file createProjectionForm whitch I will put below as well as the entities and all that is needed. My projection entity has a relation to Hall and Movie and I'm not sure how to get attributes of Movie and Hall in my html. For the fiel I tried to put ${projection.hall.name} and ${projection.movie.movieName}. Zou will find it in the code.
Thank you in advance.
#Data
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "projections")
public class Projection {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int projectionId;
#Column(name = "date")
private String date;
#Column(name = "startTime")
private String startTime;
#ManyToOne
#JoinColumn(name = "idHall")
private Hall hall;
#ManyToOne
#JoinColumn(name = "idMovie")
private Movie movie;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "projection", cascade = CascadeType.ALL)
private List<Seat> seatList;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "projection", cascade = CascadeType.ALL)
private List<Ticket> ticketList;
}
#Entity
#Table(name = "halls")
#Data
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Hall {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int hallId;
#Column(name = "name")
private String name;
#Column(name = "numberofseats")
private Integer numberOfSeats;
#Column(name = "description")
private String description;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "hall", cascade = CascadeType.ALL)
private List<Projection> projectionList;
}
#Entity
#Table(name = "movies")
#Data
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int movieId;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column (name = "length")
private String length;
#Column(name = "picture")
private String picture;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "movie", cascade = CascadeType.ALL)
private List<Projection> projectionList;
}
#Data
public class ProjectionDTO implements Serializable {
private int id;
private String date;
private String startTime;
private List<Seat> seatList;
private List<Ticket> ticketList;
private Hall hall;
private Movie movie;
public ProjectionDTO(Projection projection){
if(projection != null){
this.id = projection.getProjectionId();
this.date = projection.getDate();
this.startTime = projection.getStartTime();
this.seatList = projection.getSeatList();
this.ticketList = projection.getTicketList();
this.hall = projection.getHall();
this.movie = projection.getMovie();
}
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class CreateOrUpdateProjectionDTO implements Serializable {
private String date;
private String startTime;
private List<Seat> seatList;
//aditional info
private String name;
private String movieName;
}
/** create projection*/
public Projection createProjection(CreateOrUpdateProjectionDTO createProjectionDTO){
if(createProjectionDTO == null){
throw new InvalidDataException("Projection cannot be null");
}
timeValidator.checkTime(createProjectionDTO.getStartTime());
dateValidator.checkDate(createProjectionDTO.getDate());
Projection proj = new Projection();
proj.setDate(createProjectionDTO.getDate());
proj.setStartTime(createProjectionDTO.getStartTime());
Hall hall = proj.getHall();
if(hall == null){
hall = new Hall();
}
hall.setName(createProjectionDTO.getName());
Integer numOfSeats = hall.getNumberOfSeats();
Movie movie = proj.getMovie();
if(movie == null){
movie = new Movie();
}
movie.setName(createProjectionDTO.getMovieName());
List<Seat> list = createProjectionDTO.getSeatList();
for(int i=1; i<=numOfSeats; i++ ){
Seat seat = new Seat();
seat.setSeatNumber(i);
seat.setStatus("empty");
list.add(seat);
}
Projection projCreated = projectionRepository.save(proj);
log.info(String.format("Projection %s has been created.", proj.getProjectionId()));
return projCreated;
} The function is similar for update.
<
<!DOCTYPE html>
<html lang="en" xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Create theater</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
</head>
<body style="background-color:lightgrey;">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<img src="../pictures/cinemalogo.png" th:src="#{pictures/cinemalogo.png}" class = "center"alt="logo" width="120" height="100"/>
<ul class="navbar-nav">
<li class="nav-item">
<h1>Our cinema!</h1>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/logout">Logout</a>
</li>
</ul>
</nav>
</nav>
<br>
<br>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1>Add projection</h1><br>
<form th:action="#{/projection}" method="post" th:object="${projection}">
<div class="form-group">
<label class="control-label" for="date"> Date </label>
<input id="date" class="form-control" th:field="*{date}"
required autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label" for="startTime"> Start time</label> <input
id="startTime" class="form-control" th:field="*{startTime}" required
autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label" for="hallName"> Hall name </label> <input
id="hallName" class="form-control" th:field="*{projection.hall.name}" required
autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label" for="movieName"> Movie name </label> <input
id="movieName" class="form-control" th:field="*{projection.movie.movieName}" required
autofocus="autofocus" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
Use th:field="*{hall.name}" instead of th:field="*{projection.hall.name}" and th:field="*{movie.movieName}" instead of th:field="*{projection.movie.movieName}"
Related
I want to show on the page all the articles from the category I need.
Class Article
public class Article {
private Long id;
private String title;
private String description;
private String image;
private String content;
private String author;
private LocalDateTime created;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "categories_articles",
joinColumns = #JoinColumn(name = "id_article"),
inverseJoinColumns = #JoinColumn(name = "id_category"))
private Set<Category> categories;
}
Class Category
public class Category {
private Integer id;
private String name;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "categories")
private Set<Article> articles;
}
Service
#Override
public List<Category> findAllCategories() {
return categoryRepository.findAll();
}
#Override
public List<Article> findAllArticles() {
return articleRepository.findAll();
}
Controller
#GetMapping("/category")
public String adminAllArticlesByCategory(Model model) {
model.addAttribute("categories", categoryService.findAllCategories());
model.addAttribute("articles", articleService.findAllArticles());
return "/viewByCategory";
}
This code doesn't work. There are no errors, but for some reason the articles are still not found
viewByCategory.html
<section>
<div th:each="category: ${categories}" th:if="${category.name == 'Tourism'}">
<div th:if="${!articles.isEmpty()}">
<div th:each="el : ${articles}">
<img th:src="${'/images/' + el.image}" th:alt="${el.title}">
<div>
<a th:href="${el.url}" th:text="${el.title}"></a>
<span th:text="${el.description}"></span>
<span th:text="${#temporals.format(el.created, 'dd MMM yyyy HH:mm')}"></span>
<span th:text="${el.author}"></span>
</div>
</div>
<h2 th:if="${articles.isEmpty()}">No Articles</h2>
</div>
</div>
</section>
What can be done?
I'm creating a new java entity with html post form,but I don't understand how to convert system id to a class System (that I pass with model.addAttribute("System",id); in controller).
Release
#Table(name = "treleas")
public class Release {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "release_id")
Integer releaseId;
#Column(name = "release_name", nullable = false)
private String releaseName;
#Column(name = "create_date", nullable = false)
private LocalDateTime releaseDate = LocalDateTime.now();
#ManyToOne
#JoinColumn(name = "system_id")
private System system;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="release_id")
private List<Req> requirements;
}
System
#Entity
#Table(name = "tsystem")
public class System {
#Id
#Column(name = "system_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
Integer systemId;
#Column(name = "system_name", nullable = false)
private String systemName;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="system_id")
private List<Release> releases;
}
Html new release
<!DOCTYPE html>
<html lang="en" xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Новый релиз</title>
</head>
<body>
<form th:method="POST" th:action="#{|/systems/${System}/releases|}"
th:object="${release}">
<label for="releaseName" >Введите название релиза: </label>
<input type="text" th:field="*{releaseName}" id="releaseName"/>
<div style="color:#f80d0d" th:if="${#fields.hasErrors('releaseName')}" th:errors="*{releaseName}"
>Name error</div>
<input type="hidden" th:field="*{system}" th:value="|${System}|"/>
<br/>
<input type="submit" value="Create!"/>
</form>
</body>
</html>
Exception
Caused by: org.postgresql.util.PSQLException: ОШИБКА: значение NULL в столбце "system_id" отношения "treleas" нарушает ограничение NOT NULL
Подробности: Ошибочная строка содержит (5, ReleaseName, 2022-05-15, null).
which means that it cannot insert release with a null system id
i have a spring boot application, and when i load the data in thymeleaf it doesn't load, it's empty. I'm using H2, so every time i launch the app i do some inserts to test, but it never loads the data. I've tried the enpoints with Postman, and they all work. I'm thinking it's something with the method i'm using, but i don't know yet.
Here's my application models:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "estudiante")
public class Estudiante {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "estudiante_generator")
private Long id;
#Column(name = "nombre")
private String nombre;
#Column(name = "apellido")
private String apellido;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "profesor_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Profesor profesor;
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "profesor")
public class Profesor {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "profesor_generator")
private Long id;
#Column(name = "nombre")
private String nombre;
#Column(name = "apellido")
private String apellido;
#Column(name = "curso")
private String curso;
public Profesor(String nombre, String apellido, String curso){
this.nombre = nombre;
this.apellido = apellido;
this.curso = curso;
}
}
The controller for the index, and the controller for the backend:
#GetMapping("/index")
public String mostrarProfesores(Model model){
List<Profesor> profesor = profesorRepository.findAll();
model.addAttribute("profesores", profesor);
return "index";
}
#GetMapping("/profesores")
public ResponseEntity<List<Profesor>> getProfesores(){
List<Profesor> profesor = new ArrayList<Profesor>();
profesorRepository.findAll().forEach(profesor::add);
if(profesor.isEmpty()){
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(profesor, HttpStatus.OK);
}
And the thymeleaf template:
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h2>Users</h2>
<table>
<thead>
<th>Nombre</th>
<th>Apellido</th>
</thead>
<tbody>
<th:block th:each="profesor : ${profesores}">
<tr>
<td>[[${profesor.nombre}]]</td>
<td>[[${profesor.apellido}]]</td>
</tr>
</th:block>
</tbody>
</table>
</div>
</body>
</html>
Any help is appreciated, thanks in advance for your help.
You should iterate the collection inside the <tr> label.
<tbody>
<tr th:each="profesor : ${profesores}">
<td th:text="${profesor.nombre}"></td>
<td th:text="${profesor.nombre}"></td>
</tr>
</tbody>
I'm working on a JPA project in which I have two models: TableDecor and Product Tag, which look like this:
#Entity(name="table_decor")
public class TableDecor {
#Id
#IdConstraint
private String id;
#Positive
#Column(nullable = false)
private int width;
#Positive
#Column(nullable = false)
private int height;
#NotNull
#Enumerated(EnumType.STRING)
#Column(nullable = false)
private Color color;
#NotNull
#Enumerated(EnumType.STRING)
#Column(nullable = false)
private Fabric fabric;
#ManyToMany
#JoinTable(
name = "seller",
joinColumns = #JoinColumn(name = "table_decor_id"),
inverseJoinColumns = #JoinColumn(name = "seller_id"))
private List<Seller> sellers;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="producer_id")
private Producer producer;
#OneToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "product_tag_id", referencedColumnName = "id")
private ProductTag productTag;
// getters, setters
#Entity(name="product_tag")
#Table
public class ProductTag {
#Id
#GeneratedValue
private UUID id;
#NotNull
#Enumerated
#Column(nullable = false)
private ProductState productState;
#Column(nullable = false)
#NotNull
private float price;
#OneToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "table_decor_id", referencedColumnName = "id")
private TableDecor tableDecor;
// getters, setters
As you can see they're connected with each other with bidirectional one to one relationship. Since one can't exist without another, how can I create a Thymeleaf add form that could create both objects at the same time? The controller function currently looks like this:
#GetMapping("/table-decor/add")
public String showAddForm(TableDecor decorToAdd, ProductTag productTag) {
return "table-decor-add";
}
#PostMapping("/table-decor/add-new")
public String addTableDecor(#Valid TableDecor decorToAdd, #Valid ProductTag productTag, BindingResult bindingResult, Model model){
if (bindingResult.hasErrors()) {
System.out.println("Error");
return "table-decor-add";
}
decorToAdd.setProductTag(productTag);
tableDecorService.addTableDecor(decorToAdd);
model.addAttribute("allTableDecor", tableDecorService.getAllTableDecor());
return "redirect:/table-decor";
and the form:
<form action="#" method="post" th:action="#{/table-decor/add-new}">
<div><input name="id" type="text" th:value="${tableDecor.id}"></div>
<span th:if="${#fields.hasErrors('tableDecor.id')}" th:errors="*{tableDecor.id}"></span>
<div><input name="width" type="text" th:value="${tableDecor.width}"></div>
<span th:if="${#fields.hasErrors('tableDecor.width')}" th:errors="*{tableDecor.width}"></span>
<div><input name="height" type="text" th:value="${tableDecor.height}"></div>
<span th:if="${#fields.hasErrors('tableDecor.height')}" th:errors="*{tableDecor.height}"></span>
<select name="color">
<option th:each="color : ${T(jee.labs.lab05.domain.Color).values()}"
th:text="${color}"
th:value="${tableDecor.color}">
</option>
</select>
<select name="fabric">
<option th:each="fabric : ${T(jee.labs.lab05.domain.Fabric).values()}"
th:text="${fabric}"
th:value="${tableDecor.fabric}">
</option>
</select>
<select name="productState">
<option th:each="productState : ${T(jee.labs.lab05.domain.ProductState).values()}"
th:text="${productState}"
th:value="${productTag.productState}">
</option>
</select>
<div><input name="price" type="text" th:value="${productTag.price}"></div>
<span th:if="${#fields.hasErrors('productTag.price')}" th:errors="*{productTag.price}"></span>
<div><input type="submit" th:value="Submit"></div>
</form>
I try to make a register page for my application and when I press submit button this error appears:
Exception evaluating SpringEL expression: "#fields.hasErrors('firstName')"
From what I understand, Thymleaf uses POJO's getters for validation and this means that firstName attribute couldn't be found. For registration I use a DTO for user entity, a DTO only for registration. If i use user entity this error never appears. What am I doing wrong?
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Registration Form</title>
<link rel="stylesheet" type="text/css" th:href="#{/css/registration.css}"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form autocomplete="on" action="register.html" th:action="#{/user/register}"
th:object="${user}" method="post" class="form-horizontal"
role="form">
<h2>Registration Form</h2>
<div class="form-group">
<div class="col-sm-9">
<label th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"
class="validation-message"></label>
<input type="text" th:field="*{firstName}" placeholder="Name"
class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<label th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"
class="validation-message"></label>
<input type="text" th:field="*{lastName}"
placeholder="Last Name" class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<label th:if="${#fields.hasErrors('username')}" th:errors="*{username}"
class="validation-message"></label>
<input type="text" th:field="*{username}"
placeholder="Username" class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<input type="text" th:field="*{emailAddress}" placeholder="Email"
class="form-control"/> <label
th:if="${#fields.hasErrors('emailAddress')}" th:errors="*{emailAddress}"
class="validation-message"></label>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<label th:if="${#fields.hasErrors('phoneNumber')}" th:errors="*{phoneNumber}"
class="validation-message"></label>
<input type="text" th:field="*{phoneNumber}"
placeholder="Phone Number" class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<input type="password" th:field="*{password}"
placeholder="Password" class="form-control"/> <label
th:if="${#fields.hasErrors('password')}" th:errors="*{password}"
class="validation-message"></label>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<input type="password" th:field="*{confirmPassword}"
placeholder="Confirm Password" class="form-control"/> <label
th:if="${#fields.hasErrors('confirmPassword')}" th:errors="*{confirmPassword}"
class="validation-message"></label>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<button type="submit" class="btn btn-primary btn-block">Register User</button>
</div>
</div>
<h2><span class="text-success" th:utext="${successMessage}"></span></h2>
</form>
</div>
</div>
</div>
</body>
</html>
RegisterUserDto
#PasswordMatches
public class RegisterUserDto {
private Long id;
#NotNull(message = "This field can't be null")
#NotEmpty(message = "This field can't be empty")
private String firstName;
#NotNull(message = "This field can't be null")
#NotEmpty(message = "This field can't be empty")
private String lastName;
#NotNull(message = "This field can't be null")
#NotEmpty(message = "This field can't be empty")
private String username;
#NotNull(message = "This field can't be null")
#NotEmpty(message = "This field can't be empty")
#ValidEmail
private String emailAddress;
#NotNull(message = "This field can't be null")
#NotEmpty(message = "This field can't be empty")
#ValidPhoneNumber
private String phoneNumber;
#ValidPassword
private String password;
private String confirmPassword;
//getters and setters
Controller
#GetMapping("/register")
public ModelAndView registration(){
ModelAndView modelAndView = new ModelAndView();
RegisterUserDto user = new RegisterUserDto();
modelAndView.addObject("user", user);
modelAndView.setViewName("register");
return modelAndView;
}
#PostMapping("/register")
public ModelAndView createNewUser(#Valid RegisterUserDto user,
BindingResult bindingResult) {
ModelAndView modelAndView = new ModelAndView();
User userExists = userService.findUserByEmail(user.getEmailAddress());
if (userExists != null) {
bindingResult
.rejectValue("emailAddress", "error.user",
"There is already a user registered with the email provided");
}
if (bindingResult.hasErrors()) {
modelAndView.setViewName("register");
} else {
userService.createUser(user);
modelAndView.addObject("successMessage", "User has been registered successfully");
modelAndView.setViewName("register");
}
return modelAndView;
}
User entity
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "username",
unique = true)
private String username;
#Column(name = "email_address",
unique = true)
private String emailAddress;
#Column(name = "phone_number")
private String phoneNumber;
#Column(name = "password")
private String password;
#Column(name = "notification_type",
insertable = false)
private String notificationType = "email";
#Column(name = "date_created")
private Date dateCreated;
#Column(name = "is_active",
insertable = false)
private Boolean active = false;
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<UserGroup> groups;
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Message> messages = new ArrayList<>();
#OneToOne(fetch = FetchType.LAZY,
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
mappedBy = "userId",
orphanRemoval = true)
private VerificationToken verificationToken;
#OneToMany(mappedBy = "createdBy",
cascade = {CascadeType.DETACH, CascadeType.MERGE,
CascadeType.REFRESH, CascadeType.PERSIST},
orphanRemoval = true)
private List<Group> createdGroups = new ArrayList<>();
//getters and setters