I use Hibernate Validation 4.3.1.Final and Spring 3.2.0.RELEASE
I have a form with a string attribute. This attribute is not required, but if it contains some value this value should be exactly 10 digits length.
So I need somthing like:
#NotRquired
#Length(min = 10, max = 10)
But there is no annotation like #NotRquired. How I should write validation for this case?
Not that a #NotRquired annotation would not help in this case, because all defined constraints are evaluated. So in the case where the value is not specified #Length would still be validated and fail. There are several things you could do:\
Use the #Pattern constraint and define a pattern which matters the empty string and the one of length 10 characters. Provided of course for not required case you get an empty string
Write a custom constraint, for example #EmptyOrLength which does what you want
Try working with validation groups and assign each constraint to a different group. You then need a way to target the right validation group
Revert to Hibernate Validator specific functionality and use boolean composition of constraints - http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#d0e3701. You still need a custom constraint though, but you can use a composition of #NotNull and #Length using #ConstraintComposition(OR)
Related
I have a class User as show below.
class User {
private String name;
private Integer percent;
}
I wanted to have validation on percent field using javax annotations such that it only allows 4 values. (10, 50, 80, 100)
I know #Min, #Max, #Digit, but not sure how they serve my case.
I tried searching on https://www.baeldung.com/javax-validation but did not find any useful for my case.
I know I can create a custom validator. But is there any other way I can do this using existing annotations.
TIA !
This is pretty much a non-answer, as you said
I know I can create a custom validator. But is there any other way I can do this using existing annotations.
There is no built-in validator that checks from a list of values. The closest would be #Pattern, but every implementation that I have seen only supports CharSequence. Custom validator route is probably the way to go. I bet someone has written one, so Google is your friend.
I'm writing REST service in Spring, and I would like to have some form validation.
Let's say, that my POJO class has Integer field - age. How to check, if value that came with request is really an Integer? Is there any annotation for that, or should I write some logic for that? I would like to add some comment if validation won't pass in same style like e.g. #NotNull(message = "Product Number cannot be empty.")
I'm writing a REST service using Spring Boot and JPA. I need to be able to validate some of the input fields and I want to ensure I'm using a proper pattern for doing so.
Let's assume I have the following model and I also have no control over the model:
{
"company" : "ACME"
"record_id" : "ACME-123"
"pin" : "12345"
"company_name" : ""
"record_type" : 0
"acl" : ['View','Modify']
"language" : "E"
}
The things I need to do are:
Ensure the value is not empty - This seems simple enough using the #NotEmpty annotation and I can pass a message.
Ensure the value is part of a valid list of values - The example here is the language property in the model above. I want the value to be either E,F or S. This seems possible using a custom annotation (eg #ValidValue({"E","F","S"})) but is there a better/"Springy" way to do this?
Ensure the values in a list are part of a valid list of values - The example here is the acl property. Again this seems possible with a custom annotation like #ValidListValues({"View", "Modify", "Delete", "Hide"}) but same question as above.
Set a default value - From what I read, custom validator annotations are only able to validate and not modify. I would like to do something like #DefaultValue(value=5) if the value is null. Is this possible? More on this below.
Set a default value to the return of a static method - For example if the pin field in model above isn't set, I want to set it to something like Util.getRandomDigitsAsString(5).
Use values from another property - I would like to validate that one property contains the string from another property. Using the example model, I want to ensure that record_id starts with company.
I have this setup in what I believe is a standard way with the controller -> service -> DTO -> DAO -> Model. Another option I was thinking about was creating a method in the validateCreate() that would go through all of the items above and throw an exception if needed.
Thanks.
Yes, NotEmpty is the right way
You should define a Language enum. The language field of your POJO should be of type Language
Same as 2. Define an Acl enum.
Define that in your Java code. Initialize the value of the field to 5 by default. If the JSON contains a value, Jackson will set the field value to the value in the JSON. Otherwise, it will stay as 5. Or initialize the field to null, and add a method getValueOrDefault(int defaultValue) that returns the default value you want if the value is null.
Same as 4
Define a custom validator that applies on the class itself, rather than a property of the class. In the validator chec that the two related values are correct.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-annotations-cacheable-key
The link above shows how to specify a key when all the parameters for a method are not required for the cache key which is the default. But how do you specify more than one param (but not all in the method arg list) as the key for the cache in the Cacheable annotation?
As you know, the key attribute of the #Cacheable annotation allows SpEL to be used. In all cases, the actual key used to access the underlying Cache must "evaluate" to a single value. Therefore, you must employ the power of SpEL to combine the #Cacheable method arguments that form the (unique) key.
By way of example, lets say we want to find a Book by Author, Edition and Title. I.e., we might have a #Cacheable method signature like so...
#Cacheable(cacheNames = "Books", key="#author.name + #edition + #title")
Book findBook(Author author, Integer edition, String title, boolean checkWarehous, boolean includeUsed) {
...
}
As you can see, the key is a combination and subset of the method arguments.
Literally any valid SpEL expression used to combine the #Cacheable method arguments for use as a key is applicable.
For complex keys where the individual components (such as Author, Edition, Title etc) of the key are each accessible, then it is best create a custom key class (e.g. BookKey) and use a custom Spring KeyGenerator (e.g. BookKeyGenerator) to generate the Cache key. Note, the method, target class and arguments of the #Cacheable method (e.g. findBook(author, edition, title, ...)) are each made available to your custom KeyGenerator.
Hope this helps.
0 param: key is 0
1 param: key is the param
2 or more params: key is hashCode(param0, param1, ...)
I wrote a custom Spring formatter (which implements the org.springframework.format.Formatter interface) for converting form input values to BigDecimal values for dollar inputs. The formatter does not accept values with more than two digits after the decimal place. In that case, a ParseException is thrown by the formatter's parse() method.
public class InputDollarBigDecimalFormatter
implements Formatter<BigDecimal>
{
#Override
public BigDecimal parse(String text, Locale locale)
throws ParseException
{
// ...
}
#Override
public String print(BigDecimal amount, Locale locale)
{
// ...
}
}
The formatter is registered so it can be applied using an annotation named #InputDollarBigDecimalFormat.
A field in my form-backing object, to which the formatter is applied, looks like this:
#InputDollarBigDecimalFormat
private BigDecimal price;
The formatter is working fine.
The problem is that when the ParseException is thrown, Spring still attempts to convert the input value to a BigDecimal using the default conversion. This means an input value of 100.123 is still successfully converted to a BigDecimal, even though my custom formatter throws a ParseException.
How can I prevent Spring from converting the input value to a BigDecimal when my custom Formatter has rejected the value by throwing a ParseException?
I'm using Spring 3.1.0.
Spring formatters are documented here.
UPDATE:
After stepping through the code with a debugger, I see that the logic for this is in the org.springframework.beans.TypeConverterDelegate class. Spring is clearly doing this on purpose, so I can see only the following possible solutions:
(1) If possible, un-register the default PropertyEditor for BigDecimal values. It appears Spring registers a org.springframework.beans.propertyeditors.CustomNumberEditor for converting a string to a BigDecimal. Of course, this solution has the downside that the default PropertyEditor would not be available for other BigDecimal fields (that do not use the custom formatter).
(2) Create a Dollar class that wraps a BigDecimal and change the custom formatter to work with the Dollar class. The type of field would also have to change to Dollar. This would be a bit of a nuisance when working with the values.
(3) Perhaps Spring has realized it is incorrect to fall back to the default PropertyEditor when a custom formatter has rejected a value and so this has been changed in a more recent version of Spring. I'm doubtful, but if anyone knows either way, your help would be appreciated.
(4) But perhaps the correct solution is to view the custom formatter as just helping out the default PropertyEditor by allowing more formats. In addtion to limiting the value to two decimal places, my custom formatter also allows for the input value to include a dollar sign and commas. I could leave that logic, but remove the decimal place restriction. Then I could add a custom bean validation (JSR 303) constraint that rejects the value if it does not have exactly two digits after the decimal place (unless it has none). I would just have to annotate my field with the constraint:
#InputDollarBigDecimalFormat
#WholeDollarOrCentsConstraint
private BigDecimal price;
Of course, I would also have to add an error message for the constraint, in addition to the one for the formatter.
If any of these seems like the correct solution to you, feel free to add an answer with your reasoning.
Here's what worked for me. If you have a look in TypeConverterDelegate, the very first thing it does (and also later along as part of the 'fallback conversion' bit) is that it checks if there is a custom PropertyEditor registered for the given type. If there is, it completely bypasses the conversionService part (which is what swallows your exception) and uses the editor instead.
So instead of your suggestion (1), you could try creating your own class that extends
PropertyEditorSupport and register it for BigDecimal (see here for an example of how to do that). You could then throw an IllegalArgumentException in your setAsText method.