I have a simple method to get a list of documents for a given companyId. Here is the method:
#Override
public List<Documents> getDocumentList(#NotNull Integer companyId) {
Company company = new Company(companyId);
return this.documentRepository.findByCompany(company);
}
I wanted to use Javax validation constraints to ensure that the companyId being passed in, is not null. But it seems to not have any effect, as I'm able to pass in a null value, and it flows down to the findByCompany call on the repository. I also added #Valid before #NotNull to force validation, but that too didn't do anything.
I could always write a couple of lines to check for a null value, but wanted to use javax.validation annotations to make the code more readable and concise. Is there a way to make the annotations work on method params?
To activate parameter validation, simply annotate the class with #Validated
import org.springframework.validation.annotation.Validated;
From The Java EE 6 Tutorial:
The Bean Validation model is supported by constraints in the form of
annotations placed on a field, method, or class of a JavaBeans
component, such as a managed bean.
You should place your validation of a field related to a declared bean, something like this:
#Entity
#Table(name="users")
public class BackgammonUser {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long userId;
#Column(name="username")
#NotBlank
private String userName;
#NotBlank
private String password;
#NotNull
private Boolean enabled;
}
The BackgammonUser is considered to be a bean.
If you #Inject a class with your method, its working as expected.
#Stateless
public class MyBean{
#Inject
TestClass test;
}
and
public class TestClass {
public List<Documents> getDocumentList(#NotNull Integer companyId)
{
//...
}
}
ConstraintViolationException when you call your method with null parameter:
WFLYEJB0034: EJB Invocation failed on component MyBean for method ...:
javax.ejb.EJBException: javax.validation.ConstraintViolationException:
1 constraint violation(s) occurred during method validation.
#NotNull Annotation,
A method should not return null.
A variable (like fields, local variables, and parameters) cannot hold null value.
Related
I want to use javax validation on poco objects that contain complex types. In my code, I want to validate the PersonDetail object inside my Person class. If I don't use the #Valid PersonDetail, then validations on that subclass don't work.
Is there any way to validate nested objects without the #Valid annotation on each one?
public class Person {
#Pattern(regexp = "^[a-zA-Z]+$")
private String surname;
#Valid(//without this personDetails validations not worked)
private PersonDetail personDetail;
....
PersonDetail class
public class PersonDetail {
#Pattern(regexp = "^[a-zA-Z]+$")
private String surname2;
public String getSurname2() {
return surname2;
}
No, you need #Valid on the personDetail field in order for validation to continue to look down into that field. You can configure this in other ways (validation.xml), but ultimately you need to tell the Validator to descend into the value of the personDetail field.
Suppose I have a class, what is the order of validation in a SpringBoot class object. After an object gets created then the fields are populated or does the validation happens before the objects are populated, at the time of setting of the field values this validation happens. Or after the object is created then by a get call we validate the object field values.
package com.bablo.google.request;
import java.io.Serializable;
import java.util.Set;
import javax.validation.constraints.NotNull;
public class SomeRequest implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#NotNull
private Long userId;
private String resNote; //this is not annotated with #NotNull
#NotNull
private String revTag;
public Long getUserId() {
return userId;
}
public void setUserId(final Long userId) {
this.userId = userId;
}
public String getResNote() {
return responseNote;
}
public void setResNote(final String resNote) {
this.resNote = resNote.trim(); //Call to trim() method of the String object.
}
public String getRevTag() {
return revTag;
}
public void setRevTag(final String revTag) {
this.revTag = revTag.trim(); //Call to the trim() method of the String object.
}
}
What is the way that validation will happen in a class? What is the mechanism of validating the fields, does the #NotNull validation or for that matter any validation depends on the getter methods to do the validation?
Do they first call the setter methods to do the validation?
Splitting your questions and adding answers.
What is the order of validation in a SpringBoot class object?
Validation happens as part of data binding process. Every request parameter/path variable will be validated as per the marked annotation and only when the validation passes, the value will be assigned to the class object.
What is the way that validation will happen in a class?
Validation process differs for each binding mechanism. If the method parameter is ModelAttribute/request parameter/path variable/Map etc. Spring uses different argument resolvers for each method parameter. If #Valid is added, then it enables validation during argument resolution process (Look out for RequestMappingHandlerAdapter where the whole magic is wired).
Does the #NotNull validation or for that matter any validation depends on the getter methods to do the validation? Do they first call the setter methods to do the validation?
Spring uses reflection to construct/validate the method argument class. Data binding and validation happens even without getters/setters.
You can validate manually by calling
#Autowired
private javax.validation.Validator validator;
...
validator.validate(new SomeRequest()); // you can also pass method's argument
or you can use auto validation
Here is an example https://www.baeldung.com/spring-boot-bean-validation of using #Valid + #ExceptionHandler
Here is an example https://spring.io/guides/gs/validating-form-input/ of using #Valid + BindingResult
I'm trying to improve and simplify part of my code using Java Validation constraint (#NonNull, #Min, etc...) but there is one recurrent case in my code where I can't figure out how to use constraint annotation.
Here is an example:
public class ResourceIdentifier {
public enum ResourceType { ARTICLE, USER, COMMENT }
private #Getter #Setter String id;
private #Getter #Setter ResourceType type;
}
Then I would like to validate MyCommand object so resourceId is not null and resourceId.type can only be ARTICLE or COMMENT.
public class MyCommand {
#NotNull
#Validate(path="#resourceId.type", values={ResourceIdentifier.ResourceType.ARTICLE, ResourceIdentifier.ResourceType.COMMENT})
private ResourceIdentifier resourceId;
(...)
}
I believe I can achieve this with a custom constraint validation annotation and reflection.
Is there any other simple way ?
EDIT: Imagine I have 10-20 others Command class requiring the type same validation resourceId.type = {}
You can just use an assertion constraint (this is a method inside MyCommand):
#AssertTrue(message="Only Comment and Article are allowed as resource type")
public boolean isResourceIdValid() {
return this.resourceId.getType() == ResourceIdentifier.ResourceType.ARTICLE
|| this.resourceId.getType() == ResourceIdentifier.ResourceType.COMMENT;
}
I have a configuration class like below. All of fields in the inner class OptionalServiceConfigs has a default value as annotated using #Value as shown in below.
Sometimes in my application.properties file, it does not have a single service prefixed property. In that case, we want to have loaded an OptionalServiceConfigs instance with its default field values.
#Configuration
#ConfigurationProperties(prefix = "myconf")
public class MyConfigs {
// ... rest of my configs
#Value("${service:?????}") // what to put here, or can I?
private OptionalServiceConfigs service; // this is null
// In this class all fields have a default value.
public static class OptionalServiceConfigs {
#Value("${mode:local}")
private String mode;
#Value("${timeout:30000}")
private long timeout;
// ... rest of getter and setters
}
// ... rest of getter and setters
}
But unfortunately, the service field is null when it is accessed using its getter method. Because spring boot does not initialize an instance of it when there is no property keys found with prefixed myconf.service.* in my application.properties file.
Question:
How can I make service field to initialize to a new instance along with its specified default field values when there are no corresponding prefixed keys in properties file?
I can't imagine a value to put in annotation #Value("${service:?????}") for service field.
Nothing works, tried, #Value("${service:}") or #Value("${service:new")
Based on #M. Deinum's advice, did some changes to configuration class. I am a newbie to Spring and it seems I have misunderstood how Spring works behind-the-scenes.
First I removed all #Value annotation from inner class (i.e. OptionalServiceConfigs), and as well as service field in MyConfigs class.
Then, initialized all inner class fields with their default values inline.
In the constructor of MyConfigs, I initialized a new instance of OptionalServiceConfigs for the field service.
By doing this, whenever there is no service related keys in my application.properties a new instance has already been created with default values.
When there is/are service related key/s, then Spring does override my default values to the specified values in application.properties only the field(s) I've specified.
I believe from Spring perspective that there is no way it can know in advance that a referencing field (i.e. service field) would be related to the configurations, when none of its keys exist in the configuration file. That must be the reason why Spring does not initialize it. Fair enough.
Complete solution:
#Configuration
#ConfigurationProperties(prefix = "myconf")
public class MyConfigs {
// ... rest of my configs
private OptionalServiceConfigs service;
public static class OptionalServiceConfigs {
private String mode = "local";
private long timeout = 30000L;
// ... rest of getter and setters
}
public MyConfigs() {
service = new OptionalServiceConfigs();
}
// ... rest of getter and setters
}
you can try such a structure which works for me quite fine:
#Data
#Validated
#ConfigurationProperties(prefix = "gateway.auth")
#Configuration
public class AuthProperties {
#NotNull
private URL apiUrl;
#Valid
#NotNull
private Authentication authentication;
#Data
public static class Authentication {
#NotNull
private Duration accessTokenTtl;
#NotNull
private String accessTokenUri;
#NotNull
private String clientId;
#NotNull
private String clientSecret;
#NotNull
private String username;
#NotNull
private String password;
#Min(0)
#NonNull
private Integer retries = 0;
}
}
Important is to have getters and setters in order to enable Spring to postprocess ConfigurationProperties, I am using Lombok (#Data) for this.
please see here for more details:
Baeldung ConfigurationProperties Tutorial
I have a Spring MVC project using JPA which I have worked on for some time in the past without this issue. But now for some reason (likely an environmental issue as I have switch to a new laptop since I last worked on it) I am getting this weird error.
The project is essentially a tool for creating and performing surveys which are just a set of questions. There are multiple types of question such as "auto complete question", "multiple choice question", "integer question", etc which collect different types of data. Each of this question types is modeled by a subclass which extends an abstract class called DdmQuestion which looks something like this:
#Entity
#Table(name = "ddm_question")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "question_type")
#JsonIgnoreProperties({"dataType"})
#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#question_type")
#JsonSubTypes(value = { #Type(DdmTextQuestion.class),#Type(DdmDateQuestion.class),#Type(DdmTimeQuestion.class),#Type(DdmNumberIntegerQuestion.class),#Type(DdmChoiceMultiQuestion.class),#Type(DdmAutoCompleteQuestion.class) })
public abstract class DdmQuestion {
#Id
#GeneratedValue
#Column(name = "question_id")
private int questionId;
#Column(name = "name")
private String name;
public int getQuestionId() {
return questionId;
}
public void setQuestionId(int questionId) {
this.questionId = questionId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonIgnore
public abstract String getDataType();
}
Note the getDataType() method.
Then, for each question type, I have a subclass extending this which looks something like this:
#Entity
#DiscriminatorValue("ddm_question_date")
public class DdmDateQuestion extends DdmQuestion {
final private String DATA_TYPE = "Long"; // this is the line with the error
#Override
public String getDataType() {
return DATA_TYPE;
}
}
Now, I've never encountered this error before (that I can recall) but Eclipse is throwing up an error here that says:
"The Java field for attribute "DATA_TYPE" is final". That's all it
says.
If I remove the #Entity annotation from the class, this error disappears so evidently something in JPA doesn't like something about this but I never had this error before so I'm thinking something changed in a newer version. My POM is not particularly explicit with dependency versions so this would not be surprising.
Can anyone explain to me why this is happening and what the correct resolution is? I could just remove the "final" from the field declaration but this seems wrong to me as it is definitely a final value...
Thanks in advance.
If it is a field that should not be persisted in the database you usually should take advantage of the transient annotation which would tell the persistence provider to ommit that field in its processing.:
#Transient
final private String DATA_TYPE = "Long";
If Eclipse is smart enough, it should stop highlighting the error altogether.
in this linkshttp://docs.oracle.com/javaee/5/tutorial/doc/bnbqa.html#Entities;
An entity class must follow these requirements:
The class must be annotated with the javax.persistence.Entity annotation.
The class must have a public or protected, no-argument constructor. The class may have other constructors.
The class must not be declared final. No methods or persistent instance variables must be declared final.
If an entity instance be passed by value as a detached object, such as through a session bean’s remote business interface, the class must implement the Serializable interface.
Entities may extend both entity and non-entity classes, and non-entity classes may extend entity classes.
Persistent instance variables must be declared private, protected, or package-private, and can only be accessed directly by the entity class’s methods. Clients must access the entity’s state through accessor or business methods.