Spring: Pageable data on controller - spring-boot

Inside spring libraries there are two Pageable classes:
org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties.Pageable
org.springframework.data.domain.Pageable
Which one should I use into my controllers and into my repositories?

spring-data-commons' domain.Pageable class is for the Page's info: like offset, size, sort; which is the input to the Repositories;
spring-boot-autoConfigure's SpringDataWebProperties.Pageable is a static inner class with some defaults for the above, which you can override, for example
spring.data.web.pageable.default-page-size=50 # default is 20
spring.data.web.pageable.max-page-size=200 # 2000

Related

Enabling CORS Sprint Boot RestController

What will be the result of the following:
Is set at class level:
#CrossOrigin(origins = "https://hostname", maxAge = "3000")
And at method level, I have:
#CrossOrigin
Will the method be applied with the origin and maxAge, or will it take all the defaults from #CrossOrigin?
I understand that a mixed configuration can be applied to a method if it's annotated at class level and method level - But this set up seems backward to me
Will the method be applied with the origin and maxAge, or will it take all the defaults from #CrossOrigin?
It will use both and combine configurations from both global and local annotations. When combining non null values are taken. So your Class level annotation has values and Method level annotation has no values then Class level values will be applied.
Check CorsConfiguration.combine method for more details.

Loading String[] from properties file into origins field of #CrossOrigin using property placeholder expression

