I have a servlet that does some work for user and then decrement user's credit. When I watch user's credit in the database in real time, if there are many concurrent requests from the same user, the credit has been deducted incorrectly due to concurrency control. T
Assume I have one server and database management used is hibernate.
I am using transaction control to span the whole request, please see code for detail. I have several questions:
1. why are the credit counter in db jumping all over the place when facing many concurrent request from same user? why isn't my transaction control working?
2. if underlying data was modified after I retrieved user account and then attempt to update it, why didn't I get any HibernateException(eg.StaleObjectException)?
3. I have transaction span across the full user request, is there a better way? please critique. Feel free to rewrite the sample code structure if you feel I'm doing the whole thing wrong.
Main servlet class: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try{ Manager.beginTransaction(); cmdDowork(request, response); Manager.commitTransaction(); }catch(Exception exp){ Manager.rollbackTransaction(); exp.printStackTrace(); } finally{ Manager.closeSession(); } } public void cmdDowork(){ try{ UserAccount userAccount = lazyGetUserAccount(request.getParameter("userName")); doWorkForUser(userAccount);//time and resource consuming process if(userAccount!=null) { decUserAccountQuota(userAccount); } }catch (HibernateException e){ e.printStackTrace(); } } public static UserAccount lazyGetUserAccount(String userName) { UserAccount userAccount = Manager.getUserAccount(userName); if(userAccount == null){ userAccount = new UserAccount(userName); userAccount.setReserve(DEFAULT_USER_QUOTA); userAccount.setBalance(DEFAULT_USER_QUOTA); Manager.saveUserAccount(userAccount); } return userAccount; } private boolean decUserAccountQuota(UserAccount userAccount) { if(userAccount.getBalance()Edit: code I used to test optimistic locking as suggested by the answer, I am not getting a any StaleObjectException, the update were committed successfully..
Session em1=Manager.sessionFactory.openSession(); Session em2=Manager.sessionFactory.openSession();em1.getTransaction().begin(); em2.getTransaction().begin(); UserAccount c1 = (UserAccount)em1.get( UserAccount.class, "jonathan" ); UserAccount c2 = (UserAccount)em2.get( UserAccount.class, "jonathan" ); c1.setBalance( c1.getBalance() -1 ); em1.flush(); em1.getTransaction().commit(); System.out.println("balance1 is "+c2.getBalance()); c2.setBalance( c2.getBalance() -1 ); em2.flush(); // fail em2.getTransaction().commit(); System.out.println("balance2 is "+c2.getBalance());