Spring Data web pagination "page" parameter not working - java

I'm trying to get Spring Data's web pagination working. It's described here:
http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/repositories.html#web-pagination
Here's my Java (Spring Web MVC #Controller handler method):
#RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(
#PageableDefaults(value = 50, pageNumber = 0) Pageable pageable,
Model model) {
log.debug("Params: pageNumber={}, pageSize={}",
pageable.getPageNumber(), pageable.getPageSize());
...
}
And here's my Spring configuration:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.PageableArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
(It appears that the configuration above is the way to do this now; the configuration approach described in the link is deprecated.)
When I actually try to control the pagination using the page and page.size parameters, the latter works just fine, but the former doesn't. For example, if I hit
http://localhost:8080/myapp/list?page=14&page.size=42
the log output is
Params: pageNumber=0, pageSize=42
So I know that the argument resolver is kicking in, but not sure why it's not resolving the page number. I've tried a bunch of other param names (e.g. page.number, pageNumber, page.num, etc.) and none of them work.
Is this working for anybody else?

the page parameter is actually a bit non-intuitive - page.page and not page, changing to page.page should get things to work.

Looking into the PageableArgumentResolver I found that prefix and separator are configurable, so you can configure the class not to have it.
public class PageableArgumentResolver implements WebArgumentResolver {
private static final Pageable DEFAULT_PAGE_REQUEST = new PageRequest(0, 10);
private static final String DEFAULT_PREFIX = "page";
private static final String DEFAULT_SEPARATOR = ".";
in my #Configuration class I did something different to achieve using page, size,sort and sortDir as default.
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableArgumentResolver resolver = new PageableArgumentResolver();
resolver.setPrefix("");
resolver.setSeparator("");
argumentResolvers.add(new ServletWebArgumentResolverAdapter(resolver));
}
Now this works
http://:8080/myapp/list?page=14&size=42

You can override parameters if you want by doing:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver">
<property name="oneIndexedParameters" value="true"></property>
<property name="pageParameterName" value="page"></property>
<property name="sizeParameterName" value="size"></property>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>

Related

How do I perform Constructor-based dependency injection with Spring using annotations?

OK, so if I need to put some primitive values in the constructor, how do I do that?
#Autowired
public CustomBean(String name, #Qualifier("SuperBean") SuperBean superBean) {
super();
this.superBean = superBean;
this.name = name;
}
For instance here I am defining that the superBean has the Qualifier "SuperBean", but I'd also like to know how is it possible to use annotations to set the name value here?
I know it's possible with xml configuration, but I want to know how to do this with annotations too:
<bean id="CustomXmlBean" class="org.arturas.summerfav.beans.CustomXmlBean">
<constructor-arg name="name" type="String" value="The Big Custom XML Bean" />
<constructor-arg>
<bean id="SuperBean" class="org.arturas.summerfav.beans.SuperBean" />
</constructor-arg>
</bean>
Well how do I put in values for String, int and other generic types?
Here is one way to do this:
#Component
public class YourBean {
#Autowired
public YourBean(#Value("${prop1}") String arg1, #Value("${prop2}") String arg2) {
// rest of the code
}
}

Spring 3.2 and Jackson 2: add custom object mapper

