SpringBoot with Jakarta Validation Api not validating with #Valid Annotation - spring-boot

i have a question to Spring boot and the dependency jakarta-validation-api.
Actually i have a simple DTO which holds some properties. But this properties are not being validated when I call the REST-function within the #Valid annotation.
Can someone find my error?
A snippet of my pom.mxml dependencies:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0-M1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.0.0</version>
</dependency>
My DTO class:
import jakarta.validation.Valid;
#Data
public class TestDTO implements Serializable {
private static final long serialVersionUID = -1362258531757232654L;
#NotEmpty(message = "Id could not be empty or null.")
#Size(min = 36, max = 36, message = "Id must contains exactly out of 36 characters.")
private String id;
#Min(value = 1, message = "Page size cannot be null or <= 0.")
private Integer page;
}
And also a snippet of the REST-Resource class where the DTO is been used in the body:
#PostMapping(path = "/")
public Integer testValidation(#Valid #RequestBody TestDTO body) {
LOGGER.info(body);
return 1;
}
Actually i would think that when I call the Post-REST method that it will be validated before it will go into the method body, but actually it goes into the method body without has been validated before.
Is it due to "jakarta" dependency instead of "javax"?
Hope you can help me :)

From what I understand in the Spring Boot 3.0.0 M1 Release Notes, Spring Boot 2.X does not support Jakarta EE, but support will come with Spring Boot 3.X.
For Spring Boot 2.X you should stick with javax.
For Spring Boot 3.X you should stick with jakarta.

In my case(spring boot 2.4.*).
I remove jakarta.validation-api dependencies,then it works.
use javax.*
not jakarta.*

Related

How to make both validation annotations in a ConfigurationProperties bean and a #FeignClient interface work together?

