我有个action方法是取消订单用的在取消之前先判断订单状态是不是已取消不是的话就改成已取消并且把订单的钱加到账户但是测试的时候 连续快速的点两次取消钱加了两次 有可能是什么原因引起的是两次请求同时执行了?
action
先查询订单状态 判断是否已取消
然后
boolean res = this.ordersManager.cancelOrder(uporders);
dao层public boolean cancelOrder(Orders orders) throws DataAccessException {
Session session =null;
Transaction tx = null;
Balance bal = null;
try{
session = hibernateTemplate.getSessionFactory().openSession();
tx = session.beginTransaction();
ClientLoginInfo clientLoginInfo = orders.getClientLoginInfo();
String manualName=orders.getClientLoginInfo().getClientUserName();
double money=orders.getSumMoney();
Company c = orders.getClientLoginInfo().getCompany();
Integer companyId = c.getCompanyId();
List list = this.hibernateTemplate.find(
"from Balance as g where g.company.companyId=?", companyId);
if (list.isEmpty()) {
bal = new Balance();
bal.setCompany(c);
bal.setBalanceMoney(money);
session.save(bal);
} else {
bal= (Balance) list.get(0);
bal.setBalanceMoney(bal.getBalanceMoney()
+ money);
session.merge(bal);
}
session.merge(orders);
tx.commit();
return true;
}catch (DataAccessException e) {
tx.rollback();
e.printStackTrace();
throw e;
}finally{
if(session!=null)
session.close();
}
}
action
先查询订单状态 判断是否已取消
然后
boolean res = this.ordersManager.cancelOrder(uporders);
dao层public boolean cancelOrder(Orders orders) throws DataAccessException {
Session session =null;
Transaction tx = null;
Balance bal = null;
try{
session = hibernateTemplate.getSessionFactory().openSession();
tx = session.beginTransaction();
ClientLoginInfo clientLoginInfo = orders.getClientLoginInfo();
String manualName=orders.getClientLoginInfo().getClientUserName();
double money=orders.getSumMoney();
Company c = orders.getClientLoginInfo().getCompany();
Integer companyId = c.getCompanyId();
List list = this.hibernateTemplate.find(
"from Balance as g where g.company.companyId=?", companyId);
if (list.isEmpty()) {
bal = new Balance();
bal.setCompany(c);
bal.setBalanceMoney(money);
session.save(bal);
} else {
bal= (Balance) list.get(0);
bal.setBalanceMoney(bal.getBalanceMoney()
+ money);
session.merge(bal);
}
session.merge(orders);
tx.commit();
return true;
}catch (DataAccessException e) {
tx.rollback();
e.printStackTrace();
throw e;
}finally{
if(session!=null)
session.close();
}
}
sqlserver的
这么弄:
cancelOrder(Orders orders){
synchronized(orders){
********
}
}
如果进入了,那就是重复提交了.重复提交就需要用到令牌来验证.来避免重复提交.
这么弄:
cancelOrder(Orders orders){
synchronized(orders){
********
}
}
这里面的orders是个参数,并不是全局的变量,不存在多个线程去同时操作的情况.每次操作传进来的orders必然是一个新的orders.除非是两次操作的orders是同样的值,那这就是表单重复提交的本质了.
当然不喜欢这种方式可以在底层限制,如果不是多线程导致的,同步就没法阻止,这些都要去确认的
关键先找到原因吧
重现也没有什么问题,用账户A登录,人为在其查询出状态后设置断点不操作,用账户B登录进行同样操作但这次不断点,肯定会执行两次了,简单一点的做法借助于数据库的锁机制,hibernate使用悲观锁在查询是就锁定这条记录,其他的查询会等待,直到加钱的逻辑commit,query.setLockMode("user",LockMode.UPGRADE); // 加锁
如果不加锁就会出现这样的情况,我说的数据锁是其中的解决方法之一,完全可以把查询和返还放在一个方法用synchronized 关键字达到同样的效果
我估计你liupengxia经常用的是oracle数据库,oracle数据库数据就我了解的是一般不会出现读锁定,但其他数据库就是读锁定,必须得等update执行提交完了才可以执行select
上面我作的假想,应该就是楼主提的问题产生的原因了。 在这种情况下,如果在查询的时候加锁。这不可能实现的。
用了你的方法 发现确实是可以同时进入两个的
第一次还没改的时候 第二个已经进来了
goldenfish1919 说的 加锁很好用 不过用错了地方 应该加在action中
加在dao的话 第二次会卡在dao开始的部分 等第一次完了 他还会执行
我的判断在action里
update orders set money=.....,status =1 where status<>1.这下前面一个提交操作把状态改了,第二个提交操作就不能再改变状态了。
Service层对事务的控制,只是保证数据的一致性,如果操作失败了还能回滚。这跟此问题没有必然联系。
synchronized (Test.class) {
//you code here
}
}
那要看你的action是多例还是单例。如果是单例,加锁程序就不能用了。只能单线程来用。
如果是多例,那加锁与不加锁是一样的。因为你加的锁是对象锁,锁住的是单前的线程对象,但是另一个提交的请求还是会进来。
错了,首先要在表单提交前保存一个Token,所以在请求一到来的事件里要有 saveToken(request,true)语句; 这时你可以在表单提交的页面的源码中看到一个隐藏域, 如:〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉提交上来后,到了insert事件里面了,这里要判断请求中的Token 和保存在会话中的Token是否一样,如果一样,则表明是第一次提交.如果不一样,则表明是"重复提交".
我猜楼主的解决方法就是,最不完善的方法了,我造孽了....阿弥陀佛
spring 设置成 prototype
action 执行方法为 public String order(){
synchronized (OrderAction.class) {
// update order
}
}
这种做法很容易就会有性能瓶颈。而且还没有从根本上解决问题。