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:
- 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? 
- 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)?
- 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());
 
     
     
    