Changes not persisted in database in a Spring Boot application using Spring JPA - spring-boot

I have a Spring Boot application that needs adds a post to a feed list. A post is written by a user and consists of a content and several attachments. The database has 3 tables: post, attachment and user.
The main class of the application is:
#SpringBootApplication
public class SocialMediaApplication {
public static void main(String[] args) {
SpringApplication.run(SocialMediaApplication.class, args);
}
}
The entities are the following:
Post.java
#Entity
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String content;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#Column(nullable = false)
private Timestamp createdAt;
#Column
private String location;
#OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Attachment> attachmentList;
#OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Rating> ratingList;
public Post() {
}
public Post(String content, User user, Timestamp createdAt, String location, List<Attachment> attachmentList, List<Rating> ratingList) {
super();
this.content = content;
this.user = user;
this.createdAt = createdAt;
this.location = location;
this.attachmentList = attachmentList;
this.ratingList = ratingList;
}
// ...
}
Attachment.java
#Entity
public class Attachment implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Lob
#Column(length = 100_000, nullable = false)
private byte[] content;
#ManyToOne
#JoinColumn(name = "post_id")
private Post post;
public Attachment() {
}
public Attachment(byte[] content, Post post) {
super();
this.content = content;
this.post = post;
}
// ...
}
User.java
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
#Column(nullable = false)
private Date dateOfBirth;
#Column(nullable = false)
private String credential;
#Column(nullable = false)
private String password;
#Column
private String location;
#Lob
#Column(length = 100_000)
private byte[] photo;
#Column
private String motto;
public User() {
}
public User(String firstName, String lastName, Date dateOfBirth, String credential, String password,
String location, byte[] photo, String motto) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = dateOfBirth;
this.credential = credential;
this.password = password;
this.location = location;
this.photo = photo;
this.motto = motto;
}
// ...
}
All repositories extend CrudRepository and are annotated with #Transactional:
PostRepository.java
#Transactional
public interface PostRepository extends CrudRepository<Post, Long> {
}
AttachmentRepository.java
#Transactional
public interface AttachmentRepository extends CrudRepository<Attachment, Long> {
}
UserRepository.java
#Transactional
public interface UserRepository extends CrudRepository<User, Long> {
}
The controller that should add a post to the feed is the following:
#Controller
#RequestMapping("/post")
public class PostController {
#Autowired
PostRepository postRepository;
#GetMapping("/add")
public String greetingForm(Model model) {
model.addAttribute("post", new Post());
return "addPost";
}
#PostMapping("/add")
public String addPost(#ModelAttribute Post post, #RequestParam("attachment") MultipartFile uploadingFile) throws IOException {
User user = new User();
user.setId(1L);
post.setUser(user);
post.setCreatedAt(Timestamp.valueOf(LocalDateTime.now()));
List<Attachment> attachmentList = new ArrayList<>();
Attachment attachment = new Attachment();
attachment.setContent(uploadingFile.getBytes());
attachment.setPost(post);
attachmentList.add(attachment);
post.setAttachmentList(attachmentList);
List<Rating> ratingList = new ArrayList<>();
post.setRatingList(ratingList);
postRepository.save(post);
return "allPosts";
}
}
The addPost.html page has the following content:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Post</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Add Post</h1>
<form action="#" th:action="#{/post/add}" th:object="${post}" method="post" enctype="multipart/form-data">
<table border="0">
<tr>
<td>Content</td>
<td><textarea id="content" th:field="*{content}" rows="5" cols="50"></textarea></td>
</tr>
<tr>
<td>Location</td>
<td><input type="text" id="location" th:field="*{location}"/></td>
</tr>
<tr>
<td>Attachment</td>
<td><input type="file" id="attachment" name="attachment"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</td>
</tr>
</table>
</form>
</body>
</html>
The application.properties file has the following content:
spring.datasource.url=jdbc:mysql://localhost:3306/****?useSSL=false
spring.datasource.username=root
spring.datasource.password=****
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.id.new_generator_mappings=false
spring.jpa.show-sql=true
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
However, when I press the submit button, nothing is persisted into the database, although the queries are being displayed in the console:
Hibernate: insert into post (content, created_at, location, user_id) values (?, ?, ?, ?)
Hibernate: insert into attachment (content, post_id) values (?, ?)
What could be the cause?

