Spring Boot + Thymeleaf - How to show articles by category? - java

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?

Related

Invalid property 'projection' of bean class

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}"

Pass two objects with bidirectional One to One relationship into one Thymeleaf form

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>

How can I fill two entities with data after i click submit

I am looking for a solution all day, but I still can not find anything.
When I create a book, how can I create an author and assign it to a book at the same time?
I have two entities
Book
#Data
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
#ManyToOne
private Author author;
}
Author
#Data
#Entity
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "author")
private Set<Book> books;
}
Controller
#Controller
public class BookController {
private BookRepository bookRepository;
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#RequestMapping(value="/booklist", method= RequestMethod.GET)
public String bookList(Model model) {
model.addAttribute("books", bookRepository.findAll());
return "booklist";
}
#RequestMapping(value = "/add")
public String addBook(Model model){
model.addAttribute("book", new Book());
return "addbook";
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(Book book){
bookRepository.save(book);
return "redirect:booklist";
}
}
VIEW
<body>
<div>
<form th:object="${book}" th:action="#{save}" action="#" method="post">
<label for="title">Title</label>
<input type="text" id="title"
th:field="*{title}" />
<label for="author">Author</label>
<input type="text" id="author" th:field="*{author.name}" />
<input type="submit" value="Save"></input>
</form>
</div>
</body>
When I try to create a book I got this error
There was an unexpected error (type=Internal Server Error, status=500).
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing
After i click submit button
it should look like this
Try adding cascede to Author in entity like this
#Data
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
#ManyToOne(cascade=CascadeType.ALL)
private Author author;
}
or use prepersist annotation on your entity
#PrePersist
protected void onCreate() {
//check author is exist if not persist the author first
}

Spring MVC #ModelAttribute method returns "Bad request" 400

Good day. I faced a problem: I'm trying to send POST request with some attributes, but I get "Bad request" response.
Here is my controller:
#Controller
#RequestMapping("/group")
public class GroupController {
private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
.getLogger(GroupController.class);
#Autowired
private GroupService groupService;
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addGroup(#ModelAttribute("group") final Group group) {
GroupController.logger.info("I'm in POST method");
this.groupService.addGroup(group);
return "redirect:/student/add";
}
}
Here is my entity:
#Entity
#Table(name = "university_groups")
public class Group implements Serializable {
private static final long serialVersionUID = 1L;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "group")
Set<Student> students;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#NotNull
#JoinColumn(name = "department_id")
private Department department;
#Id
#Column(name = "group_name", unique = true, nullable = false)
private String group;
public Group() {
}
public Group(final String group, final Department deparment) {
this.group = group;
this.department = deparment;
}
public Department getDepartment() {
return this.department;
}
public String getGroup() {
return this.group;
}
public Set<Student> getStudents() {
return this.students;
}
public void setDepartment(final Department department) {
this.department = department;
}
public void setGroup(final String group) {
this.group = group;
}
public void setStudents(final Set<Student> students) {
this.students = students;
}
#Override
public String toString() {
return this.group;
}
}
Here is part of JSP page:
<form:form method="POST" action="add" commandName="group">
<table>
<tr>
<td><form:label path="group">
<spring:message code="label.student.group" />
</form:label></td>
<td><form:input path="group" /></td>
</tr>
<tr>
<td><form:label path="department">
<spring:message code="label.student.department" />
</form:label></td>
<td><form:select path="department">
<form:options items="${departments}" />
</form:select></td>
<td><a href="<c:url value="/department/add"/>"><spring:message
code="label.student.addDepartment" /></a></td>
</tr>
<tr>
<td colspan="2"><input type="submit"
value="<spring:message code="label.student.addGroup"/>" /></td>
</tr>
</table>
</form:form>
Here is Department entity:
#Entity
#Table(name = "departments")
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "department", unique = true, nullable = false)
private String department;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#NotNull
#JoinColumn(name = "faculty_id")
private Faculty faculty;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "department")
private Set<Group> groups;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "users_departments", joinColumns = {#JoinColumn(name = "department_id", nullable = false, insertable = true, updatable = true) }, inverseJoinColumns = {#JoinColumn(name = "user_name", nullable = false, insertable = true, updatable = true) })
private Set<User> users;
public Department() {
}
public Department(final String department, final Faculty faculty) {
this.department = department;
this.faculty = faculty;
}
public String getDepartment() {
return this.department;
}
public Faculty getFaculty() {
return this.faculty;
}
public Set<Group> getGroups() {
return this.groups;
}
public Set<User> getUsers() {
return this.users;
}
public void setDepartment(final String department) {
this.department = department;
}
public void setFaculty(final Faculty faculty) {
this.faculty = faculty;
}
public void setGroups(final Set<Group> groups) {
this.groups = groups;
}
public void setUsers(final Set<User> users) {
this.users = users;
}
#Override
public String toString() {
return this.department;
}
}
If I remove #ModelAttribute("group") final Group group from controller method, all is OK.
Please, help me, I can't understand why it doesn't works.
No one except Juergen Hoeller understands #ModelAttribute fully. It's as though they created it to scare away new guys.
They really need to change the name to #ReadModelAttribute.
You don't need #ModelAttribute parameter annotation just to use a POJO as a parameter.
You only need it for one of the following reasons:
Read a flashAttribute
Read a #SessionAttribute
Read a #ModelAttribute method (which should be renamed to #WriteModelAttribute)
Since you aren't doing any of those, don't use #ModelAttribute parameter annotation here.