Let's say I have this application.yml (which will be environment-dependent e.g. via Spring profiles):
app.remote:
url: http://whatever.url.it.is:8080/
and matching Java-style configuration properties class:
#Configuration
#ConfigurationProperties("app.remote")
public class MyRemoteProperties {
#NotBlank
private String url;
// matching getter/setter...
}
I want some kind of client for my remote url:
#Service
#FeignClient(value = "remote", url = "${app.remote.url}")
public interface MyRemote {
#GetMapping("/what/ever/rest/api")
String stuff();
}
Unfortunately I can't get the validation work for MyRemoteProperties e.g. when the app.remote.url property is blank (empty) the application doesn't start (Spring fails at wiring the MyRemote bean) and I get this error:
Caused by: java.lang.IllegalStateException: No Feign Client for
loadBalancing defined. Did you forget to include
spring-cloud-starter-netflix-ribbon?
(and I don't want load-balancing; I assume this is because the URL is empty at some point, then it expects some load-balancer config hence Ribbon here in the error message).
Or maybe I don't known how to plug it into the MyRemote interface's configuration, e.g. I also tried:
#FeignClient(value = "remote", configuration = MyRemoteProperties.class)
But same result.
How do I get this validation thing to work?
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
At some point where the interface is called:
#Service
public RandomServiceOrController {
#Autowired
private MyRemote myRemote;
public void processMyStuff() {
// ...
String myStuff = myRemote.stuff();
// ...
}
}
Don't forget the #Validated annotation on your Java properties class:
#Validated
#Configuration
#ConfigurationProperties("app.remote")
public class MyRemoteProperties {
#NotBlank
private String url;
// matching getter/setter...
}
Your application won't start because of the missing property, not because of a non-defined-loadbalancing-client-you-don't-need (thus making its error message more awkward).

SpringBoot 2 + Junit5: null with #Value

I have an application with SpringBoot2 and Junit5, and now I'm trying to make a test. I have a this class called OrderService that looks like this:
#Component
public class OrderService {
#Value("#{'${food.requires.box}'.split(',')}")
private List<String> foodRequiresBox;
#Value("#{'${properties.prioritization}'.split(',')}")
private List<String> prioritizationProperties;
#Value("${further.distance}")
private Integer slotMeterRange;
#Value("${slot.meters.long}")
private Double slotMetersLong;
As you can see, the class has many #Value annotations that extracts values from application.properties file.
In the POM file I have these dependences:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.0.5.RELEASE</version>
</dependency>
Tn the test/resources folder I have the application.properties file with this information:
properties.prioritization:vip,food
food.requires.box:pizza,cake,flamingo
further.distance:2
slot.meters.long:0.5
The test file looks like this:
#ExtendWith(SpringExtension.class)
#TestPropertySource(locations="classpath:application.properties")
public class OrderServiceTest {
OrderService orderService;
#BeforeEach
void before(){
orderService = new OrderService();
}
#Test
void findAll() {
Order order = new Order().withDescription("2x Pizza with Salad\\n2x Kebab with Fries\\n1x Hot dog with Fries\\n2x Pizza with Fries");
assertTrue(orderService.orderHasFood.test(order));
}
}
But the test throws NullPointerException when it tries to use foodRequiresBox, so there is a problem to read the application.properties file.
Could you tell how can I read the application.properties file for the tests?
1st Solution
I would recommend to use Spring's internal annotation called #SpringJUnitConfig
This annotation is actually the same as #ExtendWith(SpringExtension.class) BUT you can configure your spring application contexts for your test in the same way you used to use #ContextConfiguration.
Or if you want a full Spring Boot Test you could just combine:
#SpringJUnitConfig
#SpringBootTest
public class OrderServiceTest {
...
}
2nd Solution
Another way is to not use Spring at all, but mock all the internal stuff with e.g. Mockito and write a plain simple Unit Test.
You could then set your normally via Spring injected annotated #Value fields via org.springframework.test.util.ReflectionTestUtils.
I'd recommend using org.springframework.test.util.ReflectionTestUtils (as indicated in #mrkernelpanic second solution) using the setField() method, in order to avoid initializing the full Spring context.
Here is a snippet:
// ReflexionTestUtils.setField(targetObject, "targetFieldName", valueToInject);
ReflexionTestUtils.setField(orderService, "foodRequiresBox", "pizza,cake,flamingo");

Spring boot - Rest Pagination return message HttpMessageNotWritableException

I tried to implement pagination for my api with Spring Boot 1.5.3 and Elasticsearch but it returned HttpMessageNotWritableException: could not write JSON document instead.
I thought that ElasticsearchRepository already provided paging and sorting method so I tried to used that then return pageable object to my controller. And I think the error occurs when the controller returns the result.
Here is the controller.
#GetMapping(value = "/client",
params = { "page", "size" })
public Page<Client> getAllClient( #RequestParam("page") int page, #RequestParam("size") int size){
return clientService.getAllClient(page, size);
}
The service.
public Page<Client> getAllClient(int page, int size) {
Page<Client> resultPage = clientRepository.findAll(new PageRequest(0, 3));
return resultPage;
}
The repository.
public interface ClientRepository extends
ElasticsearchRepository<Client, String> {
public Client findByName(String name);
}
I already used Lombok for my entity so it should not be the problem.
#Data
#Document(indexName = "customer", type = "client")
public class Client {
#Id
private String id;
private String name;
private String city;
private String phone;
}
dependencies in pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
When I make the HTTP GET request to the url that mapped with the controller, the error log below appeared.
WARN 12616 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON document: (was java.lang.NullPointerException) (through reference chain: org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl["facets"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl["facets"])
I wonder if I forgot any configuration in my project.
I just found that after I changed to Spring Boot version 2.0.4 it works!
But I still wanted to know how to do it in version 1.5.3 though.

Spring Boot JPA Save not working

I am migrating Spring Boot 1.2.4 running in Websphere to Spring Boot 1.5.9. This application uses Spring Data JPA with Hibernate. After migration one of the save method is not working as expected. When I try to insert a new row in table getting an exception with SQLCODE=-204, SQLSTATE=42704, but same code works fine with Spring Boot 1.2.4.
My Pom.xml looks like below.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Spring Boot 1.2.4 configuration looks like below.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.10.Final</version>
</dependency>
#Repository
public interface MyDataRepository extends CurdRepository<MyEntity,Long>{}
#Service
public class MyDataService{
#Autowired
private MyDataRepository myDataRepository;
public void insertMyDate(MyData myData){
myDataRepository.save(myData)
}
}
#Entity
#Table(name="MYDATA" ,schema="MYSCHEMA")
public class MyData implements java.io.Serializable {
private int memberId;
private String firstName;
private String lastName;
private Date dateofBirth;
#GeneratedValue(strategy=GenerationType.AUTO)
#Id
#Column(name="MEMBER_ID",uniqueue=true,nullable=false)
public int getMemberId(){
return this.memberId;
}
public void setMemberId(int memberId){
return this.memberId = memberId;
}
//Geter and Setters
}
With same code when executing save method in 1.5.9 getting below exception.
could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
You don't need this hibernate-entitymanager dependency becz you are using repository to save data

How to use #NotNull with spring boot?

I have this dependency:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
Which have it's version managed by
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
And I have this piece:
import javax.validation.constraints.NotNull;
//other imports ommited
#Component
#ConfigurationProperties(prefix = "collector")
public class CollectorProperties {
#NotNull
private String urlCDI;
//getters and setters
}
And my SpringApplication.run class has this pice:
#SpringBootApplication
#EnableConfigurationProperties
#ComponentScan({ "otherPackages", "packageWhereCollectorPropertiesIs" })
When I have my application.properties with this line
collector.urlCDI=https://www.cetip.com.br/Home
It works as it was supposed inside other spring beans:
//#Component class variables:
#Autowired
private CollectorProperties props;
//inside some method
URL url = new URL(props.getUrlCDI());
But when I remove it or alter property key I get lots of NPE instead of validations errors. What I'm doing wrong? Doesn't hibernate-validator contains an implementation of javax.validation.constraints.NotNull interface?
Add ยด#Validated' annotation to your properties class

Resources