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

Spring Web应用数据库新增条目时页面自动刷新的实现问询

完善Spring Web应用前端自动刷新数据库新增数据的方案

嘿,这个需求我之前做过类似的,结合你现有的代码,给你几个从简单到进阶的完善方案,按需选择就行:

方案一:基于现有代码的定时轮询(最快上手)

你已经写了contentRefresh方法,只需要加上定时调用逻辑,同时优化后端的查询逻辑,确保每次返回最新的数据。

前端代码改造

在页面加载完成后启动定时任务,和数据库新增频率匹配(比如每10秒一次),同时加个时间戳参数减少冗余数据传输:

$(document).ready(function() {
    // 记录上次更新的时间戳,后端只返回该时间之后的新增数据
    let lastUpdateTime = new Date().getTime();
    
    function contentRefresh() {
        $.ajax({
            url: "/refresh",
            data: { lastUpdateTime: lastUpdateTime },
            success: function(data) {
                if(data.trim()) {
                    // 推荐:把新增数据追加到页面顶部,而非替换整个内容,避免页面闪烁
                    $("#main-content").prepend(data);
                    lastUpdateTime = new Date().getTime();
                }
                console.log("刷新检查完成");
            },
            error: function() {
                console.error("刷新请求失败,稍后重试");
            }
        });
    }

    // 页面加载时先刷新一次,之后每10秒自动刷新
    contentRefresh();
    setInterval(contentRefresh, 10000);
});

后端代码优化

修改refreshPage方法,根据前端传递的时间戳过滤数据,只返回新增内容:

@RequestMapping("/refresh")
public String refreshPage(Model model, 
                          @RequestParam(required = false) Long lastUpdateTime, 
                          Pageable pageable) {
    // 转换时间戳为本地时间,默认取最近10秒的数据
    LocalDateTime lastUpdate = lastUpdateTime != null ? 
        LocalDateTime.ofInstant(Instant.ofEpochMilli(lastUpdateTime), ZoneId.systemDefault()) :
        LocalDateTime.now().minusSeconds(10);

    // 按创建时间倒序查询新增数据
    List<YourEntity> newData = yourRepository.findByCreateTimeAfter(lastUpdate, pageable);
    
    model.addAttribute("newData", newData);
    // 返回仅包含新增数据的HTML片段视图,而非整个页面
    return "fragments/new-data-fragment";
}

方案二:SSE(Server-Sent Events)服务器主动推送(更高效)

定时轮询会产生不必要的空请求,SSE是单向服务器推送技术,适合这种“数据库有新增就主动通知前端”的场景,性能更优。

前端实现

使用原生EventSource接收服务器推送:

$(document).ready(function() {
    // 建立SSE连接
    const eventSource = new EventSource("/sse/refresh");
    
    // 接收服务器推送的新增数据HTML片段
    eventSource.onmessage = function(event) {
        $("#main-content").prepend(event.data);
        console.log("收到新数据,已更新页面");
    };
    
    // 处理连接异常,自动重连
    eventSource.onerror = function(error) {
        console.error("SSE连接出错,3秒后尝试重连...");
        eventSource.close();
        setTimeout(() => window.location.reload(), 3000);
    };
});

后端Spring实现

通过JPA生命周期事件监听数据库新增,再用SSE推送给前端:

// 1. 监听实体新增事件
@Component
public class EntityCreatedListener {
    private final TemplateEngine templateEngine;
    private final SseEmitterRegistry sseEmitterRegistry; // 自定义的SSE连接注册表

    public EntityCreatedListener(TemplateEngine templateEngine, SseEmitterRegistry sseEmitterRegistry) {
        this.templateEngine = templateEngine;
        this.sseEmitterRegistry = sseEmitterRegistry;
    }

    @PostPersist
    public void onEntityCreated(YourEntity entity) {
        // 渲染新增数据的HTML片段
        Context context = new Context();
        context.setVariable("data", entity);
        String htmlFragment = templateEngine.process("fragments/new-data-fragment", context);
        
        // 推送给所有在线客户端
        sseEmitterRegistry.broadcast(htmlFragment);
    }
}

// 2. SSE控制器和连接注册表
@RestController
@RequestMapping("/sse")
public class SseController {
    private final SseEmitterRegistry sseEmitterRegistry = new SseEmitterRegistry();

    @GetMapping(value = "/refresh", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamNewData() {
        SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
        sseEmitterRegistry.addEmitter(emitter);
        
        // 连接关闭时移除注册表中的emitter
        emitter.onCompletion(() -> sseEmitterRegistry.removeEmitter(emitter));
        emitter.onTimeout(() -> sseEmitterRegistry.removeEmitter(emitter));
        
        return emitter;
    }

    // 自定义SSE连接注册表
    public static class SseEmitterRegistry {
        private final List<SseEmitter> emitters = Collections.synchronizedList(new ArrayList<>());

        public void addEmitter(SseEmitter emitter) {
            emitters.add(emitter);
        }

        public void removeEmitter(SseEmitter emitter) {
            emitters.remove(emitter);
        }

        public void broadcast(String message) {
            Iterator<SseEmitter> iterator = emitters.iterator();
            while (iterator.hasNext()) {
                SseEmitter emitter = iterator.next();
                try {
                    emitter.send(SseEmitter.event().data(message));
                } catch (IOException e) {
                    iterator.remove();
                }
            }
        }
    }
}

方案三:WebSocket(双向通信,适合复杂场景)

如果以后需要前端和后端双向交互(比如用户手动触发刷新、提交数据反馈等),可以用WebSocket。Spring对WebSocket支持完善,功能比SSE更灵活,但如果只是单向的新增数据推送,SSE足够轻量。

关键注意事项

  • 用户体验:尽量追加数据而非替换整个内容,可添加“新数据提示”动画提升感知;
  • 性能优化:定时轮询频率要匹配数据库新增频率;SSE/WebSocket要做好连接管理,避免内存泄漏;
  • 分布式场景:如果是多实例部署,需要用消息队列(如RabbitMQ)统一转发新增事件,确保所有后端实例都能推送更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:00:06