I am using Hibernate validators to run validation constraints defined by javax.validation API. Everything runs on the Spring 3.
I defined my custom constraints:
#ProjectExists
#ProjectActive
#ProjectCommentable
and my custom validators that covers above constraints.
Now I would like to validate following DTO:
public class Comment {
private String content;
#ProjectExists
#ProjectActive
#ProjectCommentable
private String projectName;
}
The problem is that each validator has to run database query, find project and do something with it.
I am looking for solution which allows to cache project that was found before. The best solution would be to share something like "validation context" between all validators, so that I could set Project to validation context.
Next problem is that after validation DTO is transformed to data model, so the "find project query" has to be called once again. It would be good to reuse cached project also here.
Related
We are using Swagger to model our API with Spring annotations:
#Operation(summary = "Creates a post for given user.")
#PostMapping("/post")
open fun createPost(
#RequestParam("userId") user: User,
)
The issue we are having is that Swagger does not know there is a logic behind and we are only passing userId: Long for which the user is loaded by Hibernate.
The model of User contains several #OneToOne, #ManyToOne, #OneToMany relations to other entities and Swagger builds the model of User with all of them. This causes the model to be huge and some of our Swagger docs wouldn't even load in the browser as the model is in the size of megabytes.
Is there a way to tell Swagger:
to ignore specific entity/entities
to enforce different type (in this case Long)
Ideally something like:
#Operation(summary = "Creates a post for given user.")
#PostMapping("/post")
open fun createPost(
#SwaggerType(Long::class)
#RequestParam("userId")
user: User,
)
The cleanest way is to use Springfox with an alternate type rule. See no 10 in the examples given here:
https://springfox.github.io/springfox/docs/current/#springfox-spring-mvc-and-spring-boot
This enables you to completely replace your User class by any other (fake) class that you want to show to the Swagger user, without polluting your model with workarounds - but still staying transparent in your code.
There are a few options that can be tried:
Using inheritance with User Model, you can just define a SuperClass-childClass mapping with User class only containing the userId, and the child class that inherits from it will be holding the other attributes for you. In this way, the input will only be the userId with minimal effort.
Using JsonIgnore, but this works really well while returning the response.
With OpenAPI, Swagger has introduced the capability to use specific properties from the request class. More can be read from
https://swagger.io/docs/specification/describing-request-body/
you can use two different class,a basic class and a senior class,and the senior extends the basic,use basic class in API;
or you can use #JsonIgnore if you don't want to show the field ,like:
#JsonIgnore
private String name;
Beacuse Swagger use jackson to json, if you shield the field by jackson,it will not appear.
In swagger
you can use #ApiModelProperty(hidden = true),This is the perfect way
everyone.
So, I have a SpringBoot application with a controller that has several methods, taking the following POJO as a parameter:
package com.example.dto;
import lombok.Data;
#Data
public class MyEntity {
#NotNull
private String fieldA;
private String fieldB;
}
For one of the controller endpoints I would like to apply additional validation logic, so in the validation.xml I add the following:
<constraint-mappings>
<bean class="com.example.controller.SampleController" ignore-annotations="false">
<method name="doFoo">
<parameter type="com.example.dto.MyEntity">
<valid />
<constraint annotation="com.example.validation.ValidEntity" />
</parameter>
</method>
</bean>
</constraint-mappings>
com.example.validation.ValidEntity is the constraint annotation I would like to apply.
My problem is that this additional constraint is only invoked if #NotNull checks defined in MyEntity have passed successfully. If fieldA is null, ValidEntity constraint is ignored, and the client receives an imcomplete validation result. What am I missing?
I'm not entirely sure about this because I've never worked with the validation.xml file.
However, I would say that Spring is first creating the object and then applying the validations. The #NotNull validation is performed in the creation of the instance. This means that if that validation fails the construction will throw an exception and Spring won't even try to check your constraint (which makes sense in my opinion).
I think you can "fix" it by creating an annotation with your constraint and using it in your class. If I'm right, both annotations will be checked and the thrown exception will contain all errors.
It's just a guess. Let me know if it works.
I don't know if there is an easy way to configure the validator to aggregate constraint violations from both annotation and XML configurations when first or both fails.
As demonstrated by your code Hibernate Validator can work with mixed annotation and XML configurations, but the lack of documentation for that specific case is a hint that it is at least not recommended.
When XML configuration file is used, it takes precedence over annotations by default. ignore-annotations is used to overcome this (text highlight is mine):
Setting ignore-annotations to true means that constraint
annotations placed on the configured bean are ignored. The default for
this value is true. ignore-annotations is also available for the nodes
class, fields, getter, constructor, method, parameter, cross-parameter
and return-value. If not explicitly specified on these levels the
configured bean value applies.
Using Hibernate Validator to Cover Your Validation Needs article states that:
The default for a field is ignore-annotations=”false”. This means
that by default annotations for a field are stronger (this is of
course after you indicated that that the bean itself wont ignore
annotations). If you wont that the XML will be stronger than you have
to indicate that by ignore-annotations=”true”
It seems possible to disable annotation configuration for a specific field which is configured in XML.
Another solution to switch between annotation and XML configuration is to use Grouping constraints.
I'm not sure if anything of the above is of any use for you, but if it is possible I would probably switch to a single configuration (XML, assuming that annotation config comes from external library you cannot modify) and enforce it everywhere instead of relying on undocumented features.
I am validating fields of my Data Access Object classes. In one attempt, I have started adding Bean Validation annotations to the properties (#NotNull, #NotBlank, #Min, #Max and so on). I also have more annotations Jackson (JsonProperty (..)) for swagger library and documentation (#Api (...)). In my opinion the class was very "dirty" with many annotations (each property has at least three annotations). Example of one field:
#JsonProperty("ownName")
#Api(description="it is my own name" required=true)
#Valid
#NotNull
private SomeObject object;
In another attempt, I have performed my own validation with the Spring Validator interface. If a custom validator such as the Spring Interface is used it seems to be cleaner and also allows you freedom to generate more than one validator for different situations. Also, the class does not seem to be so overloaded with annotations and validations are independent of the class. Example of Validator:
public class UserValidator implements Validator {
#Override
public boolean supports(Class<?> arg0) {
return User.class.isAssignableFrom(arg0);
}
#Override
public void validate(Object obj, Errors error) {
User user = (User) obj;
if(user.getPassword().length() < 10)
{
error.reject("Password must be lesser than 10");
}
//more validations....
}
}
When would you use one or another?
What are the pros and cons of each?
I think it is a matter of taste and use case. I agree that sometimes it feels one ends up in some sort of annotation overload.
Some reasons for using Bean Validation are that it is a standard. The constraint annotations are standardized and many frameworks integrate with it, for example JPA in case you want to add yet another annotation based framework ;-)
Using something like Spring binds you to a specific library/framework. Your code will be less portable. If you of course never see a scenario where you would leave Spring behind, this might not matter.
Of course you could do something home grown altogether, but in this case you need to write the whole integration code into for example Spring, REST, JPA, etc.
Also writing a general purpose validation framework is not trivial. There are many things to consider.
Just started using Hibernate Validator. I have a case where a bean's id is autogenerated when saved. I'd live to validate the bean before the save. At which time the id can be null. However, when I want to update it the id must be notnull.
So the generic #NotNull on the field won't work because when I go to save it it will fail validation.
There are ways to work around this, but I was wondering if the spec or hibernate implementation have a standard way of doing this. I'd like to not have any validation errors on save and no validation on update.
Such as applying a constraint but it's ignored unless implicitly named or something like that.
Thanks in advance.
You can achieve that with groups.
public class MyBean {
#NotNull(groups = UpdateBean.class)
private Long id;
}
Validate without the id:
validator.validate(myBean);
Validate with the id:
validator.validate(myBean, UpdateBean.class);
I'm implementing several DAO classes for a web project and for some reasons I have to use JDBC.
Now I'd like to return an entity like this:
public class Customer{
// instead of int userId
private User user;
// instead of int activityId
private Activity act;
// ...
}
Using JPA user and activity would be loaded easily (and automatically specifying relations between entities).
But how, using JDBC? Is there a common way to achieve this? Should I load everiting in my CustomerDAO? IS it possible to implement lazy initialization for referenced entities?
My first idea was to implement in my UserDAO:
public void initUser(Customer customer);
and in my ActivityDAO:
public void initActivity(Customer customer);
to initialize variables in customer.
Active Record route
You could do this with AspectJ ITDs and essentially make your entities into Active Record like objects.
Basically you make an Aspect that advises class that implement an interface called "HasUser" and "HasActivity". Your interfaces HasUser and HasActivity will just define getters.
You will then make Aspects that will weave in the actual implementation of getUser() and getActivity().
Your aspects will do the actual JDBC work. Although the learning curve on AspectJ is initially steep it will make your code far more elegant.
You can take a look at one of my answers on AspectJ ITD stackoverflow post.
You should also check out springs #Configurable which will autowire in your dependencies (such as your datasource or jdbc template) into non managed spring bean.
Of course the best example of to see this in action is Spring Roo. Just look at the AspectJ files it generates to get an idea (granted that roo uses JPA) of how you would use #Configurable (make sure to use the activerecord annotation).
DAO Route
If you really want to go the DAO route than you need to this:
public class Customer{
// instead of int userId
private Integer userId;
// instead of int activityId
private Integer activityId;
}
Because in the DAO pattern your entity objects are not supposed to have behavior. Your Services and/or DAO's will have to make transfer objects or which you could attach the lazy loading.
I'm not sure if there is any automated approach about this. Without ORM I usually define getters as singletons where my reference types are initialized to null by default, i.e. my fetching function would load primitives + Strings and will leave them as null. Once I need getUser(), my getter would see if this is null and if so, it would issue another select statement based on the ID of the customer.