Validate field in entity without validating in parameter - java

I have an entity object similar to the following
#Document(collection = "job")
public class Job {
#Id
private String id;
private JobStatus status;
#NotBlank
private String term;
...standard getters/setters
}
I have a controller something like this:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Job> createJob(#RequestBody #Valid Job job) {
...store the new job in the database
}
The Job.status property is set by the code before storing to the database. Any "status" property included in the request body to the createJob method is ignored.
I would like to validate that Job.status is not null when the job is written to the database, but users must not be required to include a status in the request body. If I annotate the Job.status property with #NotNull, the validation of the request body fails unless I include "status".
How do I validate the status field only when persisting to data store and not as part of the request body?

You're trying to use the database entity as a UI entity with different validation rules. You have options:
Use different classes for the UI and the DB
Create a custom JSR-303 validator for your not null which you somehow avoid applying when you validate in the UI layer
Put the constraint for not null in the database itself, rather than hibernate/JPA and handle the constraint exception

Related

Hibernate + Spring: Conditional on create bean validation

I use the date validaton using the #Future annotation.
#NotNull
#DateTimeFormat(pattern="yyyy-MM-dd")
#Column(name = "FROM")
#Temporal(TemporalType.DATE)
#Future
private Date from;
#NotNull
#Column(name = "FOO")
private String foo;
I perform CRUD operations using Rest API. The requirement is the from date will be in future - after the entity is being created (today). However, the time changes and in case of changing the field foo using ex. PUT method, the validation won't pass.
#PutMapping(value = "/{id}")
public ResponseEntity<?> put(
#Valid #RequestBody MyEntity myEntity,
#PathVariable("id") int id)
{
... update entity based on id
}
When I call this method in the far future (after the from value persisted), the validation doesn't let me perform the operation, because the field from is no more valid.
There is a simple in-built solution to trigger a certain validation only on create event?
I have been thinking over creating the own cross-field validation through annotation, however I am not able to determine the creation based on other fields.
You can use Grouping Constraints, to restrict which validation set to use for: pre-persist, pre-update, pre-remove and ddl(For database schema).
So to validate from field just for persist operation and ignore it for put(update), you may:
Add an interface e.g. GroupFuture:
package com.example.entity;
public interface GroupFuture {}
In your MyEntity, I think you should also add #NotNull constraint as #Future consider null as valid value:
//...
//Maybe #NotNull
#Future(groups = GroupFuture.class)
private Date from;
//...
Finally, if you've configured hibernate using:
persistence.xml, add this line in the persistence-unit setting:
<property name="javax.persistence.validation.group.pre-persist" value="javax.validation.groups.Default, com.example.GroupFuture">
Programmatically:
// If you're using pure hibernate
Configuration configuration = new Configuration().setProperty("javax.persistence.validation.group.pre-persist", javax.validation.groups.Default, com.example.GroupFuture);
`
// If you're using JPA/hibernate
entityManagerFactory.getJpaPropertyMap().put("javax.persistence.validation.group.pre-persist", javax.validation.groups.Default, com.example.GroupFuture);
Useful reading(even it's for hibernate 3.6): Chapter 23. Additional modules

Spring MVC - private field on entity not populated from POST

I have a private field name on an Entity class: Product, which doesn't have a setName() method due to the business logic not allowing names to be changed.
I have an endpoint of the form:
#RequestMapping(value = "/endpoint", method = POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public Product saveProduct(
#Validated(FormSubmission.class) Product product,
Errors errors,
#RequestParam("sellerId") long sellerId) { ... }
I was under the impression that Spring was able to populate private fields as well when parsing request bodies but the name property stays null after a correctly formatted POST request unless I add a setName() method to Product.
Is the setter a requirement?
The private modifier specifies that the member can only be accessed in its own class.
and if you are outside, you can't read/write.

Restrict JSON attributes for #RequestBody

This may be a simple task, but I couldn't find a way to do it. Basically, I need to disallow some parameters at the time of using #RequestBody annotation in my controller.
Here is my model:
#Data
public class MyModel {
private int id;
private String name;
}
What I want to do is at the time of response, I want both of the properties to be serialized to JSON, but at the time of create or update, I prefer not to receive id as part of #RequestBody deserialization.
Right now, if I pass id in the JSON body, Spring initializes a MyModel object with its id set to the passed value.
Reason? The ID cannot be generated until the model is created, so the app shouldn't allow the ID to be set. On update, the ID needs to be passed in the URL itself e.g. (PUT /mymodels/43). This helps following the REST principles appropriately.
So, is there any way to achieve this functionality?
Update 1:
Right now, I am stuck with using a request wrapper. I created a new class MyModelRequestWrapper with only name as its property, and have used it with the #RequestBody annotation.
How you do this depends on what version of Jackson you are using. It's basically possible by a combination of the annotations #JsonIgnore and #JsonProperty on relevant fields/getters/setters.
Have a look at the answers here: Only using #JsonIgnore during serialization, but not deserialization

Store JPA entity with given field object id instead of object itself

The problem is following:
We have entity:
#Entity
public class Feedback {
#Id
#GeneratedValue(generator="token")
private String id;
#ManyToOne
private Product product;
private String message;
// other fields
}
And we have a server endpoint, that receives feedback from clients.
Feedback received in multipart/form-based format, with fields:
ProductId - product identifier
Message - feedback message
Some other fields
To set Feedback.product we need to load Product object from JPA - this can be time-consuming and it creates unnecessary queries.
Is it possible to store entity, but pass the product id instead of the product object?
We need some way to modify the INSERT query.
We use EclipseLink JPA with Spring and Vaadin.
Use EntityManager.getReference(): if the entity is not loaded already, it simply creates a lazy-initialized proxy around the ID, without causing any SQL query to be executed.

Read only fields in spring-roo or spring-web-mvc

I have what appears to be a common problem within spring-mvc. Several of my domain object have fields that are not updatable so in my view I am not binding these fields.
For competeness sake The way these are excluded from the view is by editing the spring-roo scaffolded view setting the render attribute on the parameter to false.
As spring-mvc creates a new instance of the object rather than updating the existing object these fields are null. This means however that the object fails its validation before the control reaches the controller.
A lot of my entities will have extra fields that are not updatable in the view so I'd like to be able to come up with a generic solution rather than continually doing the same work over and over again (violating DRY).
How can one allow validation to occur in a consistent manner if fields are omitted from the view?
#RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String UserController.update(#Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
populateEditForm(uiModel, user);
return "admin/users/update";
}
uiModel.asMap().clear();
user.merge();
return "redirect:/admin/users/" + encodeUrlPathSegment(user.getId().toString(), httpServletRequest);
}
Possible Solutions:
Omit #Valid annotation from the controller.
Pros
Easy to implement.
Easy to understand.
Cons
Means changing the controller method for every update on every object.
Validation is not occuring in the same place as all of the rest of the application.
No easy way to return the binding errors back to the view (need to validate the object afterwards)
Add Custom Validator for methods that need omitted fields
Example:
#InitBinder
public void initBinder(WebDataBinder binder, HttpServletRequest request) {
if (request.getMethod().equals("PUT")) {
binder.setDisallowedFields("registrationDate", "password");
Validator validator = binder.getValidator();
Validator userUpdateValidator = new UserUpdateValidator();
binder.setValidator(userUpdateValidator);
}
}
Pros
Clear flow.
Cons
Suffers wildly from DRY problems. This means that If the domain object is altered in any way I need to revalidate.
Field validation is not the same as Hibernate validation when saving.
No tangible benefits over omitting validation and manually validating.
Would consider if?
Custom validator could delegate to standard JSR-303 validator but just omit fields.
Remove JSR-303 annotations from the domain object
Not an option this means that there is no validation on an object before saving. Worse I believe it will affect the DDL that is producted for database, removing constraints from the DB itself. Only put in here for completeness sake
Lookup domain object before validation occurs
The idea of this solution is to lookup the existing domain object before updating. Copying any not null fields to the old object from the request.
Pros
- The validation can go through the normal cycle.
- The validation doesn't need to change depending on what method you are implying.
Cons
Database access before hitting the controller has a bit of a smell.
I can't see any way to implement this.
Won't work for fields that need to be omitted during other stages of the object lifecycle. For example if adding a timestamp during creation.
I would like to know how to implement either a validator that delegates to the standard JSR-303 validator or alternatively how to lookup the object before modifying it. Or if anyone has any other possible solutions?
Either of these solutions allow for the treatment to be consistent over multiple objects.
Hopefully either would allow for added annotations such as.
#RooCreateOnly which means the domain object could be annotated as such leaving all the validation definitions in the one place.
The last option can be achieved with the #ModelAttribute annotation.
Create a method that returns your domain object and add the #ModelAttribute annotation to it. Then add the same annotation to the domain object argument of the method where you want to use that object. Spring will first load the object from the ModelAttribute method then merge it with the posted data.
Example:
#ModelAttribute("foobar")
public User fetchUser() {
return loadUser();
}
#RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(#ModelAttribute("foobar") #Valid User user, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
return etc();
}
You can use the disabled property for the input tags in your jspx file containing the form for the fields that you want to mark as read-only.
Also make sure you clear the z attribute relating the field so that Roo will ignore the tag if there is any change made to the entity later on.
Cheers!
I'm posting another answer totally unrelated to my previous one.
There is another solution: wrap your domain object into special form object that only expose the fields you want to validate.
Example:
public class UserForm {
private final User user = new User();
// User has many fields, but here we only want lastName
#NotEmpty // Or whatever validation you want
public String getLastName() {
return this.user.getLastName();
}
public void setLastName(String lastName) {
this.user.setLastName(lastName);
}
public User getUser() {
return this.user;
}
}

Categories