How can I validate entities with spring data r2dbc? - java

In Spring data JPA there are annotations that can be used to set up validations for entities in a declarative manner. They can be found in javax.validation.constraints.* or additionally in org.hibernate.validator.constraints.* (in the case when Hibernate is plugged in).
Example:
#NotNull
private String lastName;
However, if the case of Spring data r2dbc they do not work out of the box.
Is there any simple and smooth way to set up validations for entities in Spring data r2dbc? That should not be too difficult in my opinion because probably it does not require full ORM support, just a matter of callbacks for checking object fields before persisting it.

In the RestController, validate the incoming request body using the annotations from Bean validation and Hibernate validators.
For RouterFunction or manal validation in your service, inject a Validator or ValidatorFactory to validate the request body before the data is (converted and) persisted into databases.
JPA is tightly integrated with Bean Validation/Hibernate Validator, besides validation, it will affect the generated schemes by default.
In a real world application, do not use the data layered entity classes in the web layer as request body class.

It works very fine with Bean Validation and Hibernate Validation.
You can use all the same annotations you did in JPA, since you do it in your DTOs.
See the example:
#Data
public class UserDto {
private String name;
#NotNull(message = "Last name can not be empty")
private String lastName;
#Min(value = 10, message = "Required min age is 10")
#Max(value = 50, message = "Required max age is 50")
private int age;
}
Then your controller would be annotated:
#RestController
#RequestMapping("user")
public class RegistrationController {
#Autowired
private UserService userService;
#PostMapping("register")
public Mono<UserDto> register(#Valid #RequestBody Mono<UserDto> userDtoMono{
return this.userService.registerUser(userDtoMono);
}
}
The only thing now is that you have to personalize your message, so that it returns the one you have set in your DTO. To override it you should create an ExceptionHandler.
#ControllerAdvice
public class ValidationHandler {
#ExceptionHandler(WebExchangeBindException.class)
public ResponseEntity<List<String>> handleException(WebExchangeBindException e) {
var errors = e.getBindingResult()
.getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errors);
}
}
I've been using it in my projects and got this example from: https://www.vinsguru.com/spring-webflux-validation/

Related

Spring - JPA - Hibernate how to use EntityManager

