I have a simple User class and its properties are annotated with both #NotBland and lombok #NonNull.
#AllArgsConstructor
#Data
#Builder
public class User {
#NonNull
private String name;
#NonNull
private String id;
#NotBlank
private String address;
}
What i am expecting from this is when i try to set blank in address field it informs me at compile time or at least crash at runtime. Lombok #NonNull is working in that way and is raising NPEs at runtime.
I wrote the following test to check this
#Test
public void user_null_test(){
User user = User.builder()
.id("")
.name("")
.address("")
.build();
assertThat(user).isNotNull();
But the user is a valid user object with address being empty. Obviously i can use the following code to check for validation in my test
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(user);
assertThat(violations).isEmpty();
but i can't do that or want to write this type of code in my actual code base.
So my question is what is the use of #NotBlank if it does not raise any error at compile or run time if we are setting a property to as empty string when it is not supposed to be so according to the set annotation.
Related
I am calling a third party REST endpoint.
Request sample
{
"body": {
"accountNumber": "12345"
},
"header": {
"username": "someusername",
"password": "somepassword"
}
}
I have created 3 bean classes
MyRequest.java
#Builder
#JsonDeserialize(builder = MyRequest.MyRequestBuilder.class)
public class MyRequest {
#JsonProperty("header")
private MyHeader header;
#JsonProperty("body")
private MyBody body;
}
MyBody.java
#Getter
#Builder
public class MyBody {
private String accountNumber;
}
MyHeader.java
#Getter
#Builder
public class MyHeader {
private String username;
private String password;
}
I'm creating request object using
MyBody body = MyBody.builder().accountNumber("12345").build();
MyHeader header = MyHeader.builder().username("someusername").password("somepassword").build();
MyRequest request = MyRequest.builder().body(body).header(header).build();
Everything is working as expected. The code coverage for MyRequest.java is 100% but my MyBody.java and MyHeader.java is not.
For all the fields I get the error message "Not covered by tests".
Normally I add #Getter and #Setter for Response objects. For request, I just add #Builder annotation.
In this case, if I remove #Getter from MyBody and MyHeader, the third party REST endpoint is getting null values.
It looks like #Getter is invoked when setting the objects to MyRequest.java. But for some reason it is not covered by my test cases.
How to make this work without #Getter or is there a way to cover all the fields (accountNumber, username and password) with #Getter annotation? Any help is appreciated.
Create a lombok.config and add the following attribute to it.
lombok.addLombokGeneratedAnnotation = true
For Maven projects, a file named lombok.config at project’s basedir is the right spot, but other ways/locations may apply, see https://projectlombok.org/features/configuration
You need to instruct Jackson somehow which data should be included during serialization. The default mechanism is to use getters for that purpose.
Replace #Getter with #JsonProperty to get 100% code coverage.
#Builder
public class MyBody {
#JsonProperty
private String accountNumber;
}
This is not my answer. I got this answer from my other post
Why 3rd party REST API's gets null values for request fields when removing #Getter Lombok annotation
Thanks #Alexander Ivanchenko
I have the following #Builder - annotated class:
#Data
#Builder(access = AccessLevel.PUBLIC)
#Entity
public class LiteProduct
{
// Minimal information required by our application.
#Id
private Long id; // Unique
private String name;
private Long costInCents;
private String type;
// Model the product types as a Hash Set in case we end up with several
// and need fast retrieval.
public final static Set<String> PRODUCT_TYPES = new HashSet<>
(Arrays.asList("flower", "topical", "vaporizer", "edible", "pet"));
// Have to allow creation of products without args for Entities.
public LiteProduct()
{
}
public LiteProduct(#NonNull final Long id, #NonNull final String name,
#NonNull final String type, #NonNull final Long costInCents)
{
if(!PRODUCT_TYPES.contains(type))
{
throw new InvalidProductTypeException(type);
}
this.name = name;
this.id = id;
this.costInCents = costInCents;
}
Whenever I want to use the builder class that Lombok is purported to give me, despite the fact that the IDE seems to detect it just fine:
I get a compile-time error about its visibility:
I have looked at some workarounds such as this or this, and they all seem to imply that my problem ought to already be solved automatically and that Lombok by default produces public Builder classes. This does not seem to be implied from my output, and does not happen even after I put the parameter access=AccessLevel.PUBLIC in my #Builder annotation in LiteProduct. Any ideas? Is there something wrong with the fact that this class is also an #Entity? Something else I'm not detecting?
// Edit: I determined that when I move the class in the same package as the one I am calling the builder pattern from, it works just fine. This is not an #Entity issue, but a package visibility issue which based on what I'm reading should not be there.
The problem was that I was using the following line of code to create an instance of LiteProduct:
return new LiteProduct.builder().build();
instead of:
return LiteProduct.builder().build();
which is what the #Builder annotation allows you to do. Clearly builder() is like a factory method for Builders that already calls new for you.
I am experiencing some strange behaviour with Hibernate Validator 6.0.9.Final when validating an object. Given the following POJO:
public class Log {
private Long uid;
#javax.validation.constraints.NotEmpty
#javax.validation.constraints.NotNull
private String value;
private long reference;
private String type;
private String createdDt;
private String createdBy;
private Boolean actioned;
private Long dxr;
// Constructors..
// Getters and setters...
// Equals and hashcode overrides...
}
Now, if I validate the entity using a new instance JUnit test I get the following output:
#Test
public void test() {
Log cLog = new Log();
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator beanvalidator = validatorFactory.getValidator();
Set<ConstraintViolation<CommunicationLog>> constraintViolations = beanvalidator.validate(cLog);
System.out.println(constraintViolations);
}
[ConstraintViolationImpl{interpolatedMessage='must not be null', propertyPath=value, rootBeanClass=class Log, messageTemplate='{javax.validation.constraints.NotNull.message}'},
ConstraintViolationImpl{interpolatedMessage='must not be empty', propertyPath=value, rootBeanClass=class Log, messageTemplate='{javax.validation.constraints.NotEmpty.message}'}]
Which is fine. However, if I run this same code on a Hibernate/Jersey project, only the #NotNull validation runs. I cannot seem to get the #NotEmpty validator to run on the object.
I have tried removing the #NotNull validator (as the #NotEmpty validator takes care of it) but I left it on for completeness to demonstrate that the #NotEmpty validator is not returning anything in the image above.
I do not understand why it is not running when deployed to a web project but works fine when running under a JUnit test. Is there something I am missing here?
Okay, so I have resolved this. I hadn't realised that I had deployed the project to the wrong server. I had deployed it to a Payara 4.1.1.171 server instead of a Payara 5.181 server. However, I would have expected some kind of information to have been displayed - perhaps when deploying.
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.
I have two beans:
public class User {
#NotNull
public String name;
#NotNull(groups = {Default.class, ChangeCheckGroup.class})
public String password;
#Valid
public Details details;
}
and
public class Details {
#NotNull(groups = {Default.class, ChangeCheckGroup.class})
public String email;
}
I am using Hibernate Bean Validator to manually validate User bean.
When i am trying to validate bean using no validating groups at all, it works as expected.
Set<ConstraintViolation> cv = validator.validate( entitie );
It will check user.name, user.password, user.details.email.
But when i am trying to use validator with custom ChangeCheckGroup validating group, it ignores #Valid annotation.
Set<ConstraintViolation> cv = validator.validate( entitie, groups );
Will check only user.password, and there will no check of an user.details.email.
Is there any way to validate bean and beans it holds using validating groups?
The only way in which your scenario could skip the inner validations for the Detail class would be that the User object has the details field set to null. The Validator would still consider the details null value as valid (since you did not specify a #NotNull annotation specifically for that field in any of the groups).
So, the Bean Validation annotations inside the Detail class will be ignored. Once you properly initialize the attribute details inside User, it will be validated according to the indicated group.