I'm currently revising a web application, and I have questions about data binding. I have a method that has been mapped with #RequestMapping, and in one of it's arguments I have a primitive integer type, something like this (the following code is basically a summary of my problem, not the actual code):
#RequestMapping(value = "/processSomething" , method = RequestMethod.GET)
public String processSomething(#ModelAttribute("myValue") int myValue)
{
// Do something with "myValue".
}
When I run the web application, i get the following:
HTTP Status 500 - Request processing failed; nested exception is
org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class [int]: No default constructor found; nested
exception is java.lang.NoSuchMethodException: int.()
It makes me realize that data binding works only with objects. I tried to change int with Integer, but I ended up getting something very similar:
HTTP Status 500 - Request processing failed; nested exception is
org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class [java.lang.Integer]: No default constructor
found; nested exception is java.lang.NoSuchMethodException:
java.lang.Integer.()
I know #ModelAttribute allows us to make data binding with the Spring MVC model, and if a model is not in there, it is created automatically by Spring and then it's returned. What am I doing wrong? What I forgot to do? Do I need to create a PropertyEditor for primitive types?
The funny thing is that it works perfectly with #RequestParam, but I would not want the user to see the value of my property in the URL.
NOTE: I'm currently using Spring Web MVC 4.1.1.RELEASE (with MAVEN)
UPDATE
I did what was suggested by tofindabhishek user. I created a class with the name Inteiro (translated as Integer), and I am using it as #ModelAttribute, just like this:
#RequestMapping(value = "/usuarios" , method = RequestMethod.GET)
public String getUsuarios(
Model model ,
#RequestParam("pag") int pagina ,
#ModelAttribute("total") Inteiro registros ,
#ModelAttribute("pesquisa") CriterioBuilder criterio ,
#ModelAttribute("id_sexo_f") Inteiro idSexF ,
#ModelAttribute("id_grupo_adm") Inteiro idGrpAdm )
{
// ...
}
The Inteiro class has basically just a single int primitive field with a public and empty constructor, and a set, get, equals and hashCode method. The previous problem appears to have been resolved, but when running my application, I came across this:
HTTP Status 500 - javax.el.ELException: Cannot convert
com.regra7.minhaapp.controle.wrap.Inteiro#3b of type class
com.regra7.minhaapp.controle.wrap.Inteiro to class java.lang.Long
Here's the Inteiro source code:
public class Inteiro
{
// #############################################################################################
// INSTÂNCIAS
// #############################################################################################
private int valor;
// #############################################################################################
// CONSTRUTORES
// #############################################################################################
public Inteiro()
{
this.valor = 0;
}
// #############################################################################################
// MODIFICADORES
// #############################################################################################
public void set(int valor) { this.valor = valor; }
// #############################################################################################
// ACESSO
// #############################################################################################
public int get() { return this.valor; }
// #############################################################################################
// EQUALS E HASHCODE
// #############################################################################################
#Override
public boolean equals(Object o)
{
if (o == null)
{
return false;
}
else if (o == this)
{
return true;
}
else if (o.getClass() != this.getClass())
{
return false;
}
Inteiro inteiro = (Inteiro) o;
return inteiro.get() == valor;
}
#Override
public int hashCode()
{
return valor;
}
}
For what reason Spring is complaining that can not convert Inteiro to java.lang.Long? I'm not working with Long. Moreover... EL? That would mean "Expression Language", right? Does this have something to do with some of my JSP pages? I am trying to develop a JSP page that displays search results, and on this page I use EL. Is there any possibility to be a problem in my JSP page?
Thank you for your help.
public class ViewModel {
private Integer myValue;
}
Use Wrapper object(ViewModel) to capture your value and Bind ViewModel class as model attribute in this case you can handle null values by using Wrapper(Integer).if you don't want to handle null values you can use int.
public class ViewModel {
private int myValue;
}
Related
I have been studying spring boot for a few weeks.
I am building a simple api using hibernate + jpa with a mysql database.
I have a resource call TvShowReminderResponseDTO :
public class TvShowReminderResponseDTO {
// Attributes
private Integer idTvShowReminder;
private User user;
private UserTvShow userTvShow;
private TvShowDetailsResponseDTO tvShowDetailsResponseDTO;
private Boolean completed;
private Integer currentSeason;
private Integer currentEpisode;
private Integer personalRating;
// rest of the code omittedfor brevity
}
In my controller i have a basic update PATCH endpoint, that receives the id of the tv show reminder (entity) that is stored in my database and also i receive a TvShowReminderPatchDTO with the information i want to update:
PatchDTO and Controller:
public class TvShowReminderPatchDTO {
// Attributes
private Optional<Boolean> completed;
private Optional<Integer> currentSeason;
private Optional<Integer> currentEpisode;
private Optional<Integer> personalRating;
// rest of the code omittedfor brevity
}
#PatchMapping("/{idTvShowReminder}")
public void updateTvShowReminder(#RequestBody #Valid TvShowReminderPatchDTO tvShowReminderToUpdate,
#PathVariable Integer idTvShowReminder){
tvShowReminderService.updateTvShowReminder(tvShowReminderToUpdate,idTvShowReminder);
}
Also I have my service method that is in charge of searching the TvShowReminder entity by its id, and then update the information we get from the client.
public void updateTvShowReminder(TvShowReminderPatchDTO tvShowReminderToUpdate, Integer idTvShowReminder) {
Optional<TvShowReminder> tvShowReminder = getTvShowReminder(idTvShowReminder);
TvShowReminder currentTvShowReminder = tvShowReminder.get();
if(tvShowReminderToUpdate.getCompleted() != null) {
if (tvShowReminderToUpdate.getCompleted().isPresent()) {
currentTvShowReminder.setCompleted(tvShowReminderToUpdate.getCompleted().get());
} else {
currentTvShowReminder.setCompleted(null);
}
}
if(tvShowReminderToUpdate.getCurrentSeason() != null) {
if (tvShowReminderToUpdate.getCurrentSeason().isPresent()) {
currentTvShowReminder.setCurrentSeason(tvShowReminderToUpdate.getCurrentSeason().get());
} else {
currentTvShowReminder.setCurrentSeason(null);
}
}
if(tvShowReminderToUpdate.getCurrentEpisode() != null) {
if (tvShowReminderToUpdate.getCurrentEpisode().isPresent()) {
currentTvShowReminder.setCurrentEpisode(tvShowReminderToUpdate.getCurrentEpisode().get());
} else {
currentTvShowReminder.setCurrentEpisode(null);
}
}
if(tvShowReminderToUpdate.getPersonalRating() != null) {
if (tvShowReminderToUpdate.getPersonalRating().isPresent()) {
currentTvShowReminder.setPersonalRating(tvShowReminderToUpdate.getPersonalRating().get());
} else {
currentTvShowReminder.setPersonalRating(null);
}
}
tvShowReminderRepository.save(currentTvShowReminder);
}
I have a question about the #valid annotation in the controller: i thought that it will check if the object that we send from postman for example is of type TvShowReminderPatchDTO , but i can send an entire different object and the controller will start its excecution, and the TvShowReminderPatchDTO will have all its attributes in NULL.
Whats the best way to check if the request body its in fact a TvShowReminderPatchDTO ?
I want to validate if the object we get from the Request is an instance of the TvShowReminderPatchDTO, and if not, throw an Exception.
The method that is doing the PATCH is working but its very ugly, I use optional as attributes in the TvShowReminderPatchDTO , so i can distinguish if the client wants to set a NULL (send an attribute with a null value ) or if the attribute was ommited (it does not appear on the request body) so we dont need to do anything, meaning we dont update it.
Can you guys recommend a better way to do this or improve the existing code?
Add some required fields using #NotNull annotation in your dto to help Spring understand which attributes should be present in your type
Don't use Optional. There is already JsonNullable for this purpose
public class TvShowReminderPatchDTO
{
#NotNull
private JsonNullable<Boolean> completed = JsonNullable.undefined();
}
And in controller method:
if (dto.getCompleted().isPresent()) {
object.setCompleted(dto.getCompleted().get());
}
That's it, no null-checks required, just set the value
I am using the dynamic columns feature, provided by primefaces library. Because I also have nested properties (such as bean.city.address), I made a custom BeanElResolver to deal with that:
public class ExtendedBeanELResolver extends BeanELResolver {
#Override
public Object getValue(ELContext context, Object base, Object property)
throws NullPointerException, PropertyNotFoundException, ELException
{
if (property == null || base == null || base instanceof ResourceBundle || base instanceof Map || base instanceof Collection) {
return null;
}
String propertyString = property.toString();
if (propertyString.contains(".")) {
Object value = base;
for (String propertyPart : propertyString.split("\\.")) {
value = super.getValue(context, value, propertyPart);
}
return value;
}
else {
return super.getValue(context, base, property);
}
}
It works fine for the bean properties, but when the page is rendered I'm getting PropertyNotFound Exception for some image in primefaces, that is not a bean property:
Servlet failed with an Exception -javax.el.PropertyNotFoundException: The class 'org.primefaces.application.PrimeResourceHandler' does not have the property 'primefaces- aristo:images/ui-bg_highlight-soft_100_c4c4c4_1x100.png'.
I think that is because I extended the BeanELResolver class and it somehow overrides the default ELResolver, that deals with all sorts of properties. I tried to make a CompositeELResolver class, to deal with other types of properties as well, but I can't figure out how. Any ideas?
Since upgrading my webapplication from Spring 3.0.5 to 3.1.1 I have to face some serious errors when validating my form-beans. My previously configured validator(s) doesn't work any more like they should. The problem is that the method getFieldValue(String fieldname) from Class org.springframework.validation.Errors does not return the original binded bean value like it should (and was before).
This is what my form-bean looks like:
public class PersonBean extends BaseFormBean {
private String firstname; // getters and setter omitted...
private String lastname; // getters and setter omitted...
private Integer age; // getters and setter omitted...
public PersonBean() {}
#Override
public void validateForm(Errors errors) {
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectInvalidIntValue(errors, "age", "validator.std.age", "invalid age", false);
}
}
The WebValidationUtils-class that gets invoked has some standard methods for checking bean properties. The error occurcs only on non-String values, like the property age which is of type Integer. It also happens on Collection(s).
The following snippet shows how Integer values are validated in my utils-class:
import org.springframework.validation.Errors;
...
public abstract class WebValidationUtils {
...
public static void rejectInvalidIntValue(Errors errors, String field, String errorCode, String defaultMessage){
Assert.notNull(errors, "Errors object must not be null");
Object value = errors.getFieldValue(field); // returns the string value (type: java.lang.String)
Class<?> fieldType = errors.getFieldType(field); // returns the class Integer!
if (value == null || !value.getClass().equals(Integer.class) || ((Integer)value).intValue() <= 0){
errors.rejectValue(field, errorCode, null, defaultMessage);
}
}
}
The bean itself has the correct value bound...
Do I have to configure some additonal spring beans in my context-servlet.xml do achieve the same bevahior like it was in 3.0.5?
Edit: The official Spring Doku for the method "getFieldValue(...)" says
Return the current value of the given field, either the current bean property value or a rejected update from the last binding.
So I don't have a clue why this method returns a String-value instead of the propagated bean value of type Integer...
Seem like you have a binding error so getFieldValue() return FieldError.getFieldValue() that return the value that causes the binding error. This is the expected behavior.
You can get the value that hold the property using getRawFieldValue() instead. This method always return the value using the PropertyAccessor.
I'm using BeanUtils to manipulate Java objects created via JAXB, and I've run into an interesting issue. Sometimes, JAXB will create a Java object like this:
public class Bean {
protected Boolean happy;
public Boolean isHappy() {
return happy;
}
public void setHappy(Boolean happy) {
this.happy = happy;
}
}
The following code works just fine:
Bean bean = new Bean();
BeanUtils.setProperty(bean, "happy", true);
However, attempting to get the happy property like so:
Bean bean = new Bean();
BeanUtils.getProperty(bean, "happy");
Results in this exception:
Exception in thread "main" java.lang.NoSuchMethodException: Property 'happy' has no getter method in class 'class Bean'
Changing everything to a primitive boolean allows both the set and get call to work. I don't have this option, however, since these are generated classes. I assume this happens because the Java Bean libraries only consider an is<name> method to represent a property if the return type is a primitive boolean, and not the wrapper type Boolean. Does anyone have a suggestion as to how to access properties like these through BeanUtils? Is there some kind of workaround I can use?
Finally I've found legal confirmation:
8.3.2 Boolean properties
In addition, for boolean properties, we allow a getter method to match the pattern:
public boolean is<PropertyName>();
From JavaBeans specification. Are you sure you haven't came across JAXB-131 bug?
Workaround to handle Boolean isFooBar() case with BeanUtils
Create new BeanIntrospector
private static class BooleanIntrospector implements BeanIntrospector{
#Override
public void introspect(IntrospectionContext icontext) throws IntrospectionException {
for (Method m : icontext.getTargetClass().getMethods()) {
if (m.getName().startsWith("is") && Boolean.class.equals(m.getReturnType())) {
String propertyName = getPropertyName(m);
PropertyDescriptor pd = icontext.getPropertyDescriptor(propertyName);
if (pd == null)
icontext.addPropertyDescriptor(new PropertyDescriptor(propertyName, m, getWriteMethod(icontext.getTargetClass(), propertyName)));
else if (pd.getReadMethod() == null)
pd.setReadMethod(m);
}
}
}
private String getPropertyName(Method m){
return WordUtils.uncapitalize(m.getName().substring(2, m.getName().length()));
}
private Method getWriteMethod(Class<?> clazz, String propertyName){
try {
return clazz.getMethod("get" + WordUtils.capitalize(propertyName));
} catch (NoSuchMethodException e) {
return null;
}
}
}
Register BooleanIntrospector:
BeanUtilsBean.getInstance().getPropertyUtils().addBeanIntrospector(new BooleanIntrospector());
you can just create second getter with SET - sufix as workaround :)
I am setting up a form using JSF (I'm pretty new at this) and I am getting a Validation Error: Value is not valid message on one of the fields. This field is actually a separate object (as I will show below) that has a custom converter.
Here is what I have (with non-relevant code removed):
I have a Citation class:
#ManagedBean(name="citation")
public class Citation {
private int id;
private Status status;
// getters and setters
}
I also have a Status class that you see referenced in the Citation class:
#ManagedBean(name="status")
public class Status {
private int id;
private String name;
// getters and setters
public List<Status> getAllStatuses() {
Session session = HibernateUtil.getCurrentSession();
session.beginTransaction();
session.clear();
Query query = session.createQuery("from Status");
List<Status> statuses = query.list();
try {
session.getTransaction().commit();
} catch (HibernateException e) {
// TODO: handle exception
session.getTransaction().rollback();
}
return statuses;
}
#Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof Status)) return false;
if (this.id == ((Status)obj).getId()) {
return true;
} else {
return false;
}
}
#Override
public int hashCode() {
return this.name.hashCode();
}
}
Then for my form, I have:
<h:selectOneMenu id="citation_status" value="#{citation.status}">
<f:selectItems value="#{status.allStatuses} var="s" itemValue="#{s.id}" itemLabel="#{s.name}" />
</h:selectOneMenu>
<h:message for="citation_status" />
Lastly, for my converter, I have:
#FacesConverter(forClass=Status.class)
public class StatusConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// uses Hibernate to get the Status object (using a breakpoint in Eclipse, I have verified that this works)
// I can post this code if needed, but just trying to keep it short :)
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return String.valueOf(((Status) value).getId());
}
}
Now when I get to my form and submit, I get the Validation Error next to the Status. I'm pretty new at this and thanks to #BalusC, I'm this far along.
Any help is greatly appreciated.
Validation Error: Value is not valid
In case of <h:selectOneMenu>, you will get this when error whenever the selected item does not match any of the items available in the list. I.e. selectedItem.equals(selectItem) has never returned true for any of the items.
Since it's apparently a custom object (the Status class), did you implement its Object#equals() (and #hashCode()) properly? You can if necessary let the IDE (Eclipse/Netbeans) autogenerate them.
See also:
Overriding equals and hashCode in Java
How to implement equals() in beans/entities
Update: after having a closer look at your code, it turns out that you're actually submitting #{s.id} instead of #{s} (the whole Status object). Fix the itemValue accordingly and it should work (if equals() is still doing its job properly).