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);
}
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 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 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
}
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.
In my spring application, the forms for insert/update a entity in the database have a structure similar to this:
<jsp:include page="../../common/cadastrar.jsp">
<jsp:param name="entity" value="Usuario"/>
<jsp:param name="arguments" value="login"/>
<jsp:param name="arguments" value="senha"/>
<jsp:param name="arguments" value="first_name"/>
<jsp:param name="arguments" value="last_name"/>
<jsp:param name="arguments" value="email"/>
</jsp:include>
which redirect to this shared jsp page (common to all the views):
<c:url value="${param.entity}/cadastra" var="cadastra"/>
<form:form class="form" role="form" method="post" action="${cadastra}">
<table>
<c:forEach var="item" items="${paramValues.arguments}">
<c:choose>
<c:when test="${item == 'senha'}">
<tr>
<td><form:label path="${item}">${item}</form:label></td>
<td><form:input path="${item}" type="password"/></td>
</tr>
</c:when>
<c:otherwise>
<tr>
<td><form:label path="${item}">${item}</form:label></td>
<td><form:input path="${item}"/></td>
</tr>
</c:otherwise>
</c:choose>
</c:forEach>
<tr>
<td colspan="2">
<button type="submit" class="btn btn-lg btn-primary">Cadastrar</button>
</td>
</tr>
</table>
</form:form>
When my entity class have only primitive attributes (Integer, String, etc), this code works fine. But I can't figure out how to do the same for classes like that:
#Entity
#Table(name="cliente")
public class Cliente extends Entidade {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#OneToOne
#JoinColumn(name="fk_usuario")
private Usuario usuario;
#Column(name="documento")
private String documento;
#Column(name="cpf")
private String cpf;
#Column(name="cnpj")
private String cnpj;
#ManyToMany
#JoinTable(name="endereco_entrega", joinColumns={#JoinColumn(name="fk_cliente")}, inverseJoinColumns={#JoinColumn(name="fk_endereco")})
#LazyCollection(LazyCollectionOption.FALSE)
private List<Endereco> endereco;
#ManyToMany
#JoinTable(name="pedido_cliente", joinColumns={#JoinColumn(name="fk_cliente")}, inverseJoinColumns={#JoinColumn(name="fk_pedido")})
#LazyCollection(LazyCollectionOption.FALSE)
private List<Pedido> pedido;
}
where some of the attributes are other entities. Anyone can point me a direction to do this?
UPDATE 1
Usuario.java
#Entity
#Table(name="usuario")
public class Usuario extends Entidade {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "login")
private String login;
#Column(name = "senha")
private String senha;
#Column(name="first_name")
private String first_name;
#Column(name="last_name")
private String last_name;
#Column(name="email")
private String email;
#ManyToMany
#JoinTable(name="role_members", joinColumns={#JoinColumn(name="fk_user")}, inverseJoinColumns={#JoinColumn(name="fk_role")})
#LazyCollection(LazyCollectionOption.FALSE)
private List<Role> Autorizacao = new ArrayList<Role>();
}
Endereco.java
#Entity
#Table(name="endereco")
public class Endereco {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="logadouro")
private String logradouro;
#Column(name="numero")
private String numero;
#Column(name="complemento")
private String complemento;
#Column(name="bairro")
private String bairro;
#Column(name="cidade")
private String cidade;
#Column(name="estado")
private String estado;
#Column(name="cep")
private String cep;
}
Pedido.java
#Entity
#Table(name="pedido")
public class Pedido {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#ManyToMany
#JoinTable(name="produtos_do_pedido", joinColumns={#JoinColumn(name="fk_pedido")}, inverseJoinColumns={#JoinColumn(name="fk_produto")})
#LazyCollection(LazyCollectionOption.FALSE)
private List<Produto> produto;
#ManyToMany
#JoinTable(name="cobranca_do_pedido", joinColumns={#JoinColumn(name="fk_pedido")}, inverseJoinColumns={#JoinColumn(name="fk_cobranca")})
#LazyCollection(LazyCollectionOption.FALSE)
private List<Cobranca> cobranca;
}