mercredi 8 avril 2015

Spring - creating links between existing objects

I'm trying to create links between two existing objects using form with checkboxes, e.g. categories of products and products itself. The link between them is one-to-many and is managed by Hibernate. However, I'm struggling to pass my objects between controllers with JSP.


I have CategoryViewController with form-init method:



...
@RequestMapping("/details")
@Transactional(readOnly=true)
public ModelAndView showCategoryDetails(@RequestParam Integer categoryId, @ModelAttribute CategoryProductAssignment assignmentModel, Model model) {
Category category = categoryService.getCategory(categoryId);

Set<Product> categoryProducts = category.getProducts();
List<Product> allProducts = productService.getProducts();

model.addAttribute("categoryProducts", categoryProducts);
model.addAttribute("allProducts", allProducts);
model.addAttribute("category", category);

assignmentModel.setCategory(category);

return new ModelAndView("categoryDetails");
}
...


In this method I form the list of all available products, products already assigned to category and category itself and sets it in a model.


Also, I have a jsp which uses previous model and creates this form:



...
<form:form method="post" action="assignProducts.html" commandName="categoryProductAssignment">
<form:hidden path="category.id" />
<tr>
<td><form:checkboxes path="products" items="${allProducts}" itemValue="id" itemLabel="name"/></td>
<td>${product.name}</td>
<br>
</tr>
<input type="submit" value="ok">
</form:form>
...


And also I have another CategoryAssignController, which processes form after submit:



...
@InitBinder
public void bindForm(final WebDataBinder binder) {
binder.registerCustomEditor(List.class, "products", new ProductCollectionEditor<ProductService>(productService, List.class));
binder.registerCustomEditor(Category.class, new CategoryEditor<CategoryService>(categoryService));
}

@RequestMapping(value = "/category/assignProducts", method = RequestMethod.POST)
public String assignProducts(@ModelAttribute CategoryProductAssignment assignmentModel) {
Category category = assignmentModel.getCategory();
Integer id = category.getId();

List<Product> products = assignmentModel.getProducts();

categoryService.addProducts(category, products);

return "redirect:/categoryDetails.html?categoryId=" + id;
}
...


The problem what I'm facing is to transfer Category and Products objects from one controller to another. I don't want to store it in session, so I need to pass them via jsp.


I have no problems with List formed with checkboxes. To do this I've created ProductCollectionEditor.class:



public class ProductCollectionEditor<T extends ProductService> extends CustomCollectionEditor {
private final T service;

public ProductCollectionEditor(final T service, final Class<List> collectionType) {
super(collectionType, true);
this.service = service;
}

@Override
protected Object convertElement(final Object element) {
try {
Product product = service.getProduct(Integer.valueOf(element.toString()));
return product;
} catch (NumberFormatException e) {
return null;
}
}
}


and registered it



binder.registerCustomEditor(List.class, "products", new ProductCollectionEditor<ProductService>(productService, List.class));


in my CategoryAssignController using @InitBinder.


However, I can't do the same with Category for some reason. I've created CategoryEditor:



public class CategoryEditor<T extends CategoryService> extends PropertyEditorSupport {

private final T service;

public CategoryEditor(final T service) {
super();
this.service = service;
}

@Override
public void setValue(Object value) {
Category category = service.getCategory(Integer.valueOf(value.toString()));
setValue(category);
}

}


and registered it


binder.registerCustomEditor(Category.class, new CategoryEditor(categoryService));


in my CategoryAssignController using @InitBinder.


Important note: I don't want to pass ids as Strings and find\get objects manually in Controller\Services.


As I can guess, when Spring sees "category.id" coming from form



<form:hidden path="category.id" />


it automatically constructs Category object and sets id in there. So, in this case, inside of CategoryEditor.setValue method I already have Category object created. But, if I change editor registration line to following:



binder.registerCustomEditor(String.class, "category.id",new CategoryEditor<CategoryService>(categoryService));


method CategoryEditor.setValue is not called at all.


So, the main questions of this post: what am I doing wrong and what is the best approach \ best practice to do what I need?


Thanks.


Aucun commentaire:

Enregistrer un commentaire