I'm developing a REST webservice in spring MVC. I need to change how jackson 2 serialize mongodb objectids. I'm not sure of what to do because I found partial documentation for jackson 2, what I did is to create a custom serializer:
public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
#Override
public void serialize(ObjectId value, JsonGenerator jsonGen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jsonGen.writeString(value.toString());
}
}
Create a ObjectMapper
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
SimpleModule module = new SimpleModule("ObjectIdmodule");
module.addSerializer(ObjectId.class, new ObjectIdSerializer());
this.registerModule(module);
}
}
and then register the mapper
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="my.package.CustomObjectMapper"></bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
My CustomConverter is never called. I think the CustomObjectMapper definition is wrong,I adapted it from some code for jackson 1.x
In my controllers I'm using #ResponseBody.
Where am I doing wrong? Thanks
You should annotate corresponding model field with #JsonSerialize annontation. In your case it may be:
public class MyMongoModel{
#JsonSerialize(using=ObjectIdSerializer.class)
private ObjectId id;
}
But in my opinion, it should better don't use entity models as VOs. Better way is to have different models and map between them.
You can find my example project here (I used date serialization with Spring 3 and Jackson 2 as example).
How I would do this is:
Create an annotation to declare your custom serializers:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyMessageConverter{
}
Set up component scan for this in your mvcconfiguration file
<context:include-filter expression="package.package.MyMessageConverter"
type="annotation" />
and create a class that implements HttpMessageConverter<T>.
#MyMessageConverter
public MyConverter implements HttpMessageConverter<T>{
//do everything that's required for conversion.
}
Create a class that extends AnnotationMethodHandlerAdapter implements InitializingBean.
public MyAnnotationHandler extends AnnotationMethodHandlerAdapter implements InitializingBean{
//Do the stuffs you need to configure the converters
//Scan for your beans that have your specific annotation
//get the list of already registered message converters
//I think the list may be immutable. So, create a new list, including all of the currently configured message converters and add your own.
//Then, set the list back into the "setMessageConverters" method.
}
I believe this is everything that is required for your goal.
Cheers.
There is no need to create object mapper. Add jackson-core-2.0.0.jar and jackson-annotations-2.0.0.jar to your project.
Now, add the following lines of code to your controller while handing the service:
#RequestMapping(value = "students", method = RequestMethod.POST, headers = "Accept=application/json", consumes = "application/json")
public HashMap<String, String> postStudentForm(
#RequestBody Student student, HttpServletResponse response)
Do not miss any of the annotations.

How to prevent parameter binding from interpreting commas in Spring 3.0.5?

Consider the following controller method:
#RequestMapping(value = "/test", method = RequestMethod.GET)
public void test(#RequestParam(value = "fq", required = false) String[] filterQuery) {
logger.debug(fq = " + StringUtils.join(filterQuery, "|"));
}
Here is the output for different fq combinations:
/test?fq=foo results in fq = foo
/test?fq=foo&fq=bar results in fq = foo|bar
/test?fq=foo,bar results in fq = foo|bar
/test?fq=foo,bar&fq=bash results in fq = foo,bar|bash
/test?fq=foo,bar&fq= results in fq = foo,bar|
Example 3 is the problem. I expect (want/need) it to output fq = foo,bar.
I've tried escaping the comma with \ and using %3C but niether work.
If I look at the HttpServletRequest object's version:
String[] fqs = request.getParameterValues("fq");
logger.debug(fqs = " + StringUtils.join(fqs, "|"));
It prints the expected output: fqs = foo,bar. So the "problem" is with the Spring data binding.
I could by-pass Spring's binding and use HttpServletRequest but I really don't want to as I'm using a backing bean in my real code (same thing is happening) and don't wish to re-implement the binding functionality. I'm hoping someone can provide a simple way of preventing this behavior via escaping or some other mechanism.
TIA
UPDATE: I posted this Q on Twitter and got a reply saying the expected output appears with Spring 3.0.4.RELEASE. I've now confirmed this is the case and thus is a temporary fix. I'll go ahead and log this as a bug on the Spring JIRA system. If anyone can provide a work around or fix with 3.0.5, I'll accept their answer.
I've tested your code: it's unbelievable, but I can't reproduce your issue. I've downloaded the latest version of spring (3.0.5), this is my controller:
package test;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping("/test/**")
public class MyController {
private static final Logger logger = Logger.getLogger(MyController.class);
#RequestMapping(value = "/test/params", method = RequestMethod.GET)
public void test(SearchRequestParams requestParams, BindingResult result) {
logger.debug("fq = " + StringUtils.join(requestParams.getFq(), "|"));
}
}
this is my SearchRequestParams class:
package test;
public class SearchRequestParams {
private String[] fq;
public String[] getFq() {
return fq;
}
public void setFq(String[] fq) {
this.fq = fq;
}
}
and this is my simple spring configuration:
<bean id="urlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="test.MyController" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
I've tested my code within tomcat 7.0.8; when I type http://localhost:8080/testweb/test/params.htm?fq=foo,bar I'm able to read in my log file this line: DEBUG fq = foo,bar.
What are the the differences from my code to yours? Am I doing something wrong?
I'd like to help you, so if you have any doubts or if I can do some other tests for you, it will be a pleasure.
UPDATE / SOLUTION
With your code I've reproduced the issue; you have the tag <mvc:annotation-driven /> in your dispatcher servlet configuration, so you silently use a default conversion service, instance of FormattingConversionService, which contains a default converter from String to String[] that uses comma as separator.
You have to use a different conversion service bean containing your own converter from String to String[]. You should use a different separator, I've choosed to use ";" because it's the separator commonly used into query string ("?first=1;second=2;third=3"):
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
public class CustomStringToArrayConverter implements Converter<String, String[]>{
#Override
public String[] convert(String source) {
return StringUtils.delimitedListToStringArray(source, ";");
}
}
Then you have to specify this conversion service bean in your configuration:
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="au.org.ala.testspringbinding.CustomStringToArrayConverter" />
</list>
</property>
</bean>
The issue has fixed, now you should check for any side effects. I hope you don't need in your application the original conversion from String to String[] (with comma as separator). ;-)
I have found the most elegant and the shortest way for me - add #InitBinder to a #Controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor(null));
}
It will convert String to String[] without using separator (null param), with Spring class org.springframework.beans.propertyeditors.StringArrayPropertyEditor.
If someone in same project will use new default conversion way, it will be ok.
As suggested by Philip Potter, I'm posting the "update" to my question as an answer, as it might've been easy to miss...
Down-grading from Spring 3.0.5.RELEASE to 3.0.4.RELEASE fixed the issue, when using the #RequestParam annotation, suggesting it is a bug with 3.0.5.
However, it does NOT fix the related issue, when binding to a form-backing bean - which is what I have in my webapp. I've tested all version back to 3.0.0.RELEASE and get the same result (/test?fq=foo,bar produces fq = foo|bar).
E.g.
#RequestMapping(value = "/test", method = RequestMethod.GET)
public void test(SearchRequestParams requestParams, BindingResult result) {
logger.debug("fq = " + StringUtils.join(requestParams.getFq(), "|"));
}
where SearchRequestParams contains a field String[] fq.
If anyone has a fix for this, I'll gladly accept their answer.
rougou's comment was the only solution that worked for me because I was using List<String> instead of String[].
Add this to your controller to eliminate parsing comma-deliminated values:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(List.class, new CustomCollectionEditor(List.class));
}
javanna already pointed out the correct root cause. I just wanted to further point out that you can also remove the StringToArrayConverter altogether as shown here and here.
The following is my journey of bypassing comma delimiting in a MVC request
This is my understanding of Springs evolution of these type of solutions:
The documentation is very vague about what is the latest solution.
First was WebMvcConfigAdapter implementing the interface WebMvcConfig. This was eventually deprecated
That was replaced by WebMvcConfigSupport. This was eventually deprecated but it was better than the first solution.
The main issued was that it turned off the MVC auto-configuration and had side issues like swagger.html not working and
actuator info being missing pretty format and the date became a large decimal
The latest is a revised interface WebMvcConfig that implements default methods using Java 8 features.
Create a class in my case, WebConfigUUID could be AnyClass, that implements the later version of WebMvcConfig
This allows you to change what you need, in our case a custom converter,
without impacting anything else or having to override another to get swagger to work,
or deal with actuator info output
The following are the two classes that implemented my change to bypass comma delimiting of strings to a list
in processing the MVC request:
It still produces a list of strings but with only one value.
import java.util.Collection;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class WebConfigUUID implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.removeConvertible(String.class,Collection.class);
registry.addConverter(String.class,Collection.class,BypassCommaDelimiterConfiguration.commaDelimiterBypassedParsingConverter());
}
}
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
public class BypassCommaDelimiterConfiguration {
public static Converter<String, List<String>> commaDelimiterBypassedParsingConverter() {
return new Converter<String, List<String>>() {
#Override
public List<String> convert(final String source) {
final List<String> classes = new ArrayList<String>();
classes.add(source);
return classes;
}
};
}
}
Its a hack but, have you considered passing your params delimited with '-'
/test?fq=foo-bar results in fq = foo-bar
/test?fq=foo-bar&fq=bash results in fq = foo-bar|bash
Or any other delimiter maybe ~, or !,or ^, or ???