java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException

My application has two entity Product and Category. When add product , user choose the category that product belong to. But when save the product to data base i meet the error
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation
the content of entity is
Category.java
#Entity
#Table(name="category")
public class Category {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="category_name")
private String categoryName;
#Column(name="description")
private String description;
#OneToMany(mappedBy = "category", fetch=FetchType.LAZY)
private Set<Product> products;
//getter and setter method
}
Product.java
#Entity
#Table(name="product")
public class Product {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="name")
#NotNull
#NotEmpty
private String name;
#Column(name="description")
private String description;
#Column(name="manufacture")
private String manufacture;
#Column(name="price")
private long price;
#Column(name="image_name")
private String imageName;
#ManyToOne
#JoinColumn(name = "category_id", referencedColumnName = "id", nullable = false)
Category category;
//getter and setter method
}
ProductDAO.java
#Repository("ProductDAO")
#Transactional
public class ProductDAO {
#PersistenceContext
private EntityManager entityManager;
public EntityManager getEntityManager() {
return entityManager;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void create(Product product) {
entityManager.merge(product.getCategory());
entityManager.persist(product);
}
//other method
}
my jsp file
<form:form method="post" commandName="product" action="add.html" enctype="multipart/form-data">
Productname <form:input path="name"/>
<form:errors path="name" class ="error"/><br>
description <form:input path="description"/><br>
price <form:input path="price"/><br>
manufacture <form:input path="manufacture"/><br>
Category
<form:select path="category">
<form:options items="${categories}" itemValue="id" itemLabel="categoryName" />
</form:select>
<form:errors path="category" class ="error"/><br>
<br/>
Product image<input type="file" name="file" />
<form:errors path="imageName" class ="error"/><br>
<input type="submit" class="register" value="Save">
</form:form>
my Converter
public class CategoryConverter implements Converter<String, Category>{
#Autowired
private CategoryService categoryService;
public Category convert(String id) {
return categoryService.findById(Long.valueOf(id));
}
}
thank for any help
please add (cascade=CascadeType.ALL) into category field mapping of Product
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "category_id", referencedColumnName = "id", nullable = false)
Category category;
and remove this line in ProductDAO:
entityManager.merge(product.getCategory());
NOTE: if your product's category is an existing one, you should load it first then set it to your product, this is normal flow. Do not new duplicated Category object.
This my fixed code
#Transactional
public void create(Product product) {
Category category=categoryDAO.findById(product.getCategory().getId());
category.getProducts().add(product);
categoryDAO.edit(category);
product.setCategory(category);
this.productDAO.create(product);
}

Categories

Resources