Related

EL1007E: Property or field 'name' cannot be found on null

I am using a Spring framework with thymeleaf and mysql.
I am getting the error
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null
This error is being caused by the ${selectProject.client.name} in the html code.
I have written a form to allow a user to add information about a project. As part of the form, there is a dropdown list of the different project names. However, in the dropdown list I also want it to give the name of the client alongside the name of the project.
My html code:
<form action="#" id="informationForm" method="post" th:action="#{/add-information}" th:object="${information}">
<div class="row">
<div class="form-group col-12">
<label class="col-form-label" for="project">Project</label>
<select class="form-control" id="project" th:field="*{project}">
<option value="0">Please select a project</option>
<option th:each="selectProject : ${projectList}"
th:text="${selectProject.client.name} + ' - ' +${selectProject.name}"
th:value="${selectProject.id}"></option>
</select>
Here is the information controller:
#Controller
#Slf4j
#SessionAttributes({"project", "information"})
public class InformationController {
private final InformationService informationService;
private final ProjectsService projectsService;
#Autowired
public InformationController(final ProjectsService projectsService,
final InformationService informationService) {
this.projectsService = projectsService;
this.informationService = informationService;
}
#ModelAttribute("information")
public Information getInformation() {
return informationService.createEmptyInformation();
}
#ModelAttribute("projectList")
public List<Project> getProjects() {
return projectsService.getProjects();
}
#GetMapping("add-information")
public String showAddInformationForm(Model model, #ModelAttribute("information") Information information) {
information = informationService.createEmptyInformation();
model.addAttribute(information);
return "add-information";
}
#PostMapping("add-information")
public String addInformationForm(#Valid Information information, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-information";
}
Long id = informationService.createInformationFromInput(information);
return "redirect:/homepage";
}
The relevant methods in the information service are:
public Information createEmptyInformation() {
return new Information();
}
public Long createInformationFromInput(Information information) {
try {
informationDao.save(information);
} catch (Exception ex) {
log.error("Failed to save information for {} because of {}", information.getId(), ex.getLocalizedMessage());
}
return information.getId();
}
and in the projects services:
public List<Project> getProjects() {
return projectDao.findAll();
}
Clients have a many to one relationship with projects
Project domain:
#Getter
#Setter
#Entity
#Table(name = "projects")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "client_id")
private Client client;
#Column(name = "name")
private String name;
Client domain:
#Getter
#Setter
#Entity
#Table(name = "clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Name is required")
#Size(min = 5, max = 80, message = "Name must be between 5 and 80 characters")
#Column(name = "name", nullable = false)
private String name;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "client_id")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
private List<Project> projects;
Information domain:
#Getter
#Setter
#Entity
#Table(name = "information")
public class Information {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "project_id")
private Project project;
#Column(name = "date")
private Date date;

Thymeleaf: print a Set in the template

I have a basic SpringBoot 2.0.4.RELEASE app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
I have this entity:
#Entity
#Table(name="t_user")
public class User implements Serializable, UserDetails {
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonIgnore
private Set<UserRole> userRoles = new HashSet<>();
public Set<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<UserRole> userRoles) {
this.userRoles = userRoles;
}
}
and this:
#Entity
#Table(name = "t_user_role")
public class UserRole implements Serializable {
/** The Serial Version UID for Serializable classes. */
private static final long serialVersionUID = 1L;
public UserRole() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
public UserRole(User user, Role role) {
this.user = user;
this.role = role;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "role_id")
private Role role;
...
}
and want to print all the roles of a User, so I use this code:
<td class="col_name" th:text="${user.userRoles.role}"></td><!-- ROLES -->
but what I see in the browser is literally this:
com.tdk.backend.persistence.domain.backend.User.userRoles
You will want to iterate through the set of objects to print the role values. You can use Thymeleaf's th:each syntax to do so:
<td class="col_name">
<span th:each="userRole : ${user.userRoles}" th:text="${userRole.role}">[role]</span><!-- format with line breaks as needed -->
</td>
You can alternatively look at the lists utility and call the toString() to output for a quick and dirty way:
<td class="col_name">
<span th:text="${#lists.toString(user.userRole.role)}">[role]</span>
</td>
You can optionally remove the <span> tags by using th:remove="tag".

