In a Spring MVC application using hibernate, I am encountering an error when the user tries to specify the location (entity is FacilityAddress) of an appointment (entity is Encounter) using a JSP. Specifically, the property Encounter.location needs to be set by the JSP. The list of possible locations is given to the user by a model attribute praddrs, which is correctly populated in the JSP form by the GET method in the controller.
Can someone show me how to get this functionality to work properly without throwing an error?
I can create the 400 error when I add the following line of code to my JSP:
<form:select path="location" items="${praddrs}" size="3" style="min-width:200px"/>
The 400 error states:
The request sent by the client was syntactically incorrect.
There is no stack trace in the eclipse console.
I have read a number of other stack overflow postings on this, and have done google searches along with tinkering with my code, but I cannot seem to isolate the solution for my unique situation. Can someone show me how to get past this error?
Here is the form in the JSP:
<form:form modelAttribute="encounter" method="${method}" class="form-horizontal">
<div class="control-group" id="patient">
<label class="control-label">Patient </label>
<c:out value="${encounter.patient.firstName} ${encounter.patient.lastName}"/>
</div>
<div class="control-group" id="datetime">
<label class="control-label">Date and Time </label>
<joda:format value="${encounter.dateTime}" style="SM"/>
</div>
<div class="control-group">
<label class="control-label">Office</label>
<form:select path="location" items="${praddrs}" size="3" style="min-width:200px"/>
</div>
<petclinic:inputField label="Duration (mins)" name="numMins"/>
<td>
</td>
<div class="form-actions">
<c:choose>
<c:when test="${encounter['new']}">
<button type="submit">Add Encounter</button>
</c:when>
<c:otherwise>
<button type="submit">Update Encounter</button> <h3> Link to delete will go here.</h3>
</c:otherwise>
</c:choose>
</div>
</form:form>
Here is the controller method for handling the POST for this JSP:
//THIS IS THE METHOD THAT HANDLES THE FORM
#RequestMapping(value = "/patients/{patientId}/encounters/new", method = RequestMethod.POST)
public String processCreationForm(#ModelAttribute("encounter") Encounter encounter, #RequestParam("providerId") int providerId, BindingResult result, SessionStatus status) {
System.out.println("inside processCreationForm() ");
System.out.println("encounter.getDateTime() is: "+encounter.getDateTime());
new EncounterValidator().validate(encounter, result);
Provider myprovider = this.clinicService.findProviderById(providerId);
encounter.addProvider(myprovider);
if (result.hasErrors()) {
System.out.println("about to return errors. ");
return "encounters/createOrUpdateEncounterForm";
} else {
System.out.println("about to save encounter ");
this.clinicService.saveEncounter(encounter);
System.out.println("done saving encounter.");
status.setComplete();
System.out.println("finished status.setComplete()");
return "redirect:/encounters?encounterID={encounterId}";
}
}
For reference, the controller method for handling the JSP's GET is:
#RequestMapping(value = "/patients/{patientId}/encounters/new", method = RequestMethod.GET)
public String initCreationForm(#PathVariable("patientId") int patientId, #RequestParam("providerId") int prid, org.springframework.web.context.request.WebRequest webRequest, Map<String, Object> model) {
Patient patient = this.clinicService.findPatientById(patientId);
LocalDate theday = new LocalDate(webRequest.getParameter("day"));
LocalTime thetime = new LocalTime(webRequest.getParameter("time"));
DateTime thedatetime = theday.toDateTime(thetime);
this.clinicService.findFacilityAddressByProviderId(prid);
Encounter encounter = new Encounter();
encounter.setDateTime(thedatetime);
patient.addEncounter(encounter);
ArrayList<FacilityAddress> praddrs = (ArrayList<FacilityAddress>) this.clinicService.findFacilityAddressByProviderId(prid);
Provider pr = clinicService.findProviderById(prid);
model.put("encounter", encounter);
model.put("praddrs", praddrs);
model.put("pr", pr);
return "encounters/createOrUpdateEncounterForm";
}
I HAVE POSTED THE ENTITY CODE TO A FILE SHARING SITE SO THAT THIS POSTING CAN BE EASIER TO READ. You can read the entity code by clicking on the following links:
The Encounter entity code can be read at this link.
The FacilityAddress entity code can be read at this link.
The BaseEntity code can be read at this link.
How about changing "location" to "location.id"?
<form:select path="location.id" items="${praddrs}" size="3" style="min-width:200px"/>
Related
Below is code from a controller that I'm aiming to make sure it's receiving two input parameters (name and code) from a front-end interface.
It's a page that takes two parameters within a submit form, "name" and "code".
#RequestMapping(method = RequestMethod.POST)
public String transfer(#RequestParam(name = "name") String name,
#RequestParam(name = "code") String code,
Errors errors, RedirectAttributes redirectAttributes) {
if (errors.hasErrors()) {
return "htmlPageOne";
}
try {
User userToBeTransferred = usersRepository.findByName(name);
userToBeTransferred.setTransferred(true);
Region regionOfTransference = regionsRepository.findByCode(code);
regionOfTransference.setPopulationNumber(regionOfTransference.getPopulationNumber() + 1);
userToBeTransferred.setRegion(regionOfTransference);
usersRepository.save(userToBeTransferred);
regionsRepository.save(regionOfTransference);
return "redirect:/section/users/new";
} catch (IllegalArgumentException e) {
return "htmlPageOne";
}
}
The front-page form :
<form class="form-horizontal" method="POST" action="/section/users/new" th:object="${user}">
<input type="hidden" th:field="*{id}"/>
<div class="form-group row">
<label for="name" class="col-form-label">User name</label>
<input type="text" class="form-control" id="name" th:field="*{name}" name="name"/></div>
<div class="form-group row">
<label for="code" class="col-form-label">Code</label>
<input type="text" class="form-control" id="code" th:field="*{region.code}" name="code"/></div>
<button type="submit" class="btn btn-primary col-sm-6 ">Save</button>
</form>
For some reason, I'm getting the following error after I click to submit the form :
There was an unexpected error (type=Internal Server Error, status=500).
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments to which they apply: public java.lang.String
I'm not sure if I'm using the requestparams correctly, so maybe it's got something to do with this? I don't know, I've been stuck on this for a few hours now, so would appreciate if someone could help me.
I am building a MVC application using thymeleaf and Spring and Hibernate. My question here is more about hibernate than spring.
This is what i have so far.
A UI
<form role="form" th:action="#{/user/{userId}/official(userId=${userId})}" th:object="${user}" method="post">
<!-- first form group -->
<div class="form-group">
<label class="control-label col-xs-2">First Name</label>
<div class="col-xs-2">
<input type="text" class="form-control" th:field="*{firstName}" placeholder="First Name" />
</div>
<label class="control-label col-xs-2">Last Name</label>
<div class="col-xs-3">
<input type="text" class="form-control" th:field="*{lastName}" placeholder="Last Name" />
<!-- first form group end -->
</div>
<br/><br/>
<!-- third form group -->
<div class="form-group">
<label class="control-label col-xs-2">Email Address</label>
<div class="col-xs-2">
<input type="text" class="form-control" th:field="*{emailAddress}" placeholder="Email Address" />
</div>
</div>
<div class="form-group">
<div class="col-xs-2">
<input type="submit" value="Update" class="btn btn-primary" />
</form>
Controller :
#Controller
public class UserController {
#Autowired
private IUserService userServiceImpl;
#RequestMapping(value = "/user/{userId}/official", method = RequestMethod.GET)
public String getUserOfficialInfo(#PathVariable("userId") Integer userId, Model model) throws ServiceBusinessException {
UserBO userBO = userServiceImpl.findUserByUserId(userId);
model.addAttribute("user", userBO);
model.addAttribute("userId", userId);
model.addAttribute("genders", EnumSet.allOf(Gender.class));
return "official";
}
#RequestMapping(value = "/user/{userId}/official", method = RequestMethod.POST)
public String updateUserOfficialInfo(#PathVariable("userId") String userId, #ModelAttribute("user") UserBO user,BindingResult result, Model model) throws ServiceBusinessException {
userServiceImpl.updateUser(user);
UserBO userBO = userServiceImpl.findUserByUserId(Integer.parseInt(userId));
model.addAttribute("user", userBO);
model.addAttribute("userId", userId);
model.addAttribute("genders", EnumSet.allOf(Gender.class));
return "official";
}
}
DAO :
#Override
public void updateUser(UserEntity user) throws DaoException {
entityManager.merge(user);
}
The GET method in the controller, gets the user object to the view. But on the VIew i am just displaying few of those attributes of a user object in the form.
On the form Submit, the POST method in the controller gets called, which calls the service layer and then the merge method in the DAO gets executed.
Now what I have observed is that this merge method on the entity manager is updating the attributes which are not there in the form to null.
I think this is the expected behaviour since the object is detached when its called from the POST method. So the right thing to do here is to first fetch the entity object from the database and then to that object set the fields which are changed in the form and then call the merge method.
Can some one let me know if the above what I said is correct ?
If yes, then my next question would be that isnt this quite tedious and kind of bit more effort. I mean there are going to be cases where in I would not want to display the entire object in the form. Also not in hidden fields. I am quite surprise that there is no way to handle this and I have to follow the approach I just described above each time.
Is there a better way to do this ? Wouldn't i just use JDBC template instead ? I know I would be writing boiler plate code there but I am kind of writing getters and setters here as well for each round trip to the UI.
Consider annotating your entity with org.hibernate.annotations.Entity.
#Entity
#Table(name = "user")
#org.hibernate.annotations.Entity(
dynamicInsert = true, dynamicUpdate=true
)
public class User implements java.io.Serializable {
// your properties
}
If you are using a 4.x version of Hibernate, you may want to use #DynamicUpdate instead since the usage of #org.hibernate.annotations.Entity has been deprecated recently.
References:
https://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/
https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/annotations/Entity.html
https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/annotations/DynamicInsert.html
https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/annotations/DynamicUpdate.html
you can put the following code in a util class and invoke it when you want to fill your object based on another reference object:
public static <T> void fillNotNullFields(T reference, T source) {
try {
Arrays.asList(Introspector.getBeanInfo(reference.getClass(), Object.class)
.getPropertyDescriptors())
.stream()
.filter(pd -> Objects.nonNull(pd.getReadMethod()))
.forEach(pd -> {
try {
Object value = pd.getReadMethod().invoke(source);
if (value != null)
pd.getWriteMethod().invoke(reference, value);
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
So, you can do this on the service:
public UserBO updateUser(String userId, UserBO user ) {
UserBO reference = findOne(Integer.parseInt(userId));
fillNotNullFields(reference, user);
return updateUser(reference);
}
I found the suport for this answer here. Hope it helps.
You will need to store the unnecessary bean properties in hidden fields so that they get remapped when the form is posted to the controller. For example, if you do not want to show the date of birth on the page (assuming that date of birth is already an attribute of the user), simply add the following tag inside the form.
<input type='hidden' th:field="*{dateOfBirth}" />
I have made a text field and a submit button in my view in the admin page, and i want to do so the text i submit is shown below my textbox on the same page.
This is my controller for getting to the adminPage:
#RequestMapping(value = "/adminPage", method = RequestMethod.POST)
public String adminPage(Model model) {
return "adminPage";
}
this is what i have for my adminPage:
<form th:action="#{/adminPage}" method="post">
<textarea rows="4" cols="50">
</textarea>
<input type="submit" class="btn btn-lg btn-primary btn-block"
value="Submit Text"/>
</form>
I'm still very new at controllers and MVC in general, and i find it hard to use my knowledge in Java because Controllers doesn't look like any Java i've used before, so any help would be appreciated!
ok then, no Javascript. First, textarea needs a name attribute name="inputText".
That name will be used in the model object when your server method receives the request:
#RequestMapping(value = "/adminPage", method = RequestMethod.POST)
public String adminPage(#RequestParam("inputText") String input, Model model) {
//Do stuff
model.addAttribute("theText", input); //add the text which can be accessed on "adminPage"
return "adminPage";
}
then you can add a <div> in "adminPage.jsp" and append your text there, like:
<div>${theText}</div>
If, I have understood your problem correctly than here is the workaround that will do the job.
When user visits /adinPage from browser than input_data variable will be null and the if condition won't be executed.
The JSP page will be returned with second textarea as blank.
You have to use JSP because HTML pages can't be altered.
Controller.java
#RequestMapping(value = "/adminPage", method = RequestMethod.POST)
public String adminPage(#RequestParam(value = "input_data", required = false) String input_data,Model model)
{
if(input_data!=null)
model.addAttribute("output_data",input_data);
return "adminPage";
}
adminPage.jsp
<form th:action="#{/adminPage}" method="post">
<textarea id="input_data" rows="4" cols="50">
</textarea>
<textarea rows="4" cols="50">
${output_data}
</textarea>
<input type="submit" class="btn btn-lg btn-primary btn-block"
value="Submit Text"/>
</form>
When the user submits the form, the if condition will be executed and the returned view will contain the previously input data in second textarea
I haven't tested the code so, it might contain some syntax errors.
I currently have the following method within my controller that takes the users form input and passes it to an SQL query to return a list of matches.
#RequestMapping(value = "/resultsPage", method = RequestMethod.GET)
public ModelAndView newSearch(HttpServletRequest request)
{
int id = Integer.parseInt(request.getParameter("id"));
List<newobject> listSearch = newDAO.loadSearch(id);
ModelAndView model = new ModelAndView("results");
model.addObject("listSearch", listSearch);
return model;
}
I have added #NotNull to the ID filed in the newObject however I am unsure on how to modify my method above to check the variable entered (or not entered) by the user.
I am also unsure how to display the error on the html page. My existing code is below:
<form method="get" th:action="#{/results}">
<input id="search" name="id" class="search" placeholder="Ref..."
type="text" maxlength="10" title="Numerical values only" />
<button type="submit" method="post" style="display:none;" id="search">Search</button>
</form>
Can anyone give me some advice on how I would add the validation as I am just getting errors with everything I try.
I am using Spring MVC 3.1 to develop a Java web app. I have a JSP that has two paired radio buttons, an entry field, and a dropdown select box. I need these values to be available to my mapped controller, via a model class' fields.
The security and URL mapping works fine, as I've seen in debugger before. The issue is that when I tried to get the JSP data values populating my model, I get an error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'cccForm' available as request attribute
Here is part of my JSP:
<c:url var="cccUrl" value="/registers/default/ccPreauth/authorize" />
<div class="mainWrapper">
<form:form id="cccForm" action="${cccUrl}" method="post" modelAttribute="cccForm">
...
<table>
<tbody>
<tr>
<th>Select an option.</th>
</tr>
<tr>
<td>
<div class="field-input">
<form:radiobutton id="paymentOption" path="paymentOption" value="authorizeCC" />
Collect Credit Card Information
</div>
<div class="field-input">
Authorization Amount $
<form:input path="authAmount" maxlength="10" size="10" class="extendWidth"/>
<span class="instructions">
<spring:message code="label.authorization.note" />
</span>
</div>
<div class="field-input">
<form:radiobutton id="paymentOption" path="paymentOption" value="cancelAuth" />
Choose a Reason and Cancel Credit Card Collection
</div>
<div class="field-input right">
<form:select id="selectedReason" path="selectedReason" >
<c:forEach items="${reasonList}" var="reason">
<option value=${reason.reasonText}>${reason.reasonText}</option>
<br />
</c:forEach>
</form:select>
</div></td>
</tr>
</tbody>
</table>
</div>
<div class="right">
<button class="btnBlue" id="submitButton" type="submit">
Here is part of my controller:
#Controller
#RequestMapping(value = "/registers/default/ccPreauth")
#SessionAttributes(ControllerConstants.DEFAULT_REGISTER_ATTR_NM)
public class CCCaptureController {
...
#RequestMapping(value="/authorize" )
public ModelAndView authorize(
final Authentication auth,
final #ModelAttribute("ccCapturePaymentRequest") CCCapturePaymentForm ccCapturePaymentRequest,
final BindingResult result,
final HttpServletResponse response) {
final ModelAndView mav = new ModelAndView(CC_PREAUTH_PAYMENT_VIEW);
return mav;
}
and finally, here is my model class:
public class CCCapturePaymentForm implements Serializable {
private static final long serialVersionUID = 6839171190322687142L;
#NumberFormat(style = Style.CURRENCY)
private BigDecimal authAmount;
private String selectedReason;
private String paymentOption;
public BigDecimal getAuthAmount() {
return authAmount;
}
public void setAuthAmount(BigDecimal authAmount) {
this.authAmount = authAmount;
}
public String getSelectedReason() {
return selectedReason;
}
public void setSelectedReason(String selectedReason) {
this.selectedReason = selectedReason;
}
public String getPaymentOption() {
return paymentOption;
}
public void setPaymentOption(String paymentOption) {
this.paymentOption = paymentOption;
}
}
Can anyone tell me what I need to get this to work correctly? Please don't stop at just the reason for the exception above - please verify and correct my code as I am on a tight schedule as usual and have little experience with Spring MVC. Thanks!
You have this in your form:
modelAttribute="cccForm"
So you should have this in your controller:
#ModelAttribute("cccForm") CCCapturePaymentForm ccCapturePaymentRequest
That's how you bind the form backing object with the model attribute.
I found the answer for those Spring MVC newbies...
I had to set the "cccForm" to a new instance of my next page's form inside the controller search method (that then tries to being up the page that was getting the error).
In a nutshell: I had to set the empty backing bean value in the preceding controller method so that the follow-on method and JSP page have it to work with.
Hope this helps someone else avoid my mistake.
Mark