I'm trying to build REST application using following tech stack:
Spring
VueJs
JPA (Hibernate)
This is my first experience in writing Sping application and web app development overall.
I have 4 tables in my DataBase:
Language
Sentence
Rule
User
For example in Rule there is :
Rule create(EntityManagerFactory factory, String type, String hint, String help, Language language);
List<Rule> readAll(EntityManagerFactory factory);
Rule readID(EntityManagerFactory factory, int id);
void update(EntityManagerFactory factory, String type, String hint, String help, Language language);
So there is my questions:
When I create Controllers for each table, I use the CRUD methods to modify (or not) my database, and I return a view for my HTML and VueJS part. But my method need an EntityManagerFactory, should I create a field in each Controllers class or this is not how I'm supposed to do ?
Do I need to create a bean file and configure it or persistence.xml and pom.xml are enough?
Thanks
Seems like your first question can be broken up into multiple concerns.
When I create Controllers for each table, I use the CRUD methods to modify (or not) my database, and I return a view for my HTML and VueJS part. But my method need an EntityManagerFactory, should I create a field in each Controllers class or this is not how I'm supposed to do?
Since you have already accepted an answer that recommends the use of spring-data-jpa. You will be dealing with entities and repositories.
Entities are JPA managed beans that will interact with your database.
#Entity
#Table(name = "rule")
public class Rule {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
long id;
String type;
...
#OneToOne
#JoinColumn(...)
Language language;
}
Repositories will provide all the necessary operations required to perform an action against your database. With JPA you can create an interface that extends CrudRepository which would provide you with some CRUD operations that come free with it. findOne(/* id */), delete(), save()
#Repository
public interface RuleRepository extends CrudRepository<Rule, Long> {
// You can easily specify custom finders
public List<Rule> findByType(String type);
}
But my method need an EntityManagerFactory, should I create a field in each Controllers class or this is not how I'm supposed to do?
It's typically frowned upon to have a request/response object to be JPA entity. See the linked answer for should i use jpa entity in rest request and/or response
There are multiple approaches that you can take to take a controller request and send a response to your client side project.
#Controller
public class RuleController {
#Autowired
private RuleRepository ruleRepository;
// Approach 1: Use Request Parameters - enforce inputs
#PostMapping("/rule/:id")
public Rule postWithRequestParams(#PathParam("id") Long id,
#RequestParam("type") String type,
#RequestParam("hint") String hint,
#RequestParam("languageField1") String languageField1) {
Rule inputRule = new Rule(id, type, hint, new Language(languageField1));
Rule responseRule = ruleRepository.save(inputRule);
return responseRule; // I would imagine you would want to set up a model for the response itself
}
// Approach 2: Use RequestBody - serialize Rule from the request
#PostMapping("/rule/:id")
public Rule postWithRequestParams(#PathParam("id") Long id, #RequestBody Rule inputRule) {
Rule responseRule = ruleRepository.save(inputRule);
return responseRule;
}
Do I need to create a bean file and configure it or persistence.xml and pom.xml are enough?
If you have added spring-boot-starter-data-jpa as a dependency, a lot of the bean configuration has already been done for you.
In your main/src/resources (you should have an application.properties or application.yml)
spring.datasource.url= # JDBC url of the database.
spring.datasource.username= # Login user of the database.
spring.datasource.password= # Login password of the database.
Spring does a lot of the magic and heavy lifting for you.
If you are using spring Boot then you don't need entity manager. All you need to do is to define Datasource in you properties file. And create a Bean in our Configuration class just like:
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource datasource() {
return DataSourceBuilder.create().build();
}
Now rest of the things you can handle with repositories.They will be like:
import org.springframework.data.repository.CrudRepository;
public interface RuleRepository extends CrudRepository<Rule, Long> {
}
In your controllers you will use it like:
#Autowired
private RuleRepository ruleRepository;
#Get
#Path("/getRule/:id")
public Rule find(#PathParam("id")Long id){
return ruleRepository.findOne(id);
}
These dependencies I used in my gradle project. You will find Maven version for same:
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile group: 'mysql', name: 'mysql-connector-java'
You definitely need to have a look on Spring Boot(http://start.spring.io), it allows easier to start web app development. As for persistence layer you could use Spring Data JPA(already includes Hibernate) module which also can be easily integrated with Spring Boot. The beauty of Spring Data that is already have written by default most queries like save(), remove(), find() and so on. You only need to define Objects which will be used by Spring Data.
Update: See my Spring Boot REST API example here

Exclude few fields in java bean from being sent in rest response - Spring Boot

I have a bean which I am returning as part of one of my controller methods. There are few fields in the bean that I have processing but do not want them to be returned back to the consumer. Is there a way to exclude some of the fields from being returned without having to create a new bean that only has the elements that I need to return?
Spring boot by default uses Jackson to serialise/deserialise json. In Jackson, you can exclude a field by annotating it with #JsonIgnore, e.g.
public class Bean {
#JsonIgnore
private String field1;
private String field2
//getters and setters
}
By doing this, field1 of Bean class will not get sent in the response. Also, if this bean is used in the request, this field will not be deserialised from request payload.
Here's the documentation for JsonIgnore.

Difference between #Valid and #Validated in Spring

Spring supports two different validation methods: Spring validation and JSR-303 bean validation. Both can be used by defining a Spring validator that delegates to other delegators including the bean validator. So far so good.
But when annotating methods to actually request validation, it's another story. I can annotate like this
#RequestMapping(value = "/object", method = RequestMethod.POST)
public #ResponseBody TestObject create(#Valid #RequestBody TestObject obj, BindingResult result) {
or like this
#RequestMapping(value = "/object", method = RequestMethod.POST)
public #ResponseBody TestObject create(#Validated #RequestBody TestObject obj, BindingResult result) {
Here, #Valid is javax.validation.Valid, and #Validated is org.springframework.validation.annotation.Validated. The docs for the latter say
Variant of JSR-303's Valid, supporting the specification of validation
groups. Designed for convenient use with Spring's JSR-303 support but
not JSR-303 specific.
which doesn't help much because it doesn't tell exactly how it's different. If at all. Both seem to be working pretty fine for me.
A more straight forward answer.
For those who still don't know what on earth is "validation group".
Usage for #Valid Validation
Controller:
#RequestMapping(value = "createAccount")
public String stepOne(#Valid Account account) {...}
Form object:
public class Account {
#NotBlank
private String username;
#Email
#NotBlank
private String email;
}
Usage for #Validated Validation Group
Source: http://blog.codeleak.pl/2014/08/validation-groups-in-spring-mvc.html
Controller:
#RequestMapping(value = "stepOne")
public String stepOne(#Validated(Account.ValidationStepOne.class) Account account) {...}
#RequestMapping(value = "stepTwo")
public String stepTwo(#Validated(Account.ValidationStepTwo.class) Account account) {...}
Form object:
public class Account {
#NotBlank(groups = {ValidationStepOne.class})
private String username;
#Email(groups = {ValidationStepOne.class})
#NotBlank(groups = {ValidationStepOne.class})
private String email;
#NotBlank(groups = {ValidationStepTwo.class})
#StrongPassword(groups = {ValidationStepTwo.class})
private String password;
#NotBlank(groups = {ValidationStepTwo.class})
private String confirmedPassword;
}
As you quoted from the documentation, #Validated was added to support "validation groups", i.e. group of fields in the validated bean. This can be used in multi step forms where you may validate name, email, etc.. in first step and then other fields in following step(s).
The reason why this wasn't added into #Valid annotation is because that it is standardized using the java community process (JSR-303), which takes time and Spring developers wanted to allow people to use this functionality sooner.
Go to this jira ticket to see how the annotation came into existence.
In the example code snippets of the question, #Valid and #Validated make no difference. But if the #RequestBody is annotated with a List object, or is a string value annotated by #RequestParam, the validation will not take effect.
We can use the #Validated's method-level validation capability to make it work. To achieve this, the key point is to place #Validated on the class. This may be another important difference between #Valid and #Validated in spring framework.
Refrence
Spring boot docs
Just for simplifying:
#Validated annotation is a class-level annotation that we can use to tell Spring to validate parameters that are passed into a method of the annotated class.
and
#Valid annotation on method parameters and fields to tell Spring that we want a method parameter or field to be validated.
besides above, you can only apply #Valid on a domain/field for nested validation, not with a #validated.
#Validated can be used for a class:
#Validated
public class Person {
#Size(min=3)
private String name;
...

Resteasy Bean Validation Not Being Invoked, again

My question is similar to Resteasy Bean Validation Not Being Invoked. The solutions there don't work, though.
I'm using Resteasy 3.0.9.Final with resteasy-validator-provider-11 in my pom. I'm launching the whole thing using a custom Jetty class.
Weirdly, validation is working fine on #PathParams, but not on beans.
#POST
#Path("/foo/{myParam}")
public Message post(MyBean myBean, #PathParam("myParam") #Size(min=5) String myParam) {
return new Message("bean:" + myBean.toString());
}
public static class MyBean {
#NotNull
public String myStr;
#Max(value = 3)
public int myInt;
public String toString() {
return myStr + myInt;
}
}
In this case, the #Size constraint on myParam is working fine. But the #NotNull and #Max constraints in MyBean are not getting invoked.
Am I missing an annotation somewhere?
Here's one more clue. My logs include these entries:
2014-12-30 12:16:56 org.hibernate.validator.internal.util.Version 6446 INFO HV000001: Hibernate Validator 5.0.1.Final
2014-12-30 12:16:56 org.jboss.resteasy.plugins.validation.AbstractValidatorContextResolver 6477 INFO Unable to find CDI supporting ValidatorFactory. Using default ValidatorFactory
I believe, but not 100% sure, that the issue is that you're missing #Valid on the MyBean parameter. I would also recommend to make it a separate class, rather than a static class.
Per the spec, validation constraints on methods where the object is a complex object need to have the parameter annotated #Valid to ensure that the constraints are cascaded.

How to disable Hibernate validation in a Spring Boot project

I have a spring boot project that has a CrudRepository, an Entity and a Controller. I am basically trying to persist an entity based on the data passed to the Controller.
To do this, I am using spring-boot-starter-jpa. My Entity is annotated with JSR-303 annotations, which are checked in the controller before the data gets passed to the CrudRepository for persistence.
Controller method:
#RequestMapping(value = "users", method = { RequestMethod.POST })
public SuccessfulResponse<User> addUser(#Valid #RequestBody User user, BindingResult validation) {
if (validation.hasErrors()) {
throw new ValidationException(validation);
}
User saved = this.users.save(user);
return new SuccessfulResponse<User>(saved);
}
Entity:
#Entity /* JPA */
public class User {
#Id /* JPA */
#Column(name="email_address", nullable=false, length=255) /* JPA */
#UserUnique
private String emailAddress;
}
The cause of my issues is the UserUnique annotation. Its validator looks like this:
public class UserUniqueValidator implements ConstraintValidator<UserUnique, String> {
private UserRepository users;
#Autowired
public UserUniqueValidator(UserRepository users) {
this.users = users;
}
#Override
public void initialize(UserUnique annotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return users.findOne(value) == null;
}
}
What seems to be happening is, the validation is getting run twice. Once in the controller via the #Valid annotation, and once when Hibernate tries to persist the object. However, when Hibernate tries to persist the object, it throws:
javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: class test.UserUniqueValidator`
This seems to be because its not spring-aware and cant inject the dependency into the constructor. So, what I want to do is disable Hibernate validation completely (as its redundant and already happening in the controller).
There seems to be a property called javax.persistence.validation.mode which you can set to none. However, I cant for the life of me figure out where to set it in a code-based configuration.
I realise there are questions like JSR-303 dependency injection and Hibernate but these are all using xml config and manually configuring parts of the persistence layer.
What I want to do is "post-configure" the required parts of the persistence layer that Spring Boot creates for me because if I define my own then I am no longer leveraging Spring Boot's auto configuration. Can anyone help me determine if 1) this is possible and 2) which parts do I need to configure and how?
Thanks!
As [M. Deinum] mentioned in a comment on my original post, the solution is to set:
spring.jpa.properties.javax.persistence.validation.mode=none
In the application.properties file.
Additionally, this behaviour is described here (its easy to miss because no example is provided).
#Erin Drummond's Answer is for database entity validation (individual records)
But if someone ran into a problem with schema validation below property works well.
# Hibernate ddl auto (create, create-drop, validate, update, none)
spring.jpa.hibernate.ddl-auto=none
Actually, in spring boot 2.x. it is:
spring.jpa.hibernate.ddl-auto: none
for spring boot unless you add validation dependency the spring validation would not kick in.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Categories