Spring MVC Error: Failed to convert property value of type java.lang.String to required type

I can't let this exception go:
Failed to convert property value of type java.lang.String to required type com.company.springdemo.entity.Product for property productId; nested exception is java.lang.IllegalStateException: Cannot convert value of type java.lang.String to required type com.company.springdemo.entity.Product for property productId: no matching editors or conversion strategy found
Order Model
#Entity
#Table(name = "orders") // naming the table only order, will throw exception
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
private Integer orderId;
#OneToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
#JoinColumn(name = "product_id")
private Product productId;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
#JoinColumn(name = "client_id")
private Client client;
....
Product Model
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_id")
private Integer id;
#Column(name = "product_name")
private String productName;
#Column(name = "product_serial")
private String productSerial;
...
Client Model
#Entity
#Table(name = "clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotEmpty
#Column(name = "first_name")
private String firstName;
#NotEmpty
#Column(name = "last_name")
private String lastName;
#NotEmpty
#Email
#Column(name = "email")
private String email;
#NotEmpty
#Column(name = "location")
private String location;
#OneToMany(mappedBy = "client",cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Order> orders;
Controller, where I save the order with related client and product
#PostMapping("add")
public ModelAndView addOrder( #Validated #ModelAttribute("ords") Order order, BindingResult bindingResult ){
if (bindingResult.hasErrors()) {
System.out.println("Having errors: " + bindingResult.getAllErrors());
Iterable<Product> products = productService.listProducts();
Iterable<Client> clients = clientService.listClients();
System.out.println("Error "+ bindingResult.getAllErrors());
ModelAndView mv = new ModelAndView("orders/add-order");
mv.addObject("products",products);
mv.addObject("clients",clients);
return mv;
}
try {
orderService.saveOrder(order);
} catch (Exception e) {
e.printStackTrace();
}
ModelAndView mv = new ModelAndView("redirect:list");
return mv;
}
Finally, my JSP form View page
<form:form action="add" method="post" modelAttribute="ords">
<label for="productId" >Product Id</label>
<form:select path="productId" >
<c:forEach var="product" items="${products}">
<form:option value="${product.id}">${product.productName}</form:option>
</c:forEach>
</form:select>
<form:errors path="productId"/>
<br>
<label for="client" >Client Id</label>
<form:select path="client" >
<c:forEach var="client" items="${clients}">
<form:option value="${client.id}">${client.id} - ${client.lastName}</form:option>
</c:forEach>
</form:select>
<form:errors path="client"/>
<br>
<input type="submit" value="Place Order">
</form:form>
What am I doing wrong?
You most likely need to build a converter class such as this one :
#Component("facilityConverter")
public class FacilityConverter implements Converter<String, Facility>
{
#Autowired
FacilityService facilityService;
#Override
public Facility convert(String id)
{
return facilityService.findById(Integer.parseInt(id));
}
}
Then, you need to register it by implementing the addFormatters method inside of a configuration class implementing WebMvcConfigurer like so :
#Override
public void addFormatters (FormatterRegistry registry)
{
registry.addConverter((FacilityConverter)ctx.getBean("facilityConverter"));
}
Your entities will then correctly be mapped from a dropdown selection. Also, this might not be part of your issue but you can just build your dropdowns like this :
<form:select name="linkedInterface" path="linkedInterface" id="linkedInterface">
<form:options items="${interfaces}" itemLabel="name" itemValue="id"/>
</form:select>
The productId field is actually a Product object, not an ID (String/int). You need your JSP to use path="productId.id" rather than path="productId".
(Although I'd also suggest you also rename the field product rather than productId.)
<form:select path="product.id">
I think you'll hit the same issue on your <form:select path="client"> too.

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.

Unable to get dependent object data after form submission in spring MVC

public class Employee implements IEmployee, Serializable {
private static final long serialVersionUID = 3539505455231361934L;
#Column(name="emp_Id", nullable=false)
private Integer emp_Id;
#Id
#GeneratedValue
#Column(name="login_Id", nullable=false)
private String login_Id;
#Column(name="password", nullable=false)
private String password;
#Column(name="first_name", nullable=false)
private String first_name;
#Column(name="last_name", nullable=false)
private String last_name;
#Column(name="email", nullable=false)
private String email;
#Column(name="address", nullable=false)
private String address;
#Column(name="mobile_Number", nullable=false)
private Integer mobile_Number;
#Column(name="create_Date", nullable=false)
private Date create_Date;
#Column(name="modified_Date", nullable=false)
private Date modified_Date;
#Column(name="security_Question")
private String security_Question;
#Column(name="security_Question_Answer")
private String security_Question_Answer;
#Column(name="login_Attempts")
private String login_Attempts;
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name="employe_role",
joinColumns = {#JoinColumn(name="login_Id")},
inverseJoinColumns = {#JoinColumn(name="role_Id")})
private Collection<Role> role;//need to change name --> Dependent Object Role
---settters and getters
}
Role
#Id
#GeneratedValue
#Column(name="role_Id")
private Integer role_Id;
#Column(name="role_Code", nullable=false)
private String role_Code;
#Column(name="role_Name", nullable=false)
private String role_Name;
#Column(name="discription", nullable=false)
private String discription;
#Column(name="created_Date")
private Date created_Date;
#Column(name="modified_Date")
private Date modified_Date;
JSP form
<form:form name="register-employee" action="/registerEmployee" method="post" commandName="employee">
<c:forEach var="role" varStatus="statusEmpRole" items="${employee.role}">
<form:hidden path="role[${statusEmpRole.index}].role_Name" value="${role.role_Name}" />
<form:checkbox path="role[${statusEmpRole.index}].role_Name" value="${role.role_Name}" itemValue="role.role_Id" />
<c:out value="${role.role_Name}" /><br>
Controller
For displaying the form
#Override
#RequestMapping(value="/employeeregistrationform", method = RequestMethod.GET)
public ModelAndView employeeRegistrationForm(#ModelAttribute("employee") Employee employee, Model map) throws HibernateException, RoleNotFoundException {
IEmployee iEmployee = new Employee();
Collection<Role> collectionRoles= IRoleService.getLookUpRoles();
for (Role role : collectionRoles) {
LOGGER.info("roel {}",role.getRole_Name());
}
iEmployee.setFirst_name("helloooooo");
iEmployee.setRole(collectionRoles);
return new ModelAndView("registerEmploye", "employee", iEmployee);
}
Get the Submitted form Data
#Override
#RequestMapping(value="/registerEmployee",method=RequestMethod.POST)
public ModelAndView registerEmployee(#ModelAttribute("employee")Employee employee, BindingResult result) {
LOGGER.info("Registering Employe {}",employee.getFirst_name());
LOGGER.info("Selected Role Employe {}",employee.getRole());
ModelAndView model = new ModelAndView();
model.setViewName("registerEmploye");
return model;
}
employee.getRole() is getting null
my case is employee having multiple roles. let say admin and Projectmanager. while creating employee admin may select the roles(these are come from database) after submitting the employee registration from i'm getting role object is null.
please help me in this. Am i missing any thing here like property editor or init binder. if so please give me example how to use them.
Thanks
after Google i find the answer
I create Init Binder
#InitBinder
public void bindForm(WebDataBinder binder) {
binder.registerCustomEditor(Collection.class, new RoleEditor(Collection.class,true));
}
And i supply CustomCollectionEditor to that i.e RoleEditor
package com.evoke.tms.util;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import com.evoke.tms.model.Role;
public class RoleEditor extends CustomCollectionEditor {
private Set<Role> roles;
public RoleEditor(Class collectionType, boolean nullAsEmptyCollection) {
super(collectionType, true);
}
public void setValue( Object object ){
if(object!=null&&object instanceof String)
System.out.println("Object is of type - " + object.getClass().getCanonicalName());
String[] roleIds = (String[])object;
roles=new HashSet<Role>();
if(roleIds!=null && roleIds.length>0)
for( int i=0; i<roleIds.length; i++ ){
try {
int id = Integer.parseInt(roleIds[i]);
Role role = new Role();
role.setRole_Id(id);
roles.add(role);
}catch( NumberFormatException ne ){}
}
}
public Object getValue(){
System.out.println("Roles are - " + roles);
return roles;
}
}
And i'm still confused how it is working
can any one help on this...

Resources