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
Related
I am using following controller method
public String listUsers(#PageableDefault(size = 20, page = 0) Pageable pageable)
And obviously, we can controll page and size with query params size and page. But lets say, we want to protect ourselfs from dumping whole database in one page by user or due to tempering. Is there a limit, that can be set to Pageable object eg. #DefaultPageableConstrains or I have to validate size by hand?
I just would like to make use of Spring validation mechanisms like #Valid #Min or #Max annotations.
The default maxPageSize is 2000. This can be overriden like so:
#Configuration
public class PaginationConfiguration extends SpringDataWebConfiguration {
private final int MAX_PAGE_SIZE = 200;
#Bean
#Override
public PageableHandlerMethodArgumentResolver pageableResolver() {
PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver =
new PageableHandlerMethodArgumentResolver(sortResolver());
pageableHandlerMethodArgumentResolver.setMaxPageSize(MAX_PAGE_SIZE);
return pageableHandlerMethodArgumentResolver;
}
}
So I want to limit maximum pageable size value to 10 (example value) and I can do this like that:
#Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setMaxPageSize(10);
argumentResolvers.add(resolver);
}
}
and delcare controller method like this
#RequestMapping(name = "list")
public String listUsers(#PageableDefault(size = 5, page = 0) Pageable pageable) {
Indeed, this will work, I wont be able to set page size to>10 but I am curious why? What is happening to Spring created PageableHandlerMethodArgumentResolver? Why this instance is taken into consideration and not the default one? After all, I am no replacing resolvers here, only adding a new one.
1. Configuration phase
When you extend WebMvcConfigurerAdapter and add custom resolvers in addArgumentResolvers, (skipping lots of configuration code) you are actually adding them to RequestMappingHandlerAdapter RequestMappingHandlerAdapter bean saves internally the list of all resolvers, provided during initialization. (WebMvcConfigurationSupport). After that they are combined with default resolvers. And as you can see from the source code, PageableHandlerMethodArgumentResolver is not actually part of the default resolvers list, but comes from some configuration class. And in my case (screenshot below) the spring-boot-starter-data-rest configuration classes provide different version of PageableHandler: HateoasPageableHandlerMethodArgumentResolver
2. Resolvers Order
Custom resolvers are ordered after built-in ones (source). So lets check this and call some controller, but put breakpoint in RequestMappingHandlerAdapter.invokeHandlerMethod() first. From here we can see the internal state of RequestMappingHandlerAdapter
I highlighted custom resolver MyPageableHandlerMethodArgumentResolver, registered the same way you did in your question code.
And the code that actually resolves arguments is in HandlerMethodArgumentResolverComposite. It is simple loop and it means that the first registered HandlerMethodArgumentResolver will be used
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers)
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
And as you can see from the source code, the result is cached and never iterated again for the same parameter type.
I am using Spring Boot v1.5.2.RELEASE.
My controller is like this:
#PostMapping(path = "/list_praxis")
public
#ResponseBody
ModelAndView login(Pageable pageable,HttpServletRequest request) {
return new ModelAndView(new WebJsonView());
}
My parameters size:10 page:0, Pageable works fine.
But now I want to change my parameters to pageSize:10 currentPage:0.
Pageable doesn't work, because pageable can only receive size and page, and doesn't support other parameter names.
How can I configure this to use pageSize and currentPage instead of size and page?
You can also use spring's application properties like this
spring.data.web.pageable.page-parameter: pageNumber
spring.data.web.pageable.size-parameter: pageSize
You can learn about other common application properties here.
I think this would work for you.
#Configuration
public class RepositoryRestMvcConfiguration extends SpringBootRepositoryRestMvcConfiguration{
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setPageParamName("p")
.setLimitParamName("l")
.setSortParamName("s");
}
}
This configuration would set the paging parameter to p, the size parameter to l, and the sort parameter to s:
I would like to port two projects to Spring Boot 1.1.6. The are each part of a larger project. They both need to make SQL connections to 1 of 7 production databases per web request based region. One of them persists configuration setting to a Mongo database. They are both functional at the moment but the SQL configuration is XML based and the Mongo is application.properties based. I'd like to move to either xml or annotation before release to simplify maintenance.
This is my first try at this forum, I may need some guidance in that arena as well. I put the multi-database tag on there. Most of those deal with two connections open at a time. Only one here and only the URL changes. Schema and the rest are the same.
In XML Fashion ...
#Controller
public class CommonController {
private CommonService CommonService_i;
#RequestMapping(value = "/rest/Practice/{enterprise_id}", method = RequestMethod.GET)
public #ResponseBody List<Map<String, Object>> getPracticeList(#PathVariable("enterprise_id") String enterprise_id){
CommonService_i = new CommonService(enterprise_id);
return CommonService_i.getPracticeList();
}
#Service
public class CommonService {
private ApplicationContext ctx = null;
private JdbcTemplate template = null;
private DataSource datasource = null;
private SimpleJdbcCall jdbcCall = null;
public CommonService(String enterprise_id) {
ctx = new ClassPathXmlApplicationContext("database-beans.xml");
datasource = ctx.getBean(enterprise_id, DataSource.class);
template = new JdbcTemplate(datasource);
}
Each time a request is made, a new instance of the required service is created with the appropriate database connection.
In the spring boot world, I've come across one article that extended TomcatDataSourceConfiguration.
http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html That at least allowed me to create a java configuration class however, I cannot come up with a way to change the prefix for the ConfigurationProperties per request like I am doing with the XML above. I can set up multiple configuration classes but the #Qualifier("00002") in the DAO has to be a static value. //The value for annotation attribute Qualifier.value must be a constant expression
#Configuration
#ConfigurationProperties(prefix = "Region1")
public class DbConfigR1 extends TomcatDataSourceConfiguration {
#Bean(name = "dsRegion1")
public DataSource dataSource() {
return super.dataSource();
}
#Bean(name = "00001")
public JdbcTemplate jdbcTemplate(DataSource dsRegion1) {
return new JdbcTemplate(dsRegion1);
}
}
On the Mongo side, I am able to define variables in the configurationProperties class and, if there is a matching entry in the appropriate application.properties file, it overwrites it with the value in the file. If not, it uses the value in the code. That does not work for the JDBC side. If you define a variable in your config classes, that value is what is used. (yeah.. I know it says mondoUrl)
#ConfigurationProperties(prefix = "spring.mongo")
public class MongoConnectionProperties {
private String mondoURL = "localhost";
public String getMondoURL() {
return mondoURL;
}
public void setMondoURL(String mondoURL) {
this.mondoURL = mondoURL;
}
There was a question anwsered today that got me a little closer. Spring Boot application.properties value not populating The answer showed me how to at least get #Value to function. With that, I can set up a dbConfigProperties class that grabs the #Value. The only issue is that the value grabbed by #Value is only available in when the program first starts. I'm not certain how to use that other than seeing it in the console log when the program starts. What I do know now is that, at some point, in the #Autowired of the dbConfigProperties class, it does return the appropriate value. By the time I want to use it though, it is returning ${spring.datasource.url} instead of the value.
Ok... someone please tell me that #Value is not my only choice. I put the following code in my controller. I'm able to reliably retrieve one value, Yay. I suppose I could hard code each possible property name from my properties file in an argument for this function and populate a class. I'm clearly doing something wrong.
private String url;
//private String propname = "${spring.datasource.url}"; //can't use this
#Value("${spring.datasource.url}")
public void setUrl( String val) {
this.url = val;
System.out.println("==== value ==== " + url);
}
This was awesome... finally some progress. I believe I am giving up on changing ConfigurationProperties and using #Value for that matter. With this guy's answer, I can access the beans created at startup. Y'all were probably wondering why I didn't in the first place... still learning. I'm bumping him up. That saved my bacon. https://stackoverflow.com/a/24595685/4028704
The plan now is to create a JdbcTemplate producing bean for each of the regions like this:
#Configuration
#ConfigurationProperties(prefix = "Region1")
public class DbConfigR1 extends TomcatDataSourceConfiguration {
#Bean(name = "dsRegion1")
public DataSource dataSource() {
return super.dataSource();
}
#Bean(name = "00001")
public JdbcTemplate jdbcTemplate(DataSource dsRegion1) {
return new JdbcTemplate(dsRegion1);
}
}
When I call my service, I'll use something like this:
public AccessBeans(ServletRequest request, String enterprise_id) {
ctx = RequestContextUtils.getWebApplicationContext(request);
template = ctx.getBean(enterprise_id, JdbcTemplate.class);
}
Still open to better ways or insight into foreseeable issues, etc but this way seems to be about equivalent to my current XML based ways. Thoughts?
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.