error converting from string to blob in spring mvc hibernate application - java

In a spring mvc application that utilizes hibernate and jpa, I have a form that needs to store a document including metadata about the document such as the current date when the form is submitted. I have the model, controller, and jsp all set up, but when it runs, the create or add jsp is returned when the user clicks on the button to submit a document to the database. The error indicates that the problem is that the code is not handling the conversion from String to Blob. I HAVE NOTED THE LOCATION IN THE processCreationForm() METHOD WHERE THE CODE IS RETURNING THE createOrUpdateDocumentForm.jsp INSTEAD OF REDIRECTING TO THE LIST JSP AND I HAVE INCLUDED THE ERROR MESSAGE AT THE BOTTOM OF THIS POSTING.
How can I change my code below so that document is saved to the database when the user clicks on the submit button?
Here is my createOrUpdateDocumentForm.jsp:
<body>
<script>
$(function () {
$("#created").datepicker({ dateFormat: 'yy/mm/dd'});
});
</script>
<div class="container">
<jsp:include page="../fragments/bodyHeader.jsp"/>
<c:choose>
<c:when test="${document['new']}">
<c:set var="method" value="post"/>
</c:when>
<c:otherwise>
<c:set var="method" value="put"/>
</c:otherwise>
</c:choose>
<h2>
<c:if test="${document['new']}">New </c:if>
Document
</h2>
<form:form modelAttribute="document" method="${method}"
class="form-horizontal">
<div class="control-group" id="patient">
<label class="control-label">Patient </label>
<c:out value="${document.patient.firstName} ${document.patient.lastName}"/>
</div>
<petclinic:inputField label="Name" name="name"/>
<petclinic:inputField label="Description" name="description"/>
<div class="control-group">
<petclinic:selectField name="type" label="Type " names="${types}" size="5"/>
</div>
<td><input type="file" name="content" id="content"></input></td>
<div class="form-actions">
<c:choose>
<c:when test="${document['new']}">
<button type="submit">Add Document</button>
</c:when>
<c:otherwise>
<button type="submit">Update Document</button>
</c:otherwise>
</c:choose>
</div>
</form:form>
<c:if test="${!document['new']}">
</c:if>
<jsp:include page="../fragments/footer.jsp"/>
</div>
</body>
Here are the relevant parts of the controller:
#RequestMapping(value = "/patients/{patientId}/documents/new", method = RequestMethod.GET)
public String initCreationForm(#PathVariable("patientId") int patientId, Map<String, Object> model) {
Patient patient = this.clinicService.findPatientById(patientId);
Document document = new Document();
patient.addDocument(document);
model.put("document", document);
return "documents/createOrUpdateDocumentForm";
}
#RequestMapping(value = "/patients/{patientId}/documents/new", method = RequestMethod.POST)
public String processCreationForm(#ModelAttribute("document") Document document, BindingResult result, SessionStatus status) {
document.setCreated();
//THE FOLLOWING LINE PRINTS OUT A VALID DATE FOR document.getCreated()
System.out.println("document.getCreated() is: "+document.getCreated());
new DocumentValidator().validate(document, result);
if (result.hasErrors()) {
System.out.println("result.getFieldErrors() is: "+result.getFieldErrors());
//THIS IS BEING RETURNED BECAUSE result.getFieldErrors() RETURNS WHAT IS BEING
//SHOWN AT THE BOTTOM OF THIS POSTING, BELOW
return "documents/createOrUpdateDocumentForm";
}
else {
this.clinicService.saveDocument(document);
status.setComplete();
return "redirect:/patients?patientID={patientId}";
}
}
And here is the the model, which are parts of Document.java:
#Entity
#Table(name = "documents")
public class Document {
#Id
#GeneratedValue
#Column(name="id")
private Integer id;
#ManyToOne
#JoinColumn(name = "client_id")
private Patient patient;
#ManyToOne
#JoinColumn(name = "type_id")
private DocumentType type;
#Column(name="name")
private String name;
#Column(name="description")
private String description;
#Column(name="filename")
private String filename;
#Column(name="content")
#Lob
private Blob content;
#Column(name="content_type")
private String contentType;
#Column(name = "created")
private Date created;
public Integer getId(){return id;}
public void setId(Integer i){id=i;}
protected void setPatient(Patient patient) {this.patient = patient;}
public Patient getPatient(){return this.patient;}
public void setType(DocumentType type) {this.type = type;}
public DocumentType getType() {return this.type;}
public String getName(){return name;}
public void setName(String nm){name=nm;}
public String getDescription(){return description;}
public void setDescription(String desc){description=desc;}
public String getFileName(){return filename;}
public void setFileName(String fn){filename=fn;}
public Blob getContent(){return content;}
public void setContent(Blob ct){content=ct;}
public String getContentType(){return contentType;}
public void setContentType(String ctype){contentType=ctype;}
public void setCreated(){created=new java.sql.Date(System.currentTimeMillis());}
public Date getCreated() {return this.created;}
#Override
public String toString() {return this.getName();}
public boolean isNew() {return (this.id == null);}
}
The above code compiles, but when the user presses the submit button after entering the information to upload a document, the same add or update form is returned instead of redirecting to the summary page. This indicates from the controller method above that result.haserrors is true even though none of the errors checked for by system.out.println are true. The eclipse console does not show an error. However result.getFieldErrors() prints out the following:
[
Field error in object 'document' on field 'content':
rejected value [mydocname.txt];
codes [typeMismatch.document.content,typeMismatch.content,typeMismatch.java.sql.Blob,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [document.content,content];
arguments []; default message [content]];
default message
[
Failed to convert property value of type 'java.lang.String' to required type 'java.sql.Blob' for property 'content';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type [java.lang.String] to required type [java.sql.Blob] for property 'content':
no matching editors or conversion strategy found
]
]

