解决方案 »

  1.   


    就是公司的金钱流水表
    会员的金钱流动会在金钱流水添加一条记录 
    如: 
    会员存款100元记录中的 money字段就为 100
    会员购买商品消耗10元
      

  2.   


    上面那个回复手抖了 还没写完就发出去了  不知道为啥还不能编辑的就是公司的金钱流水表
    会员的金钱流动会在金钱流水添加一条记录 
    如: 
    会员存款100元                      记录中的 money字段就为 100
    会员购买商品消耗10元        记录中的 money字段就为 -10判断会员余额时就 sum(money)   获得 90    90就是会员的余额我在 insert  的时候判断了下 如果这次插入的数据是负数 就判断 会员的余额必须比本次插入的 负数的绝对值 大(就是余额要大于本次消费的金额)
    但出现了 余额小于 商品金额的情况下 成功购买了商品的情况简单讲就是  一个 10元的商品 会员只有 11元的余额 会员购买该商品时 不知道怎么回事 并发了2个请求 结果两个请求判断 会员的金额都是大于10 然后 会员 购买了2个 10元的商品 余额变为 -9
      

  3.   

    不知道是不是由于2个SQL并发
    且在事务中并未提交 延迟提交后产生的问题?事物管理器用的 org.springframework.orm.hibernate3.HibernateTransactionManager如果是 有什么解决办法不?(纯SQL修改的方式)
    有没有高人来解答下疑惑....
      

  4.   

     这个并发问题,我感觉啊 , 你在方法名加上synchronized,防止出现负数问题
      

  5.   


    上面有说明的 后来由于是生产环境就先把方法加了一个锁先解决问题
    主要是没搞明白为啥insert where没有效果加 synchronized 会非常影响性能 锁粒度太大了 我用了个 ConcurrentHashMap 作为锁对单个会员进行锁操作boolean flag = false;
    //用会员ID进行加锁
    String moneyLockkey = geMoneyDetails.getAccountId().toString();
    synchronized (LockUtil.MoneyLock){
    flag = LockUtil.MoneyLock.get(moneyLockkey,true);
    if(flag){
    LockUtil.MoneyLock.put(moneyLockkey,false);
    }
    }
    if(flag){
    try{
    //是扣除金额
    if(geMoneyDetails.getMoney().compareTo(BigDecimal.ZERO)==-1 &&
    //因为回滚操作会导致负数 回避掉回滚代码
    !geMoneyDetails.getOperateType().equals(OperateType.CancelBalance.getType()) &&
    !geMoneyDetails.getOperateType().equals(OperateType.ReconstructionBet.getType()))
    {
    //InsertMoneyDetailsByAccumulate 就是 insert where 的那段代码
    List<Long> resultCount = this.getMybatis().getListByObject("InsertMoneyDetailsByAccumulate", geMoneyDetails);
    if(resultCount == null || resultCount.isEmpty())
    throw new DaoException("余额不足!");
    }
    else
    this.getHibernate().save(geMoneyDetails);
    }finally{
    //释放锁信息
    LockUtil.MoneyLock.put(moneyLockkey,true);
    }
    }else{
    throw new DaoException("您当前有金钱操作未完成请稍后尝试!");
    }
      

  6.   


    上面有说明的 后来由于是生产环境就先把方法加了一个锁先解决问题
    主要是没搞明白为啥insert where没有效果加 synchronized 会非常影响性能 锁粒度太大了 我用了个 ConcurrentHashMap 作为锁对单个会员进行锁操作boolean flag = false;
    //用会员ID进行加锁
    String moneyLockkey = geMoneyDetails.getAccountId().toString();
    synchronized (LockUtil.MoneyLock){
    flag = LockUtil.MoneyLock.get(moneyLockkey,true);
    if(flag){
    LockUtil.MoneyLock.put(moneyLockkey,false);
    }
    }
    if(flag){
    try{
    //是扣除金额
    if(geMoneyDetails.getMoney().compareTo(BigDecimal.ZERO)==-1 &&
    //因为回滚操作会导致负数 回避掉回滚代码
    !geMoneyDetails.getOperateType().equals(OperateType.CancelBalance.getType()) &&
    !geMoneyDetails.getOperateType().equals(OperateType.ReconstructionBet.getType()))
    {
    //InsertMoneyDetailsByAccumulate 就是 insert where 的那段代码
    List<Long> resultCount = this.getMybatis().getListByObject("InsertMoneyDetailsByAccumulate", geMoneyDetails);
    if(resultCount == null || resultCount.isEmpty())
    throw new DaoException("余额不足!");
    }
    else
    this.getHibernate().save(geMoneyDetails);
    }finally{
    //释放锁信息
    LockUtil.MoneyLock.put(moneyLockkey,true);
    }
    }else{
    throw new DaoException("您当前有金钱操作未完成请稍后尝试!");
    }

    比如你余额50,在19行, 你执行一次取款50, 这条数据插入了,可是数据库没有提交, 你下次执行余额还是显示50  你又插入了,我觉得你的insert where 没问题
      

  7.   

    synchronized可以锁具体对象,比如synchronized(geMoneyDetails.getMoney()),这样不会太影响性能
      

  8.   

    1、有负数是正常的,电信运营商都能欠费,太严格的校验会增加负担
    2、应该实时计算当前最最新余额,而不是每次都sum
      

  9.   

    建议加一个UserBalance表来记录用户的余额
    消费时按UserId判断余额 读取数据是加 排他锁 select ××× for update with RS