Spring boot using placeholders in messages.properties - spring

I'm working with Spring Boot 2.1.5, and I'm using javax annotations to validate my class properties. So for example, I have:
class MyClass {
#NotEmpty(message = "{validation.notEmpty}")
private String company;
}
Then I have a messages.properties file that contains:
validation.notEmpty={0} is missing
I was hoping to get the error message "company is missing", but instead I'm getting "{0} is missing". What is the proper way of passing my variable name as placeholder?
Thanks.

You cannot achieve such a thing because #NotEmpty resolves the placeholder and get that value as message, it doesn't pass it to the placeholder to be added as parameter.
Look at the default message :
javax.validation.constraints.NotEmpty.message = must not be empty
You can just pass a string as value.
Validation messages are not default not designed to hold the field name. That is retrievable from the ConstraintViolation objects that provide (among other things) paths for each validation error.
So to achieve your requirement, what you could do is creating a custom annotation/validator with a second attribute :
#NotEmpty(message = "{validation.notEmpty}", field="company")
private String company;
But doing it for every kind of constraints you use looks an overhead.
Maybe just accept the duplication that is simple to refactor :
#NotEmpty(message = "car is missing")
private String company;
Note that you loses the benefit from locales, which may be undesirable if you handle multiple languages.
In this case, a more robust approach would be to use ConstraintViolations that has the path value of the field.
If my view doesn't address your issue, I encourage to post the question in the Hibernate Validator issues tracker.

Related

Creating internationalized standard javax.validation error messages

I have a small requirement of writing standardized, internationalized javax.validation error messages in this format
#NotNull
private String name;
#Max(10)
private int age;
Then in this case the error message should prop up as
"The field name is not null"
"The field age is not greater than or equal to 10"
How can I achieve this in a more dynamic way instead of hard-coding the message or variable name inside the annotation.
#NotNull(message = "{errors.name.missing.msg}")
private String name;
define the value for errors.name.missing.msg in messages.properties (which should be available in your classpath)
You can also make it locale specific by placing it in locale specific property files say in messages_en.properties and messages_fr.properties for i18n.

findById() gives No property Id found