In my spring boot application I have the following controller
#RestController(value = "ProjectController")
#CrossOrigin(origins = {"${app.api.settings.cross-origin.urls}"})
public class ProjectController {
// some request mapping methods
}
The property app.api.settings.cross-origin.urls is already a key having comma separated valid urls in application.properties file like
app.api.settings.cross-origin.urls=http://localhost:3000, http://localhost:7070
This approach works till I have only single value like
app.api.settings.cross-origin.urls=http://localhost:3000 but not for comma separated values.
The origins field inside #CrossOrigin is of type String[] still it does not convert into String[] automatically.
I mean there should be some way to achieve this provided by the framework. Not a work around.
I can achieve using comma separated urls from properties files using #Value into a List<String> or String[] as a field inside a #Configuration class like below
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Value("${app.api.settings.cross-origin.urls}")
private String[] consumerUiOrigins;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/api/**")
.allowedOrigins(consumerUiOrigins);
}
}
But this would be a global configuration having application wide applicability. I want to stick to the more fine grained #CrossOrigin annoation based CORS configuration.
So I put my question clearly below.
Is it possible to inject comma separated value from properties file as String[] using property plcaholer expression (${*}) into spring annotation fields having the same type i.e. String[] ????? If yes then how??? If no then can we tweak some core framework classes to achieve this??? Anyone please help....
P.S. - Please do not mark my question as duplicate of Use Spring Properties in Java with CrossOrigin Annotation or in Spring-Config XML
My question is more on usage of property placholder expressions inside spring annotation fields having multi element type like String[] and less on the configuration of CORS in spring applications.
Try doing as below in application.properties:
app.api.settings.cross-origin.urls="http://localhost:3000","http://localhost:7070"

How to auto generate response fields that do not have POJO

We have a service that simply returns the json document on a GET request. Since we do not have the POJO for the response "model", it appears we won't be able to use the auto response fields generation "goodness".
One option for us is to create the Pojos (quite large, about 50 attributes) and a corresponding controller that uses the pojos. This is awkward as we now have to maintain the model and corresponding controller just so we can auto generate the model.
Any ideas on how we can still leverage some auto generation of the response fields would be greatly appreciated.
Here's the controller I'm referring to:
#RestController
#RequestMapping("/api")
public class ProductController {
#Autowired
ProductService productService;
#RequestMapping(value = { "/products/{ids}" }, method = { RequestMethod.GET },
produces = "application/json", headers={"accept=application/json"})
#Timed
#ExceptionMetered
#LogExecutionTime
public String getProductDetails(#PathVariable("id") String id) {
return productService.getProductDetails(id);
}
At the moment I see no way of leveraging the auto generation without putting additional effort into it. Spring Auto REST Docs works by inspecting POJOs with a Jackson visitor (static introspection without runtime information) and there is currently no way of deriving the JSON fields from a string (would be dynamic at runtime). Thus, I only see two options:
The approach that you already described: Creating the corresponding POJO and using it.
Using Spring REST Docs for the corresponding test and manually document each field in the test. Might be the better option here if you do not want to alter the production code.

Set default page size for JPA Pageable Object

I have a PagingandSorting Repository which has a method that accecpts a pageable object.
I also have a controller that accepts a pageable object through the URL.
My use case is that, if a user specifies a page size parameter in the URL i must take that value for the pageable object. If he does not mention take a default value of 50.
But the pageable object defaults to 20 right now.
Any Suggestions would help
If you are talking about a Spring Data PagingAndSortingRepository you can set the default page size by using the #PageableDefault on a Controller method as follows:
public String listClients(#ModelAttribute FilterForm form, Model model, WebRequest request, #PageableDefault(sort = { "surname",
"forename", "address.town" }, value = 50) Pageable pageable) {
}
Or you can configure a global default using the following in your Spring config as shown below in both XML and Java config.
Note that newer versions of Spring Data use zero based page indexing while older versions used 1 for the first page. If your UI paging library expects 1 as first page then you can set the oneIndexedParameters property to true:
public void setOneIndexedParameters(boolean oneIndexedParameters)
Configures whether to expose and assume 1-based page number indexes in
the request parameters. Defaults to false, meaning a page number of 0
in the request equals the first page. If this is set to true, a page
number of 1 in the request will be considered the first page.
Parameters: oneIndexedParameters - the oneIndexedParameters to set
public void setFallbackPageable(Pageable fallbackPageable)
Configures the Pageable to be used as fallback in case no PageableDefault or
PageableDefaults (the latter only supported in legacy mode) can be
found at the method parameter to be resolved. If you set this to null,
be aware that you controller methods will get null handed into them in
case no Pageable data can be found in the request. Note, that doing so
will require you supply bot the page and the size parameter with the
requests as there will be no default for any of the parameters
available.
Parameters: fallbackPageable - the Pageable to be used as general
fallback.
In XML this looks like the following then:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver">
<property name="oneIndexedParameters" value="true"/>
<property name="fallbackPageable">
<bean class="org.springframework.data.domain.PageRequest">
<constructor-arg name="page" value="1" />
<constructor-arg name="size" value="10" />
</bean>
</property>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
In Java Config this looks like the below:
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setOneIndexedParameters(true);
resolver.setFallbackPageable(new PageRequest(1, 20));
argumentResolvers.add(resolver);
super.addArgumentResolvers(argumentResolvers);
}
}
For Spring Boot 2.X you have set of parameters:
# DATA WEB (SpringDataWebProperties)
spring.data.web.pageable.default-page-size=20 # Default page size.
spring.data.web.pageable.max-page-size=2000 # Maximum page size to be accepted.
spring.data.web.pageable.one-indexed-parameters=false # Whether to expose and assume 1-based page number indexes.
spring.data.web.pageable.page-parameter=page # Page index parameter name.
spring.data.web.pageable.prefix= # General prefix to be prepended to the page number and page size parameters.
spring.data.web.pageable.qualifier-delimiter=_ # Delimiter to be used between the qualifier and the actual page number and size properties.
spring.data.web.pageable.size-parameter=size # Page size parameter name.
spring.data.web.sort.sort-parameter=sort # Sort parameter name.
You can set below in application.yml
spring.data.rest.default-page-size: 50
You can use this Annotation before your Pageable param:
#PageableDefault(size = 40)
// so your parameter should be like this:
#PageableDefault(size = 40) Pageable pageable
** Update:
You can store 40 in application.yml file and use it in the whole project.
in application.yml :
pageSize: 40
then:
// so your parameter should be like this:
#PageableDefault(size = ${pageSize}) Pageable pageable
And, for completeness, here is an example for a Spring Boot configuration. In the #Configuration class that extends WebMvcConfigurerAdapter, set the default page size to 50 items like this:
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setFallbackPageable(new PageRequest(0, 50));
argumentResolvers.add(resolver);
super.addArgumentResolvers(argumentResolvers);
}
This still isn't well documented but for anyone else finding this article, the RepositoryRestConfigurerAdapter has all the spring data rest config there.
#Configuration
public static class RestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultPageSize(50);
}
}
In Spring Boot 2.1.6.RELEASE, you can use below:
#Configuration
public class WebConfig implements WebMvcConfigurer{
#Value("${paging.default.pageSize}")
private int size;
#Value("${paging.default.page}")
private int page;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setFallbackPageable(PageRequest.of(page, size));
resolvers.add(resolver);
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
}
}
Provided answers are very good and should help in most cases. However, I use slightly different approach, which allows me to use different default page size per model and can be configurable with Spring or system properties.
Please note that this approach has one fundamental limitation, namely, it does not accept any size coming with a request; it uses sorting information though. So if you need ability to change number of returned items per page via request parameters, this solution is not for you.
First of all, I created a utility class (or just a method in a controller) which creates a new Pageable instance base on a request Pageable and configured page size
public static Pageable updatePageable(final Pageable source, final int size)
{
return new PageRequest(source.getPageNumber(), size, source.getSort());
}
In a controller I add a variable which holds my default page size (in this case default value is 20 if configuration is not provided):
#Value("${myapplication.model.items-per-page:20}")
private int itemsPerPage;
And then I override (i.e. create a new Pageable instance) default page size in request handling method:
#RequestMapping(method = RequestMethod.GET)
public Page<Model> websites(final Pageable pageable)
{
return repository.findAll(updatePageable(pageable, itemsPerPage));
}
I use different default page size variables for different models / controllers which then can be configured even from system properties.
If there is someone who is trying to use #PageableDefault with spring.data.web.pageable.default-page-size and don't know why the default page size you set ins't working
public #interface PageableDefault {
/**
* The default-size the injected {#link org.springframework.data.domain.Pageable} should get if no corresponding
* parameter defined in request (default is 10).
*/
int size() default 10;
}
As you can see the above code if you use #PageableDefault the default page size is set to 10 no matter what you set spring.data.web.pageable.default-page-size: 99
So if you want to provide default sort condition in a simple way with page size , you can use something like this
#PageableDefault(size = 99, sort = {"createdAt"}, direction = sort.Direction.DESC) Pageable pageable

spring mvc 3 caching example

I have requirement for spring mvc 3 caching. Requirement is : while starting the server, we need to call database for one dropdown and put those values in the cache. So that whenever we required those values, we need to retrieve from cache.
Please help me with an example.
Thanks in advance.
May be you can use init-method (Spring 2.5) or #PostConstruct annotation (in Spring 3.0).
This method will be called during server start up
The following is code snippet
#Component
public class CacheDBData {
private String values[];
//add setter & getter
//This will be called during server start up after properties are initialised
#PostConstruct
public void getDataFromDB() {
values = //Logic to get data from DB and store that in values property
}
}
Suppose for example you can use in class as follows
#controller
public class HomeController {
#Autowired
private CacheDBData cacheDBData ;
//getter and setters
private void methodxyz() {
String values[] = cacheDBData.getValues();
}
}
I've had success with Ehcahe for Spring. There's a couple of config files to setup but after that you simply annotate the methods you want to cache the output from and it just works.
This has the advantage that you can change the values coming back from the service/database and NOT have to restart your app, unlike the accepted answer.

Resources