I built a form validation system using Spring mvc.
This is my object User
public class User{
#NotEmpty
#NotBlank
#Size(min=3,max=20)
private String name;
.....
}
So far, if you don't fill the "name" field you get 3 messages:
may not be empty
may not be blank
size must be between 3 and 20
Is there a way, for example, to get the message 2 just if the message 1 is not sent?
I explain better: if a user doesn't fill a field I just want to print the message "may not be empty". Then, if the user fills a field with just spaces I want to print the message "may not be blank".
Then, if the field is filled (then is not empty) and doesn't contain just spaces (then is not blank) I want to print the size message.
Is there a way to handle this issue with annotation or I have to create my own validator class?
I looked into it on the internet but I didn't find a solution, probably because it's difficult to explain Google what to look for.
You can use group and #GroupSequence to achive order with your validator:
http://docs.jboss.org/hibernate/validator/4.1/reference/en-US/html/validator-usingvalidator.html#validator-usingvalidator-validationgroups
You can create your own annotation and use the meta constraint #ReportAsSingleViolation.
have a look at these websites
http://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html/validator-customconstraints.html
http://docs.jboss.org/hibernate/validator/4.0.1/reference/en-US/html/validator-customconstraints.html
So your code could look
#ReportAsSingleViolation
#NotEmpty
#NotBlank
#Length(min = 3, max = 20)
public #interface CustomAnnotation {
public abstract String message() default "{customkey}";
public abstract Class<?>[] groups() default {};
public abstract Class<?>[] payload() default {};
}
Related
Given a username field which cannot be null, must be between 4 and 64 characters long and match the regexp [A-Za-z0-9]+, when the username field is null, the error message is simply: must not be null. The desired outcome is must not be null AND length must be between 4 and 64 characters AND must match "[A-Za-z0-9]+".
Initial setup:
#NotNull
#Length(min = 4, max = 64)
#Pattern(regexp = "[A-Za-z0-9]+")
String username;
What I also tried:
#NotNull(message = """
{jakarta.validation.constraints.NotNull.message}
AND {org.hibernate.validator.constraints.Length.message}
AND {jakarta.validation.constraints.Pattern.message}""")
#Length(min = 4, max = 64)
#Pattern(regexp = "[A-Za-z0-9]+")
String username;
But this latter's outcome is:
ConstraintViolationImpl{
interpolatedMessage='must not be null AND length must be between {min} and {max} AND must match "{regexp}"',
propertyPath=username, rootBeanClass=class app.User,
messageTemplate='{jakarta.validation.constraints.NotNull.message} AND {org.hibernate.validator.constraints.Length.message} AND {jakarta.validation.constraints.Pattern.message}'}
The values for the constraints (min, max, regexp) are not accessed. How to render the actual values of these in the error message?
Most constraint annotations, including #Length and #Pattern, regard null as valid input. That's why you won't get what you want by just using these annotations.
Fortunately, it's really easy to do what you want by introducing a new constraint annotation:
#Constraint(validatedBy = {}) // no validator needed, it delegates to other constraints
#NotNull
#Length(min = 4, max = 64)
#Pattern(regexp = "[A-Za-z0-9]+")
#ReportAsSingleViolation // to prevent separate violations for the other constraints
#Target(FIELD)
#Retention(RUNTIME)
public #interface ValidUsername {
String message() default """
{jakarta.validation.constraints.NotNull.message}
AND {org.hibernate.validator.constraints.Length.message}
AND {jakarta.validation.constraints.Pattern.message}""";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
This is annotated with your constraints, which means these are applied when this annotation is checked.
The message will still contain place holders {min} etc., because these are not properties of this constraint itself. You can solve that in two ways:
Don't use the templates but hard-code the message
Add these properties to your annotation as well, and tell the Bean Validation framework to use these for the other annotations:
#OverridesAttribute(constraint = Length.class)
int min() default 4;
#OverridesAttribute(constraint = Length.class)
int max() default 64;
#OverridesAttribute(constraint = Pattern.class)
String regexp() default "[A-Za-z0-9]+";
Now all you need to do is replace the annotations on the username field with #ValidUsername.
I have the following method in my controller:
#PostMapping("/register")
public String registerNewUser(#Valid User user, BindingResult result, Model model, RedirectAttributes redirectAttributes) {
System.out.println(result);
System.out.println(user);
if(result.hasErrors()) {
System.out.println("***ERROR***");
System.out.println(result.getAllErrors());
return result.getAllErrors().toString();
} else {
//userRepository.save(user);
System.out.println("user saved!");
return "user saved!";
}
}
And my user entity specifies:
#NonNull
#Column(nullable = false, unique = true)
#Valid
public String alias;
Now if I make a simple post request (I use the Advanced REST client for chrome extension) I get:
org.springframework.validation.BeanPropertyBindingResult: 0 errors
User(id=null, email=null, password=null, enabled=false, firstName=null, lastName=null, fullName=null null, alias=null, roles=[], links=[])
user saved!
Where it seems to validate despite #NonNull alias being null.
If I change #NonNull to #NotEmpty
Then validation works as expected:
[Field error in object 'user' on field 'alias': rejected value [null]; codes [NotEmpty.user.alias,NotEmpty.alias,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.alias,alias]; arguments []; default message [alias]]; default message [must not be empty]]
BUT what I don't understand is why #NonNull allows Nulls?
There's no #NonNull annotation in the JSR-303 validation API. The annotation is called #NotNull. Make sure you actually use the javax.validation.constraints.NotNull annotation to mark the field.
You should use NotNull from javax.validation package and not from lombok (those are to be deleted, when java starts supporting validation - see here). It validates the beans. More info here. You can also use hibernate's #NotNull from org.hibernate.validator. This also does validation.
javax.validation.constraints
#NotNull: The annotated element must not be null.Accepts any type
#NotEmpty: The annotated element must not be null nor empty. Supported types are:
CharSequence (length of character sequence is evaluated)
Collection (collection size is evaluated)
Map (map size is evaluated)
Array (array length is evaluated)
#NotBlank:The annotated element must not be null and must contain at least one non-whitespace character. Accepts CharSequence
#NonNull refer to Lombok
Here are the Great Details which you may like Click Here
I have some classes with which I'm exploring Hibernate. One of them has a name field, and I attempted to set the length of it with the following:
private String firstname;
#Column(length=25)
public String getFirstName() { return firstName; }
public void setFirstName(String first) { this.firstName = first; }
I did this with several fields, all in the same pattern -- I put the #Column on the property firstName instead of on the field firstName. I have read that this determines how the framework accesses your field's information -- either directly from the field, or from the getter/setter of the field.
The idea that length can be put in #Column in that position is confirmed somewhat in the documentation; in the Hibernate Reference Documentation, in section 5.1.4.1.4., "Declaring column attributes", it has the following lines:
#Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
so they put the length attribute on #Column in front of a getter.
The problem is that it doesn't work. My little test program adds a property for hbm2ddl.auto to "create", so that it drops everything and re-creates it; the configuration also echoes the generated SQL. With #Column in front of the getter, not the field, the generated field is VARCHAR(255), same as without #Column.
When I move #Column to be in front of the field instead, it is created with the designated length of 25.
Is this a bug, or am I missing something (else) about the configuration of Hibernate fields with annotations? Unfortunately I don't want the other attributes mentioned in the docs, and I would think it strange that you had to specify one of those to get length recognized anyway.
If you want to use mixed access mode (i.e. use annotations on fields for some properties and on getters for others) you have to take a few extra steps.
First, set the default access type for the entity. For example, this will set the default access type to FIELD:
#Entity
#Access(AccessType.FIELD)
public class MyEntity { … }
Then for the properties that you want to annotate on the getters, explicitly set the access type to PROPERTY:
#Access(AccessType.PROPERTY)
#Column(length=25)
public String getFirstName() { return firstName; }
I want to allow only positive integers for number fields including zero. How can I define this validation using JSR 303 ?
I tried
#Min(value=0 message = "msg1") - But it allows float values like 1.2.
#Digits(fraction = 0, integer = 10, message ="msg2") - It accepts negative values.
#Min(value=0, message = "msg1" )
#Digits(fraction = 0, integer = 10, message ="msg2") - It works fine but sometimes both the messages i.e. msg1 and msg2 are displayed.
Any suggestions?
Thanks!
Just use the annotation #Min in your bean:
#Min(value = 0L, message = "The value must be positive")
private Double value;
Looks like you are looking for natural numbers, I think you can use the regex pattern to get the desired output. Something like
#Pattern(regexp = "[\\s]*[0-9]*[1-9]+",message="msg")
If you use hibernate-validator then you may create a custom constraint which combines #Min and #Digits from the 3rd option by using #ConstraintComposition(AND). When you add #ReportAsSingleViolation, only a custom message will be shown.
You can use #Positive that works for the int and its wrappers too (ie Integer). This also will work for the BigInteger as well.
Its better to use range annotation like below for positive numbers
#Range(min = 0l, message = "Please select positive numbers Only")
For negative numbers
#Range(min = -9223372036854775808l, max = 0l, message = "Please select Negative numbers Only")
Another kind of solution and in my personal opinion cleaner and easier to read.
#Positive
#Digits(integer = 5, fraction = 0)
This is an example code from the answer https://stackoverflow.com/a/41675990/258544 above
#Documented
#Min(value=0, message = "add a min msg" )
#Digits(fraction = 0, integer = 10, message ="add a digit msg")
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {})
#ReportAsSingleViolation
public #interface NumberFormatValidator {
String message() default "invalid number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
It use Constraint composition http://docs.jboss.org/hibernate/validator/4.3/reference/en-US/html_single/#validator-customconstraints-compound and #ReportAsSingleViolation to avoid display both messages
Change the data time of your field from int to Integer and a message.properties file to set the message
Example: assuming your fieldName is myNumericField and belongs to a class called Test
Change the datatype:
change private int myNumericField;
to private Integer myNumericField;
Update getters and setters to use/return Integer
Create custom message.properties file.
This may appear to be more work but it's a more elegant and scalable solution rather than having the messages hardcoded in your annotations
Create a message.properties file under resources directory
Edit messages.properties and add the following line
typeMismatch.Test.myNumericField=Please use integers only
Remember to change Test and myNumericField for your class and field name
Edit your Application Context xml file and add the following tags before the <\beans> tag
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="resources/messages" />
</bean>
Using the following combination solved the issue for me:
#NotNull
#Range(min = 1)
#JsonProperty("end_range")
private Integer endRange;
Make sure to use Integer class instead of int for #NotNull to work.
I have created different java annotations which shouldn't be used together (something like #SmallTest, #MediumTest and #LargeTest. Is there a way to make the compiler not allow them being used together?
EDIT: more info to make my problem more explicit
Suppose I have:
public #interface SmallTest
{
String description();
}
public #interface MediumTest
{
String description();
ReasonToBeMedium reason(); //enum
int estimatedTimeToRun();
}
public #interface LargeTest
{
String description();
ReasonToBeLarge reason(); //enum
int estimatedTimeToRun();
}
Instead of creating three different annotations, you could create one annotation which takes an enum parameter, like #MyTest(TestSize.SMALL), #MyTest(TestSize.MEDIUM), or#MyTest(TestSize.LARGE).
Something like this (not tested, no guarantees, may cause abdominal distension, yadda yadda):
public #interface MyTest
{
TestSize value() default TestSize.MEDIUM;
}
Edit re: OP's comment "What if the annotation has a content itself, say "description"? And what if the content for each is different (say one has description, the other has estimatedTimeToRun)?"
It's not fantastically elegant, but you could lump all of the annotation elements in as well, with reasonable defaults for the optional elements.
public #interface MyTest
{
String description(); // required
TestSize size() default TestSize.MEDIUM; // optional
long estimatedTimeToRun default 1000; // optional
}
Then use it like:
#MyTest(description="A big test!") or
#MyTest(size=TestSize.SMALL, description="A teeny tiny test", estimatedTimeToRun = 10) or
#MyTest(description="A ho-hum test")