Hi All,
I am putting together a generic online shop webapp using the open session in view pattern (I begin the Hibernate session as soon as the request thread hits my control servlet and end it after the page has been rendered with a servlet filter).
Almost everything is working fine, but I do have a couple of issues. The first problem is that if a user clicks on the 'add to basket' button several times in quick succession (i.e. before waiting for the request to be processed and the response to return) then several instances of the same item get added to their basket. This is particularly bad because multiple clicks like this will cause the item to be added even if the first click should reduce the stock to zero!
Here is the code in my AddToBasketAction class:
Code:
//get the user id from the request and get an instance of the User object from the hibernate session
User user = ActionUtils.getUser(request, false);
Basket basket = user.getBasket();
Map paramMap = request.getParameterMap();
// productId from request
Product product = productDAO.findById(productId, true);
BasketItem item = new BasketItem(product, 1);
try {
//adds item to the basket, decrements product stock by 1. If the product has a stock value of zero, an OutOfStockException is thrown
basket.addBasketItem(item);
} catch (OutOfStockException e) {
logDebug("Product " + product.toString() + " is out of stock.", request);
errors.addError("Sorry, this item is out of stock");
}
Action.findAction("redirectToLastPageAction").getCommand().doCommand(request, response);
First point to note: rather than storing an actual User object inside my HttpSession, I'm storing the user id - every time I need to get hold of an instance of the User object I just use my UserDAO to get me a fresh copy from the database. Likewise with the current Product etc - I'm doing a lookup using the id each time. This is bad, right?
Let's say I have a Product with a "stock" value of 1. A user should be able to buy only one instance of that Product. Clearly each time a request thread enters the above method, it's looking up the Product from the database, seeing that the stock is greater than zero and proceeding with adding the Product to the basket. Now if a second request thread enters the above method before the hibernate session associated with the previous request has closed, the second request thread will also see that the Product has a stock value of 1 and allow the basket add to proceed. Likewise with the 3rd, 4th and 5th request threads.
I've tried all mannor of session.flush() and session.refresh(product) method calls in an attempt to make sure my view of the data within this method is consistent with the underlying database, but I still seem to see the Product object as it was before the first request thread added it to the basket. I've also tried to synchronize the method, and also to set the LockMode to UPGRADE when obtaining the Product instance from the hibernate session.
If anyone has any ideas I'd be grateful!
Cheers,
Richard.
Hibernate version: 3.2.3 Name and version of the database you are using: MySQL 5Code: