I would like to use a custom WebArgumentResolver for id -> entity. Easy enough if I use request parameters: use the parameter key to determine the entity type and look up accordingly.
But I would like it to be like the @PathVariable annotation.
eg.
http://mysite.xzy/something/enquiryId/itemId would trigger this method
@RequestMapping(value = "/something/{enquiry}/{item}")
public String method(@Coerce Enquiry enquiry, @Coerce Item item)
@Coerce annotations would tell the WebArgumentResolver to use the particular service based on it's type.
Problem is working out which uri part belongs to entity.
Can someone explain how PathVariable annotation does it. And is it possible to emulate it with my custom annotation.
Thanks.
You can use @InitBinder to let spring know how to coerce a given String to your custom type.
You'd want something like:
@RequestMapping(value = "/something/{enquiry}")
public String method(@PathVariable Enquiry enquiry) {...}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Enquiry.class, new PropertyEditorSupport() {
@Override
public String getAsText() {
return ((Enquiry) this.getValue()).toString();
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Enquiry(text));
}
});
}
For a generic approach where you don't need to specify one PropertyEditor for each entity you could use a ConditionalGenericConverter:
public final class GenericIdToEntityConverter implements ConditionalGenericConverter {
private static final Logger log = LoggerFactory.getLogger(GenericIdToEntityConverter.class);
private final ConversionService conversionService;
@PersistenceContext
private EntityManager entityManager;
@Autowired
public GenericIdToEntityConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return ImmutableSet.of(new ConvertiblePair(Number.class, AbstractEntity.class),
new ConvertiblePair(CharSequence.class, AbstractEntity.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return AbstractEntity.class.isAssignableFrom(targetType.getType())
&& this.conversionService.canConvert(sourceType, TypeDescriptor.valueOf(Long.class));
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Long id = (Long) this.conversionService.convert(source, sourceType, TypeDescriptor.valueOf(Long.class));
Object entity = entityManager.find(targetType.getType(), id);
if (entity == null) {
log.info("Did not find an entity with id {} of type {}", id, targetType.getType());
return null;
}
return entity;
}
}