Spring 3: disable SpEL evaluation of a bean property value?

We're in the process of updating our apps from Spring 2.5 to 3.0 and we've hit a problem with the new SpEL evaluation of bean properties.
We've been using an in-house templating syntax in one module which unfortunately uses the same "#{xyz}" markup as SpEL. We have a few beans which take string's containing these expressions as properties but spring assumes they are SpEL expressions and throws a SpelEvaluationException when it tries to instantiate the bean.
e.g.
<bean id="templatingEngine" class="com.foo.TemplatingEngine">
<property name="barTemplate" value="user=#{uid}&country=#{cty}"/>
</bean>
Is it possible to disable SpEL evaluation, ideally per-bean, but alternatively for the whole application context?
Alternatively is there a way to escape the values?
Thanks,
Stephen
Completely disable SpEL evaluation by calling the bean factory setBeanExpressionResolver method passing in null. You can define a BeanFactoryPostProcessor to do this.
public class DisableSpel implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory)
throws BeansException
{
beanFactory.setBeanExpressionResolver(null);
}
}
Then define this bean in the application context.
<bean class="com.example.spel.DisableSpel"/>
Well what you could do is re-define the expression language delimiters.
I would say the way to do this is through a special bean that implements BeanFactoryPostProcessor (thanks to inspiration by Jim Huang):
public class ExpressionTokensRedefiner implements BeanFactoryPostProcessor{
private BeanExpressionResolver beanExpressionResolver;
public void setBeanExpressionResolver(
final BeanExpressionResolver beanExpressionResolver){
this.beanExpressionResolver = beanExpressionResolver;
}
#Override
public void postProcessBeanFactory(
final ConfigurableListableBeanFactory beanFactory)
throws BeansException{
beanFactory.setBeanExpressionResolver(createResolver());
}
private String expressionPrefix = "${";
private String expressionSuffix = "}";
public void setExpressionPrefix(final String expressionPrefix){
this.expressionPrefix = expressionPrefix;
}
public void setExpressionSuffix(final String expressionSuffix){
this.expressionSuffix = expressionSuffix;
}
private BeanExpressionResolver createResolver(){
if(beanExpressionResolver == null){
final StandardBeanExpressionResolver resolver =
new StandardBeanExpressionResolver();
resolver.setExpressionPrefix(expressionPrefix);
resolver.setExpressionSuffix(expressionSuffix);
return resolver;
} else{
return beanExpressionResolver;
}
}
}
Define it as a bean like this:
<bean class="foo.bar.ExpressionTokensRedefiner">
<property name="expressionPrefix" value="[[" />
<property name="expressionSuffix" value="]]" />
</bean>
or like this:
<!-- this will use the default tokens ${ and } -->
<bean class="foo.bar.ExpressionTokensRedefiner" />
or use a custom resolver:
<bean class="foo.bar.ExpressionTokensRedefiner">
<property name="beanExpressionResolver">
<bean class="foo.bar.CustomExpressionResolver" />
</property>
</bean>
Now you can leave your definitions untouched and if you want to use SpEL, use the new delimiters.
EDIT: now I did test it and it actually works.
<bean class="foo.bar.ExpressionTokensRedefiner">
<property name="expressionPrefix" value="[[" />
<property name="expressionSuffix" value="]]" />
</bean>
<bean class="foo.bar.FooFritz">
<property name="fizz" value="[[ systemProperties['user.home'] ]]"></property>
<property name="fozz" value="[[ systemProperties['java.io.tmpdir'] ]]"></property>
<!-- this is what it would normally choke on -->
<property name="fazz" value="#{ boom() }"></property>
</bean>
Test code:
final ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("classpath:foo/bar/ctx.xml");
context.refresh();
final FooFritz fooFritz = context.getBean(FooFritz.class);
System.out.println(fooFritz.getFizz());
System.out.println(fooFritz.getFozz());
System.out.println(fooFritz.getFazz());
Output:
/home/seanizer
/tmp
#{ boom() }
I am not a dab, but this mighbe of help.
https://issues.apache.org/jira/browse/CAMEL-2599

annotations in Spring MVC

I'd like to convert this SimpleFormController to use the annotation support introduced in Spring MVC 2.5
Java
public class PriceIncreaseFormController extends SimpleFormController {
ProductManager productManager = new ProductManager();
#Override
public ModelAndView onSubmit(Object command)
throws ServletException {
int increase = ((PriceIncrease) command).getPercentage();
productManager.increasePrice(increase);
return new ModelAndView(new RedirectView(getSuccessView()));
}
#Override
protected Object formBackingObject(HttpServletRequest request)
throws ServletException {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
return priceIncrease;
}
}
Spring Config
<!-- Include basic annotation support -->
<context:annotation-config/>
<!-- Comma-separated list of packages to search for annotated controllers. Append '.*' to search all sub-packages -->
<context:component-scan base-package="springapp.web"/>
<!-- Enables use of annotations on controller methods to map URLs to methods and request params to method arguments -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean name="/priceincrease.htm" class="springapp.web.PriceIncreaseFormController">
<property name="sessionForm" value="true"/>
<property name="commandName" value="priceIncrease"/>
<property name="commandClass" value="springapp.service.PriceIncrease"/>
<property name="validator">
<bean class="springapp.service.PriceIncreaseValidator"/>
</property>
<property name="formView" value="priceincrease"/>
<property name="successView" value="hello.htm"/>
<property name="productManager" ref="productManager"/>
</bean>
Basically, I'd like to replace all the XML configuration for the /priceincrease.htm bean with annotations within the Java class. Is this possible, and if so, what are the corresponding annotations that I should use?
Thanks,
Don
It'll look something like the following, although whether it works or not exactly as is will depend a bit on your configuration (view resolver, etc). I should also note that there are about eight billion valid ways to write this thing. See the Spring documentation, 13.11.4 "Supported handler method arguments and return types" for an overview of the insanity. Also note that you can autowire the validator
#Controller
#RequestMapping("/priceincrease.htm")
public class PriceIncreaseFormController {
ProductManager productManager;
#Autowired
public PriceIncreaseFormController(ProductManager productManager) {
this.productManager = productManager;
}
// note: this method does not have to be called onSubmit
#RequestMapping(method = RequestMethod.POST)
public String onSubmit(#ModelAttribute("priceIncrease") PriceIncrease priceIncrease, BindingResult result, SessionStatus status {
new PriceIncreaseValidator().validate(priceIncrease, result);
if (result.hasErrors()) {
return "priceincrease";
}
else {
int increase = priceIncrease.getPercentage();
productManager.increasePrice(increase);
status.setComplete();
return "redirect:hello.htm";
}
}
// note: this method does not have to be called setupForm
#RequestMapping(method = RequestMethod.GET)
public String setupForm(Model model) {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(20);
model.addAttribute("priceIncrease", priceIncrease);
return "priceincrease";
}
}
Someone completed this project with a recent MVC and it's on github, so you can see how all the classes are changed compared to Spring's tutorial.
Link: PriceIncreaseFormController

Categories