JavaFX中Scene1插入数据库后Scene2自动弹窗通知问题求助
解决车辆预订系统实时通知问题
你的核心问题是getNotification()方法仅在user2登录时执行一次,无法持续监听数据库的新增预订请求,导致运行期间的新请求无法触发通知。以下是几种可行的解决思路和代码实现:
一、快速实现:定时轮询数据库(最易落地)
通过定时任务定期查询数据库,检测新增的待处理请求,同时避免重复通知。
1. 核心修改步骤
- 记录最后处理的预订ID,过滤已通知过的请求
- 使用定时任务框架定期执行查询
- 优化数据库连接和SQL安全性
2. 修改后的代码示例
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import javafx.util.Duration; import tray.notification.NotificationType; import tray.notification.TrayNotification; public class User2Controller { private String getDepartment; private long lastProcessedBookingId = 0; // 记录最后处理的预订ID(假设表中有自增的booking_id字段) private ScheduledExecutorService scheduler; private HikariDataSource dataSource; // 控制器初始化方法(JavaFX中自动调用) public void initialize() { // 初始化数据库连接池 initDataSource(); // 登录时先执行一次,处理历史待处理请求 try { getNotification(); } catch (SQLException e) { e.printStackTrace(); } // 启动定时任务:每隔5秒查询一次(可根据业务调整间隔) scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(() -> { try { getNotification(); } catch (SQLException e) { e.printStackTrace(); } }, 5, 5, TimeUnit.SECONDS); // 窗口关闭时清理资源 // 假设primaryStage是你的窗口对象,需根据实际场景获取 // primaryStage.setOnCloseRequest(event -> { // scheduler.shutdown(); // dataSource.close(); // }); } // 初始化HikariCP连接池(替代每次创建新连接) private void initDataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost/vbms_db"); config.setUsername("root"); config.setPassword(""); config.setDriverClassName("com.mysql.cj.jdbc.Driver"); // 替换为新版驱动 config.setMaximumPoolSize(5); // 连接池大小按需调整 dataSource = new HikariDataSource(config); } // 修改后的通知查询方法 public void getNotification() throws SQLException { // 用try-with-resources自动释放数据库资源 try (Connection conn = dataSource.getConnection()) { getDepartment = lbl_holdUserDepartment.getText(); // 仅查询未处理过的新请求,避免重复通知 String sql = "SELECT * FROM bookings WHERE driver_department = ? AND vehicle_AvailStatus = 'Pending...' AND booking_id > ? ORDER BY booking_id ASC"; try (PreparedStatement pst = conn.prepareStatement(sql)) { pst.setString(1, getDepartment); pst.setLong(2, lastProcessedBookingId); try (ResultSet rs = pst.executeQuery()) { while (rs.next()) { // 更新最后处理的ID,避免重复通知 lastProcessedBookingId = rs.getLong("booking_id"); // 触发托盘通知 NotificationType nt = NotificationType.WARNING; TrayNotification tray = new TrayNotification(); tray.setTitle("新预订通知"); tray.setMessage("有新的车辆预订请求,请及时处理!"); tray.setNotificationType(nt); tray.showAndDismiss(Duration.millis(3000)); } } } } } }
3. 关键优化点
- SQL注入防护:替换字符串拼接为
PreparedStatement参数绑定,避免安全风险 - 资源自动释放:使用
try-with-resources确保数据库连接、语句、结果集自动关闭,防止资源泄漏 - 连接池优化:用HikariCP连接池替代每次创建新连接,提升性能
- 避免重复通知:通过
lastProcessedBookingId过滤已处理的请求,同一请求不会重复弹窗
二、更高效方案:主动推送(适合客户端-服务器架构)
如果你的系统是客户端-服务器模式,可以在user1提交请求时,由服务器主动向对应部门的user2客户端推送通知(比如用WebSocket),比定时轮询更高效,实时性更强。
核心思路:
- user2客户端登录后与服务器建立WebSocket连接
- user1提交预订请求时,服务器根据
driver_department找到对应的在线user2客户端 - 服务器主动向目标客户端发送通知消息,客户端收到后触发TrayNotification
三、注意事项
- 定时轮询的间隔需根据业务平衡:间隔太短会增加数据库压力,太长会降低实时性
- 需确保
bookings表有唯一自增的ID字段(比如booking_id),用于跟踪已处理的请求 - 新版MySQL驱动需替换为
com.mysql.cj.jdbc.Driver,旧驱动已废弃
内容的提问来源于stack exchange,提问作者T_Miles
相关产品推荐
相关产品推荐

