|
|
Problem of stale data in hibernate session, in Controller
Hello,
I am a new spring user and currently working on my first spring project (which uses Appfuse as a base) and having a problem to make few things work. The problem is based on the fact that Hibernate session has first-level cache in which it stores objects when it retrieves them from the database, and that Hibernate session is spanning user request, so I can not manage to make my controller read the updated object directly from DB and not from cache.
Application flow is classic and looks like this:
User makes a new order by completing a form and clicking on Submit. I have one AbstractWizardFormController which handles form processing and creates a new order based on the input. It sets order's status to PENDING, stores it in the db, and also sends the order to the queue for async. processing.
After the wizard controller returnsand show pleasewait page, the user is redirected to (or clicks) orderResults.html, and DispatcherServlet forwards request to OrderResultsController which *should* periodically check the status of the order by querying the DB - but it does not do that properly. It actually queries the DB only the first time and after that always retrieves order status from the session cache, thus not reacting to updates of the order status.
And the order status has been changed meanwhile by the order processing engine, and updated status was stored to db.
Could you please help me to solve this – I read a lot of material and I am still clueless…
In hibernate manual I've seen that first-level cache could be cleaned with evict - is it a right way to go and how it can be called from spring controller?
Based on what I read on this forum I suppose that the problem also probably has something to do with the OpenSessionInViewFilter which is by default turned on in the appfuse app. I tried to remove it, but after that I get LazyLoadExceptions, which I’ve seen that are common question on this forums, but I would rather prefer to avoid with having to deal with changing the whole app to work with eager loading, as I’ve seen that lazy load is true in hibernate3 by default. (and the whole issue also looks rather confusing).
Could you please advise me how to resolve this situation?
Tnx a million!!!
Ana
I've included the controller code below, in case that I am doing something wrong:Code:
public class OrderResultsController extends AbstractController #123;
private OrderManager orderManager = null;
private UserManager userManager = null;
private int maxNumberOfTrials;
private int period;
public void setOrderManager#40;OrderManager orderManager#41;#123; this.orderManager=orderManager; #125;
public void setUserManager#40;UserManager userManager#41; #123; this.userManager = userManager; #125;
public void setMaxNumberOfTrials#40;int maxNumberOfTrials#41; #123; this.maxNumberOfTrials = maxNumberOfTrials; #125;
public void setPeriod#40;int period#41; #123; this.period = period; #125;
public ModelAndView handleRequestInternal#40;fromServletRequest request, fromServletResponse response#41; throws Exception #123;
OrderStatus status = null;
if #40;request.getSession#40;#41;==null#41; return new ModelAndView#40;quot;paymentResultsquot;,quot;statusquot;,status#41;;
String orderId = #40;String#41; request.getSession#40;#41;.getAttribute#40;Constants.ORDER_ID#41;;
boolean executed = orderManager.isExecuted#40;orderId#41;;
int tryCount=0;
status = OrderStatus.PENDING;
while #40;!executed amp;amp; tryCountlt;maxNumberOfTrials#41; #123;
status = orderManager.getOrderStatus#40;orderId#41;;
if #40;status == OrderStatus.PENDING#41; #123;
tryCount++;
try#123; Thread.sleep#40;period#41;; #125; catch #40;InterruptedException e#41; #123;#125;
#125;
else #123;
executed=true;
Order order = orderManager.getOrder#40;orderId#41;;
order.setExecuted#40;true#41;;
orderManager.saveOrder#40;order#41;;
if #40;status == OrderStatus.AUTHORIZED#41; #123;
User user = order.getUser#40;#41;;
Double accountBalance = user.getAccountBalance#40;#41;;
accountBalance+=order.getSubscription#40;#41;.getCredits#40;#41;;
user.setAccountBalance#40;accountBalance#41;;
userManager.saveUser#40;user#41;;
#125; else if #40; status == OrderStatus.FAILED#41; #123;
User user = order.getUser#40;#41;;
if #40;user.getVersion#40;#41; == 0#41; #123;
user.setEnabled#40;Boolean.FALSE#41;;
userManager.saveUser#40;user#41;;
#125;
#125;
#125;
#125;
return new ModelAndView#40;quot;orderResultsquot;,quot;statusquot;,status#41;; #125;
#125;The Hibernate Session has an Evict method which you could use. There may be a better way than that, though.
Tnx for the quick answer! I have also seen session evict in hibernate manual, but I am not pretty sure how to use this and also some other hibernate features from the .
Actually I do not know how to reuse information from the manual because there everything is done programmatically (open session, start transaction, do some work, close all..) and as far as I have seen – the only hibernate calls are in the methods of dao objects –where we call getHibernateTemplate and do queries.
Is there maybe more to it? (tnx for any hint/link/tutorial)
Could you maybe explain me how to get hibernate session interface from my web controller (on which I could call evict method)?
Hello to all,
I managed to find a working solution for the problem of stale data in session with calling session.clear(). Interesting enough, even after calling session.evict(status) the order status was still retrieved from the cache, and not from the DB .
So this code now works nicely:
Code:
public ModelAndView handleRequestInternal#40;fromServletRequest request, fromServletResponse response#41; throws Exception #123;
OrderStatus status = null;
if #40;request.getSession#40;#41;==null#41; return new return new ModelAndView#40;quot;orderResultsquot;,quot;statusquot;,status#41;;
String orderId = #40;String#41; request.getSession#40;#41;.getAttribute#40;Constants.ORDER_ID#41;;
if #40;orderId == null || orderId.equals#40;quot;quot;#41;#41; return new ModelAndView#40;quot;orderResultsquot;,quot;statusquot;,status#41;;
boolean executed = orderManager.isExecuted#40;orderId#41;;
int tryCount=0;
status = OrderStatus.PENDING;
while #40;!executed amp;amp; tryCountlt;maxNumberOfTrials#41; #123;
Session session = SessionFactoryUtils.getSession#40;sessionFactory, false#41;;
if #40;session!=null#41; session.clear#40;#41;;
status = orderManager.getOrderStatus#40;orderId#41;;
if #40;status == OrderStatus.PENDING#41; #123;
tryCount++;
try#123; Thread.sleep#40;period#41;;#125; catch #40;InterruptedException e#41; #123;#125;
#125; else #123;
executed=true; if #40;status == OrderStatus.AUTHORIZED#41; orderManager.executeOrder#40;orderId#41;;
else if #40;status == OrderStatus.REJECTED#41;orderManager.discardOrder#40;orderId#41;;
#125;
#125;
return new ModelAndView#40;quot;orderResultsquot;,quot;statusquot;,status#41;; #125;
I also moved order amp;account updating code to the orderManager, with transaction attribute set on PROPAGATION_REQUIRED for executeOrder and discardOrder methods.
?---gt;I would highly appreciate if somebody more experienced with spring could tell me if this handleRequestInternal method would now be able to correctly handle necessary updating in multi-threaded environment…
Thanks in advance,
Ana |
|