You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vaadin进度条在数据库删除操作中无法动态更新的问题排查

问题排查:删除操作进度条未按预期更新

ProfilesDeletionPage类负责实现删除操作页面,流程为:上传包含待删除数据的文件→数据存入数据库临时表→异步执行deleteMsisdns方法删除数据→getDeletedCount方法统计删除进度并通过processingUpdated更新ProgressBar。日志显示进度值已正常传递,但进度条无变化,核心问题及修复方案如下:


核心问题1:异步线程中UI.getCurrent()返回null,UI更新代码未执行

在processingUpdated和processingSucceeded方法中,调用UI.getCurrent()获取当前UI实例,但异步线程(由asyncDeletionProcessExecutor执行)未绑定任何UI实例,导致UI.getCurrent()返回null,后续的access()方法不会触发,进度条的setValue操作完全没执行。

修复方案:
在页面初始化时保存当前UI实例,避免在异步线程中动态获取:

// 在ProfilesDeletionPage类中添加成员变量
private UI currentUI;

// 构造方法中初始化保存
public ProfilesDeletionPage() {
    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    currentUI = UI.getCurrent(); // 保存当前页面的UI实例
    // ... 原有其他代码
}

// 修改进度更新和完成方法
public void processingUpdated(Float progress) {
    log.info("processingUpdated");
    if (currentUI != null && !currentUI.isClosed()) {
        currentUI.access(() -> progressBar.setValue(progress));
    }
}

public void processingSucceeded() {
    log.info("processingSucceeded");
    if (currentUI != null && !currentUI.isClosed()) {
        currentUI.access(() -> {
            progressBar.setVisible(false);
            Notification.show("Done!");
        });
    }
}

核心问题2:AsyncDeletionProcessService中deletedCount为全局成员变量,线程不安全

deletedCount是类级别的全局变量,每次调用getDeletedCount时不会重置,若同时有多个删除操作,会导致进度计算混乱;即使单个操作,用户重复点击提交按钮也会出现数据污染。

修复方案:
将deletedCount改为getDeletedCount方法内的局部变量,避免跨线程共享:

public void getDeletedCount(Long totalCountToDelete, Consumer<Float> progressListener, Runnable succeededListener) {
    Runnable r = () -> {
        Long deletedCount = 0L; // 改为方法内局部变量
        Long remainingCount;
        while(deletedCount < totalCountToDelete) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 正确处理线程中断
                return;
            }
            remainingCount = dao.getRemainingMsisdnCount();
            // 防止数据库查询返回null导致死循环
            if (remainingCount == null) break;
            
            deletedCount = totalCountToDelete - remainingCount;
            float progress = (float)deletedCount/totalCountToDelete;
            log.info("progress {}", progress);
            progressListener.accept(progress);
            log.info("remainingCount {}, deletedCount {}", remainingCount, deletedCount);
        }
        succeededListener.run();
    };
    asyncDeletionProcessExecutor.execute(r);
}

问题3:两个异步任务无同步,进度计算可能提前结束或无效

deleteMsisdns和getDeletedCount是独立的异步任务,getDeletedCount可能在deleteMsisdns开始执行前就进入循环,此时remainingCount等于totalCountToDelete,deletedCount为0,循环会持续等待;若deleteMsisdns执行速度极快,getDeletedCount可能还没来得及多次查询就直接跳到100%,导致进度条无中间过渡过程。

优化方案:
在删除任务启动后延迟启动进度查询,确保删除操作已开始执行:

submitButton.addClickListener((Button.ClickListener) event -> {
    final AsyncDeletionProcessService deletionService = app.getBean(AsyncDeletionProcessService.class);
    progressBar.setCaption("Processing...");
    progressBar.setVisible(true);
    progressBar.setIndeterminate(false);
    // 启动删除任务
    deletionService.deleteMsisdns(sequenceId, user.getId(), user.getLogin());
    // 延迟500ms启动进度查询,确保删除操作已初始化
    asyncDeletionProcessExecutor.execute(() -> {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        deletionService.getDeletedCount(totalCountToDelete, ProfilesDeletionPage.this::processingUpdated, ProfilesDeletionPage.this::processingSucceeded);
    });
});

附加优化:异常处理不规范问题

  • Thread.sleep的中断异常仅打印栈轨迹,未正确终止线程;
  • 文件操作异常仅打印栈轨迹,未给用户反馈。

优化建议:
在UploadListener的异常处理块添加用户提示:

catch(IOException e) {
    log.error("Failed to process uploaded file", e);
    Notification.show("文件处理失败:" + e.getMessage(), Notification.Type.ERROR_MESSAGE);
}

内容的提问来源于stack exchange,提问作者gonzay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 03:55:04