java事务编程陷阱

1:数据库客户端操作未提交导致行锁定


    在用PL\SQL进行系统运维过程中,有时候经常需要直接数据库中表的内容,进行一些查询、插入和更新等操作。
    在进行更新时,很多时候我们喜欢使用for update,对行进行锁定后,修改相关值,然后commit。
    在操作过程中,有时候会忘记commit或者commit滞后时间比较长,此时会给系统带来一些潜在的风险。
    ORACLE在对行进行for update操作时,在行上加入独占锁,这是其它会话(包含应用端)恰好也更新此行,会导致该会话堵塞等待。
    尤其是对多行数据进行for update锁定,长时间不提交或者忘记提交,会导致应用端更新出现不可预料的问题。
    切记在业务空闲时,进行这些操作,且及时commit。
2:批量更新导致的死锁
    在编写程序时,尤其是高并发情况下,不正确的批量更新将导致数据库的死锁。
    public void deductStock(int[] productIds) throws Exception {
            if(productIds != null && productIds.length > 0 ) {
            for(int pId : productIds) {
                // product stock deduct 1(default)
                updateStock(pId);
            }
            }
        }
    如果按照购物车的加入顺序传递商品ID列表,可能会导致A购物车[1,2,3],B购物车[2,1,3],那么在提交扣减库存时,可能会发生
    A操作会话锁定1行时,B操作会话锁定2行时,此时A会话要获取1行的独占锁,B会话要获取2行的独占锁,相互等待,导致死锁。
    此情况一般不会出现,如果有多个热点商品(商品做活动,优惠力度比较大),而代码恰好有此bug,就有很大的几率引发死锁。

3:不正确的程序调用导致事务问题


      目前spring框架一般都是配置事务管理,而EJB容器具有天然事务性的特点,在编写程序时,好多程序员不关注事务切入点在何处,不清楚哪里是事务的开始点和结束点。
     目前spring框架,通过AOP配置,一般我们调用服务层业务方法时,就是事务的开始点,业务方法结束就是事务的完结点。
    本来应该在一个事务的操作,由于对事务的不了解,导致写出事务不一致的代码,例如:
    public void action()  {
            try {
            //更新书籍类目
            bookCategorySevice.update(bookCategory);
            //更新图书信息
            bookSevice.update(book);
            }catch(Exception e) {
            logger.error(" operator is error!!!",e);
            }
        }
     从业务上,更新书籍类目和更新图书信息应该在一个操作完成,单从控制层代码看也没有问题,但是如果考虑到事务的切入点,这就是2个事务,会出现不一致问题。
    
    同来,有时候就是应该是两个事务操作,如果写在一个事务也是操作,例如审计记录,更新图书信息动作不管成功与否都应该有一条审计记录。代码写成:
    public void action()  {
            try {
 
            //更新图书信息
            bookSevice.update(book);
            }catch(Exception e) {
            logger.error(" operator is error!!!",e);
            }
        }
    审计记录动作在update业务操作中,这样就不能满足业务要求,会导致审计记录的缺失。
    
    

4:事务回滚陷阱
    在业务方法中做一些非事务的操作时,例如文件操作,如果和事务没有关联,建议写入单独的业务方法,在控制层单独调用。
    如果不方便写入单独方法,可以把文件操作的异常进行单独捕获处理,不让异常影响事务操作。
    一般事务的回滚都是以捕获异常为回滚点,进行事务回滚,如果是和事务操作无关的异常抛出,导致异常回滚,就会业务的正确性。
    public void update() throws Exception {
            //更新图书信息
            bookDao.update(book);
        //写入文件内容,此操作和事务无关,但是抛出异常,事务照样回滚
        fileUtil.write(info);
        }
    当然也可以指定特殊的异常对象类似为回滚,最后还是对此操作进行单独的异常处理,不抛出异常。
    
5:异步操作事务陷阱
    如果我们在业务方法操作中,创建异步线程进行一些操作,又传递服务层对象到异步线程中,在异步线程的操作中调用传递的服务层对象方法,
    此时会出现事务异常,因为事务实例是线程共享变量,而新创建的异步线程中,还没有此共享变量,所以报错。需要在异步线程重新进行事务的切入点调用,才能正常使

用,暂不写代码实例了。
6:事务并发提交陷阱
    应用端有时候会出现同时更新同一条记录,两条都更新成功,单结果只能是其中一条的更新结果,一般我们会引入版本号来解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值