I am trying to understand how Wicket LoadableDetachable model is working. What i understand from the Wicket documentation is in a normal scenario , when the request completes the processing wicket will auto serialize the all components with associated model values. This one will consumes more memory . If we use the LoadableDetachable model at the time of serialization the model values will not get serialized. Is this right? . So it will detach the model object automatically . So For the next request the model value will be reload again automatically? See my below code.
public class ProductListPanel extends Column<Product> {
@SpringBean
private ProductService productService;
private List productList;
public ProductListPanel(String id) {
super(id);
class ProductModel extends LoadableDetachableModel {
@Override
public void detach() {
// TODO Auto-generated method stub
productList = null;
System.out.print("Called Detach Object\n");
}
@Override
protected Object load() {
// TODO Auto-generated method stub
productList = productService.findAll();
System.out.print("Called Get Object\n");
return productList;
}
}
System.out.print("Before creating also calling\n");
final ProductModel productModel = new ProductModel();
ListView view = new ListView("list", productModel) {
protected void populateItem(ListItem item) {
System.out.print("\nBefore start also calling\n");
System.out.print("Before this one is callling \n");
Product result = (Product) item.getModelObject();
item.add(new Label("code", result.getCode()));
item.add(new Label("name", result.getName()));
final Link deleteLink = new Link("remove", item.getModel()) {
@Override
public void onClick() {
Product product = (Product) getModelObject();
productService.delete(product);
}
};
item.add(deleteLink);
}
};
add(view);
Link addProductLink = new Link("addProduct") {
@Override
public void onClick() {
// TODO Auto-generated method stub
setResponsePage(new AddProduct());
}
};
add(addProductLink);
productModel.detach();
}
}In the above code i am listing all the products from the DB. and i am having Remove link for each product , when we are clicking that link i am removing the product from the DB. After clicking the Remove link the page is not getting refreshed means it still showing the deleted product . if i add this line productModel.detach(); then it is working properly . My question here is why i have to call productModel.detach(); manually? LoadableDetachableModel suppose do automatically right?
Please help me
LoadableDetachableModel caches the retrieved object for the duration it is attached. When you load the data from the database, all items are there. Then you execute a query to delete a record, but don't update the list. So either you have to change your onClick handler to also update the retrieved list, or just detach the model.
There is no magic in LoadableDetachableModel. Take a look at the code below:
List<Person> people = dao.getListOfPeopleAttendingParty();
// assume that the number of people attending the party is 4 assert people.size() == 4;
Person guest = people.get(0); dao.delete(guest);
// what is the number of people attending the party now? assert people.size() == ?;What do you think that people.size() is now?
The execution of LoadableDetachableModel is not any different than the code snippet above:
List<Person> people = peopleModel.getObject(); // is called by ListView
// assume that the number of people attending the party is 4 assert people.size() == 4;
Person guest = people.get(0); dao.delete(guest);
// what is the number of people attending the party now? assert people.size() == ?;So: no magic involved, just plain Java and ordinary logic.
PS. The answer to both questions is 4 even though the database no longer holds the guest
This topic is a wide one. LoadableDetachableModel is great for read-access to data since you get the advantage of traversing the load method only once per request. However, this may not be enough depending on what you want to achieve.
Since LoadableDetachableModel stores the once loaded value within a member variable for the duration of the request, it may not see updates to the underlying data that occur within the same request. Whether or not this is a problem depends of course on the timing of the calls to getObject on the model instances involved. It should be a rather rare problem actually.
I think first one needs to consider the component structure and the model instances that are involved. There may be several possible designs to achieve the same thing.
Depending on what the component structure looks like and how many model instances get created and assigned to components, there may be more than one instance of a specific model within a request. Also, if page serialization IS activated, components that somehow cling to their model instances may end up with further instances of models, which in case of LoadableDetachableModels may become an issue within later requests.
It is in my opinion a good practice to inject model instances into the constructors of components and make sure that there are only as many model instances as really needed. Components that get a model instance passed in the constructor, but do not need it themselves, should simply delegate it to constructors of one or more relevant child components. The API of the component get defined quite clearly in the constructor using IModel typed parameters. The only drawback may be that the user of the component needs to pass a lot of models depending on what is going on. Certainly models may be wrapped in decorators for any kind of purposes by the components even in this pattern.
For some purposes LoadableDetachableModel may not be enough, i.e. solutions with more features should be considered for implementation, for example a CachedModel that looks into a cache before performing an actual load.
In realy complex component interplay situations or request handling situations, one may consider to implement a reset mechanism for the LoadableDetachableModel in an own specialized LoadableDetachableModel class. A timestamped reset token could be published into the request scope to notify other addressed model instances that they have to discard their member variable value and traverse the load method again when their getObject method gets called. Each model instance should only honor each token once per request, while more than one token may be issued per request.
Or consider the case that there is no caching layer to access the data and therefore somehow you need sophisticated caching in the frontend layer. One may consider to implement a CachedModel as a specialization of LoadableDetachableModel - i.e. it has three places to look for the model value in getObject, the member variable, then the cache and then finally the load method - with the additional feature to publish a timestamped reset token into the request scope to notify addressed CachedModels that they have to discard their member variable value before returning anything. This additional reset feature can make sure that after an update to a value the relevant CachedModel instances would discard their possibly stored value of their member variable and take look into the central cache.
Of course there are other ways to achieve certain things with models. It depends what you want to do, on your design and how many components within a page need the data in question at which points in time.