First of all Your form is missing the tag enctype="multipart/form- data",
If it not still working you may consider using the MultipartFile interface
UPDATE
You can read the spring documentation, it is really straightforward .
Now, to apply it in your situation you can follow this tutorial : Saving/Retreving BLOB object in Spring 3 MVC and Hibernate

Related

thymeleaf and spring boot form exception

I'm trying to add data to my database and reload the same page using spring boot and thymeleaf but when I save data I face this error
org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'org.closure.gcp.entities.QuestionEntity'; nested exception
is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value 'adsf'; nested exception is java.lang.NumberFormatException: For input string: "adsf"
controller code :
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#RequestMapping(path = "/")
public String index(#ModelAttribute("question") QuestionEntity question, Model model)
{
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
#RequestMapping(value="/add", method=RequestMethod.POST)
public String addQuestion(Model model,#ModelAttribute("question") QuestionEntity question) {
questionRepo.save((QuestionEntity)model.getAttribute("question"));
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
}
thymeleaf page :
<html>
<header>
<title>Questions</title>
</header>
<body>
<h2>hello questions</h2>
<hr>
<tr th:each="q: ${questions}">
<td th:text="${q.question}"></td>
<br>
<td th:text="${q.question_type}"></td>
<hr>
</tr>
<!-- <form th:action="#{/add}" th:object="${question}" method="post"> -->
<form action="./add" th:object="${question}" method="POST">
<input type="text" th:field="*{question}" />
<br >
<input type="text" th:field="*{question_type}" />
<br >
<input type="submit" value="save" >
</form>
</body>
</html>
#Entity
#Table(name="question")
public class QuestionEntity {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
#Column(nullable=false)
private String question;
#Column(nullable=false)
private String question_type;
#ManyToOne(optional = true)
private InterestEntity interest;
#ManyToOne(optional = true)
private LevelEntity level;
#Column(nullable = true)
private String sup_file;
#Column(nullable = false)
private int pionts;
#ManyToMany
private List<ContestEntity> contest;
#OneToMany(mappedBy ="question")
private List<AnswerEntity> answers;
// getters and setters
}
notice when I try to open another page in "/add" it works
I found this to solve
I just made a model class and use it instead of entity
and I used just one method to handle index and add requests
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#RequestMapping(path = {"/",""},method = {RequestMethod.POST,RequestMethod.GET})
public String index(#ModelAttribute("question") QuestionModel question, Model model,HttpServletRequest request)
{
if(request.getMethod().equals("POST"))
{
questionRepo.save(new QuestionEntity().question(question.getQuestion()).question_type(question.getQuestion_type()));
}
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
return "Questions";
}
}
It is better to have 2 separate methods, one for GET and one for POST and to use redirect after the POST (see https://en.wikipedia.org/wiki/Post/Redirect/Get). This is how I would code this based on your separate QuestionModel class:
#Controller
#RequestMapping(path = "/Questions")
public class QuestionView {
#Autowired
QuestionRepo questionRepo;
#GetMapping
public String index(Model model)
{
List<QuestionEntity> list = questionRepo.findAll();
model.addAttribute("questions", list);
model.addAttribute("question", new QuestionModel());
return "Questions";
}
#PostMapping("/add")
public String addQuestion(#Valid #ModelAttribute("question") QuestionModel question, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()) {
return "Questions";
}
questionRepo.save(new QuestionEntity().question(question.getQuestion()).question_type(question.getQuestion_type()));
return "redirect:/Questions";
}
}
Main points:
Use separate methods for GET and POST
Add the #Valid annotation to the #ModelAttribute in the POST method so any validation annotations on QuestionModel are checked (Because you probably want to make sure the question has at least some text in it for example).
Use BindingResult as parameter to check if there are validation errors.
Use "redirect:" to force a new GET after the POST to help avoid double submissions if a user would refresh the browser.

Validation an entity class contains subclass in spring MVC

please help me.
I'm working validate form. The fields of class student as "name","address","email" display message when i click submit form but my problem is the fields that class student contains relationship as class major is not display message. I tried to put #Valid annotations and i get the same a result.
I get an errors: Failed to convert property value of type java.lang.String to required type com.springmvc.entities.Major for property major; nested exception is java.lang.IllegalStateException: Cannot convert value of type java.lang.String to required type com.springmvc.entities.Major for property major: no matching editors or conversion strategy found.
Can someone help me or give me solutions ? I'm so grateful !
I sincerely apologize if my English is not good
#Entity(name="student")
public class Student{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int id;
#NotNull(message="Your name must not null !")
String name;
#NotNull(message="Your address must not null !")
String address;
#NotNull(message="Your email must not null !")
String email;
#NotNull(message="Select a major !")
#Valid
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="idMajor")
private Major major;
//getter - setter ...
}
#Entity(name="major")
public Class Major{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
int idMajor;
String major;
//getter - setter ...
}
//My controller
#Controller
public class StudentController{
#Autowired
StudentService studentService;
#InitBinder
public void InitBinder(WebDataBinder binder){
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
binder.registerCustomEditor(String.class, stringTrimmerEditor);
}
#ModelAttribute("studentForm")
public Student studentForm()
{
return new Student();
}
#RequestMapping(value="/saveStudent",method= RequestMethod.POST)
public String SaveStudent(#Valid #ModelAttribute("studentForm") Student
student,BindingResult bindingResult, ModelMap model) {
if (bindingResult.hasErrors())
{
return "page-student";
}
else {
model.addAttribute("msg", "Save success!");
studentService.SaveStudent(student);
return "page-student;
}
}
}
//My View (page-student.jsp)
<form:form action="saveStudent" enctype="multipart/form-data" method="post"
modelAttribute="studentForm" >
<p>Name:<form:input path="name"/></p>
<form:errors path="name" cssClass="error" /> // Validate ok !
<p>Address:<form:input path="address"/></p>
<form:errors path="address" cssClass="error" /> // Validate ok !
<p>Email:<form:input path="email"/></p>
<form:errors path="email" cssClass="error" /> // Validate ok !
<p>Major:<form:select path="major">
<form:option value="0">-- Select --</form:option>
<c:forEach var="major" items="${major}">
<form:option value="${major.getIdMajor()}">
${major.getMajor()}
</form:option>
</c:forEach>
</form:select></p>
<form:errors path="major" cssClass="error" />
// I get an errors as I mentioned in my description above.
<form:button type="submit" >Submit</form:button>
</form:form>

Accessing Object in View with Thymeleaf

I have an customer object whose fields I'm trying to access in my view with Thymeleaf. I've used the usual syntax i.e:
<p th:text="${customer.name}"></p>
however, this does not work, it does work when I use the get() method i.e:
<p th:text="${customer.get.name}"></p>
Any idea why that is happening? I'm just getting started with Thymeleaf, so I apologize in advance if this is a dumb question.
Here's my Model:
#Id
#GeneratedValue
private int id;
#NotNull
#Size(min = 2, message="Company name length must be at least 1 character long.")
private String name;
public Customer() {}
public Customer(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
And the Controller:
#RequestMapping("")
public String index(Model model) {
model.addAttribute("title", "Home");
model.addAttribute("customers", customerDao.findAllByOrderByNameAsc());
return "index";
}
#RequestMapping(value = "", method = RequestMethod.POST)
public String processFetch(#ModelAttribute Customer customer, Model model) {
model.addAttribute("title", "Home");
model.addAttribute("customers", customerDao.findAllByOrderByNameAsc());
model.addAttribute("c", customerDao.findById(customer.getId()));
return "index";
}
and the View:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments::head"></head>
<body>
<nav th:replace="fragments::nav"></nav>
<div class="container">
<h1 class="title" th:text="${title}"></h1>
</div>
<div style="text-align: center">
<form method="post" th:object="${customers}" style="display: block;margin: 0 auto;width:300px;margin-top:5%">
<select class="form-control" name="customer">
<option style="text-align:center" th:each="customer:${customers}" th:value="${customer.id}" th:text="${customer.name}"></option>
</select>
<input class="button" style="display:block;margin:0 auto;margin-top:30px" type="submit" value="Fetch" />
</form>
</div>
<div>
<h1 th:text="${c.get().name}"></h1>
</div>
</body>
</html>
Here's my Repository class:
public interface CustomerDao extends CrudRepository<Customer, Integer> {
public Iterable<Customer> findAllByOrderByNameAsc();
}
The error I try to submit the form is:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null
I'm not sure why it cannot find property 'name'. It should be part of the object.
I appreciate any insight into this!

Unable to get updated values in a dynamically created form using Spring Boot and Thymeleaf

I have created a dynamic form in Thymeleaf which populates feedbacks from all users in a table format on the UI. The form is first called when the GET Api of the controller gets hit. Relevant code for the same is given below :
allfeedbacks.html
<h2>Dynamic form</h2>
<form action="#" th:action="#{/updatefb}" th:object="${feedbacklist}"
method="post">
<table>
<tr>
<th>Message</th>
<th>Status</th>
<th>Comments</th>
</tr>
<tr th:each="feedback : ${feedbacklist.myfbList}">
<td th:text="${feedback.message}" th:field="${feedback.message}">The
first name</td>
<td><select>
<option value="Pending"
th:selected="${feedback.status == 'Pending'}">Pending</option>
<option value="In Process"
th:selected="${feedback.status == 'In Process'}">In
Process</option>
<option value="Done" th:selected="${feedback.status == 'Done'}">Done</option>
</select></td>
<td><input type="text" placeholder="Enter Comment Here"
name="comments" th:text="${feedback.comment}"
th:field="${feedback.comment}" /></td>
</tr>
</table>
<button type="submit">Submit</button>
</form>
Basically I have created two beans, one is the Feedback.java bean while the other is FeedbackList.java bean. Code for the same is given below :
Feedback.java
#Entity
#Table(name = "feedback")
public class Feedback implements Serializable {
private static final long serialVersionUID = -3009157732242241606L;
#Id
private String id;
public String getId() {
return id;
}
public String getMessage() {
return message;
}
public String getStatus() {
return status;
}
public String getComment() {
return comment;
}
#Column(name = "message")
private String message;
#Column(name = "status")
private String status;
#Column(name = "comment")
private String comment;
public Feedback() {
}
public Feedback(String message, String status) {
this.message = message;
this.status = status;
this.id = UUID.randomUUID().toString();
}
FeedbackList.java
public class FeedbackList {
ArrayList<Feedback> myfbList;
public ArrayList<Feedback> getMyfbList() {
return myfbList;
}
public void setMyfbList(ArrayList<Feedback> myfbList) {
this.myfbList = myfbList;
}
}
Relevant code from my Controller class is as follows :
#RequestMapping(value = "/getAll", method = RequestMethod.GET)
public String getAllFeedbacks(#Valid FeedbackList feedbacklist,
BindingResult bindingResult, Model model) {
ArrayList<Feedback> fbarray = new ArrayList<>();
for (Feedback fb : repository.findAll()) {
fbarray.add(fb);
}
feedbacklist.setMyfbList(fbarray);
model.addAttribute("feedback", new Feedback());
model.addAttribute("feedbacklist", feedbacklist);
return "allfeedbacks";
}
#RequestMapping(value = "/updatefb", method = RequestMethod.POST)
public String updatefbStatus(#Valid FeedbackList feedbacklist,
BindingResult
bindingResult, Model model) {
//feedbacklist is coming as NULL below
for (Feedback fb : feedbacklist.getMyfbList()) {
System.out.println(fb.getComment());
System.out.println(fb.getMessage());
System.out.println(fb.getStatus());
}
// Code to update the database with the new status and comment would go
// here
return "result";
}
The form is getting properly rendered on the UI when I fire the Get request, however, when I make some changes in the form and submit it ( POST ), feedbacklist is coming as NULL. Could anyone please guide me with this ?
To use a list inside a form with Thymeleaf is a little bit more tricky, you need to use an specific syntax, here i show you an example.
<tr th:each="feedback : ${feedbacklist.myfbList}">
<td th:field="*{myfbList[__${feedbackStat.index}__].message}">The
first name
</td>
...//Same for others fields
</tr>
In thymeleaf you have to use the Stat object to say the array position where you want to set the value, also as normal fields inside an object you have to use the '*' notation.

Spring MVC Form Processing

First of all: I'm a beginner in Spring and this is my first try to implement an web application with Spring MVC.
Here is what I've done yet:
Entities:
#Entity
#Table(name = "coins")
public class Coin
{
#Id
#GeneratedValue
private Integer id;
#OneToOne
private Country country;
private double value;
private int year;
}
#Entity
#Table(name = "countries")
public class Country
{
#Id
#GeneratedValue
private Integer id;
private String name;
}
Controller:
#Controller
public class CoinViewController {
#Autowired
private CoinService service;
#Autowired
private CountryService countryService;
#ModelAttribute("countries")
public List<Country> frequencies() {
return countryService.get();
}
#RequestMapping(value = "/coins/add", method = RequestMethod.GET)
public String addCoin(Model model) {
model.addAttribute("coin", new Coin());
return "coins/add";
}
#RequestMapping(value = "/coins/add", method = RequestMethod.POST)
public String addCoinResult(#ModelAttribute("coin") Coin coin, BindingResult result) {
// TODO: POST HANDLING
return "/coins/add";
}
}
JSP:
<form:form action="add" method="POST" modelAttribute="coin">
<div class="form-group">
<label for="country">Country:</label>
<form:select path="country" class="form-control" >
<form:option value="" label="-- Choose one--" />
<form:options items="${countries}" itemValue="id" itemLabel="name" />
</form:select>
</div>
<div class="form-group">
<label for="value">Value:</label>
<form:input path="value" class="form-control" />
</div>
<div class="form-group">
<label for="year">Year:</label>
<form:input path="year" class="form-control" />
</div>
<button type="submit" value="submit" class="btn btn-default">Erstellen</button>
</form:form>
But when I try to save the input from the JSP I always get this:
Field error in object 'coin' on field 'country': rejected value [1];
codes
[typeMismatch.coin.country,typeMismatch.country,typeMismatch.Country,typeMismatch];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [coin.country,country]; arguments []; default message
[country]]; default message [Failed to convert property value of type
'java.lang.String' to required type 'Country' for property 'country';
nested exception is java.lang.IllegalStateException: Cannot convert
value of type [java.lang.String] to required type [Country] for
property 'country': no matching editors or conversion strategy found]
So my questions are:
What should I use Editor / Converter?
How do I register one of them in my Controller?
You can register a custom editor into initBinder of your controller class:
#Controller
public class CoinViewController {
#Autowired
private CountryEditor countryEditor;
#InitBinder
protected void initBinder(final WebDataBinder binder, final Locale locale) {
binder.registerCustomEditor(Country.class, countryEditor);
}
......
}
(locale parameter is not needed in this case, but it can be useful if you need locale to make conversion - for example if you are working with dates)
and you can define your CountryEditor like the following:
#Component
public class CountryEditor extends PropertyEditorSupport {
#Autowired
private CountryService countryService;
#Override
public void setAsText(final String text) throws IllegalArgumentException {
try{
final Country country = countryService.findById(Long.parseLong(text));
setValue(cliente);
}catch(Exception e){
setValue(country);
// or handle your exception
}
}
}
I let spring handle injection of my editors with #Component annotation. So if you like to do in that way remember to enable package scan for that class!
Hope this help!

Categories