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

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
}

Related

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

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?

Thymeleaf not loading the data

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>

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>

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