I have three entities.
(I am using xxx as a place holder for this example)
I have set up all their #Entities with
#Entity
#Table(name = "xxx")
public class xxx {
#Id
#Column(name = "xxx_id")
int xxx_id;
However, I am able to do:
findById(int ...) for only ONE of the entities in their respective Repository.
When I try to do that for the other two entities, I get this problem:
"Invalid derived query! No property id found for type xxx!"
I am able to run it and get the proper results. But why am I getting this error for two entities but not the other?
I set up the entities and their repositories exactly the same way.
The ONLY difference is that in the entity whose repository does not return any errors, I am joining that entity with another entity whose repository fails, but I used "mappedBy" in the #JoinColumns section.
Could that have something to do with why that one has no problems?
How findBy... works?
Entity:
private int clientid;
private String firstname;
private String lastname;
Query:
findBy<Name_as_per_entity>
findByClientid(int clientid);
findByFirstnameAndLastname(String firstname, String lastname)
Solution
Because we treat the underscore character as a reserved character, we
strongly advise following standard Java naming conventions (that is,
not using underscores in property names but using camel case instead).
Doc
The underscore _ is a reserved character in Spring Data query derivation to potentially allow manual property path description.
Stick to the Java naming conventions of using camel-case for member variable names and everything will work as expected.
Also Refer this

Spring validate nested object with condition on parent

What I want to achieve might be a bit weird but sadly I cannot change that easily. I have a DTO that has a nested resource. The nested resource gets validated and the validation it gets differs based on an attribute of its parent.
class rootDto {
#NotEmpty
private String type;
#Valid
private AddressDto address;
// Other attributes follow ...
}
Now AddressDto looks like the following:
class AddressDto {
#NotEmpty(/* ONLY IF rootDto.type IS 'xxx' */)
private String name;
#NotEmpty(/* ONLY IF rootDto.type IS 'yyy' */)
private String street;
// Other attributes follow ...
}
I read about class level contrains (example answer) but I don't think that this is what I want. I could always create a Validator for the rootDto and inside that have the conditional validations for the AddressDto but that would be ugly and will grow way too much since AddressDto is not the only nested resource I have that requires such validation.
So my main question is, can I somehow expose to the nested resource attributes from its parent in order to use them for the validation? Is there an alternative I have not found/thought?
If you are using Hibernate Validator as Bean Validation provider and are happy to use a non standard feature, you can use Hibernate Validator's GroupSequenceProvider SPI - http://docs.jboss.org/hibernate/validator/5.2/reference/en-US/html_single/#__literal_groupsequenceprovider_literal
The idea would be to work with validation groups which you programmatically activate by using a GroupSequenceProvider on your RootDto.

Ignoring spring mvc JSR-303 validations for selective entity fields

I have spring4 mvc application to save an Address entity, code bit as follows.
My Controller
#RequestMapping(value = "addAddress", method = POST)
public String registerComplaint(#Valid #ModelAttribute final Address address, final BindingResult resultBinder) {
if (resultBinder.hasErrors())
return "addAddress";
addressService.addAddress(address);
return "redirect:myAddress";
}
My Entity
#Entity
#Table(name = "address")
public class Address {
#NotNull
private String street;
#NotNull
private String pin;
#NotNull
private String createdBy;
........
}
My form conatins only street and pin as field, where as createdBy should be set by me after validating the other form values.
Here the problem is spring JSR303 validation support is validating a field ie createdBy which i don't want to validate by spring mvc.
How can i instruct spring mvc not to validate these kind of optional fields while using #Valid annotation.
Is there any way i can skip fields like this using spring mvc ?
Validation is mainly for user input. Since you will be setting createdBy yourself, just do so before saving it (e.g #PrePersist), or have a new Date as a default value. If you need to enforce a constraint for createBy, you can do so at the schema level.
#Column(nullable=false, ...)
private String createdBy = new Date();
You need to read up on Validation Groups. This lets you use different validators depending on the "scenario"
Use Spring's #Validated annotation to use groups
If you don't protect the createdBy field, a user can change it by altering the POST variables. See DataBinder.setDisallowedFields()
Conceptually, how is a pin related to an address?
It sounds like you want to use a Form Backing Object here (a regular non-JPA POJO made just for a form), and copy values to your real entities.

Spring: Same object, different validation

I have an object called User where I save all the data of the User. I have some annotations to perform validation and it works fine.
public class User{
#NotEmpty
#Email
#Size(max=100)
#Column(name="username", length=100, nullable=false, unique=true)
private String username;
#NotEmpty
#Size(min=5, max=40)
#Column(name="password", length=40, nullable=false)
private String password;
#Size(min=5, max=40)
#Transient
private String newPassword;
// other attributes ,getters and setters
}
I have two different forms (each one in a different page). In the first one I ask for username and password to create the user, so both of them are compulsory.
In the second form I show the information about the user: username, other data (which will be validated as well) andthe password and newPassword. If the newPassword is set and the password is the same as the user has I'll change the user's password, but if they are left empty it means I shouldn't change the password.
The problem is that I have two forms relating to the same object where there is a different validation for the field password. In the first one it must be not empty, but in the second one it can be empty.
In the controller I validate the object in this way:
public String getUserDetails(#Valid #ModelAttribute("User") User user, BindingResult result, Model model){
if(result.hasErrors()){
//There have been errors
}
...
}
but is password is empty there will be an error.
Is there any way to perform a validation only in some fields of the object?
Can I, at least, remove any validation error after the validation?
What is the best practice in this case?
Thanks
You can use JSR-303 constraint groups to achieve this.
public class User {
public interface GroupNewUser {};
#NotEmpty
private String username;
#NotEmpty(groups = {GroupNewUser.class});
private String password;
// ...
}
Then in the controller, instead of using #Valid, use Spring's #Validated annotation which allows specifying a constraint group to apply.
See this blog post and this one also for more information.
There is also this superb post for Spring usage.
This is the problem with declarative validation, it's not very easy to do this sort of thing.
The easiest solution is to remove the validation annotations from the password field, and validate that field manually in the controller. The BindingResult class has methods on it for you to explicitly mark fields as invalid.
Another alternative would be to create two subclasses of the form class, each with its own password field, one with validation annotations, and one without, and use the appropriate one in the appropriate place.
A few pointers. I'm not sure they will be useful, though:
Spring docs say that:
you may call binder.setValidator(Validator) within a #Controller's #InitBinder callback. This allows you to configure a Validator instance per #Controller class
javax.validation has two *Context interfaces. Get into more details with them to see whether different validation can be achieved in different contexts.
instead of using hasErrors() function, use hasFieldErrors(fieldname) and only validate particular fields required by the form.

Resources