JavaFX浏览器项目地址栏输入网址时MySQL浏览历史重复插入问题
问题根本原因
你每次调用load()方法触发地址栏加载时,都会给WebEngine的LoadWorker状态属性新增一个监听器,之前注册的监听器不会被自动移除。这就导致第N次访问新页面时,会有N个监听器同时在加载成功后触发collectData()方法,对应插入N条相同的历史记录,和你给出的SQL日志规律完全匹配:第一次访问谷歌插1条、第二次访问CNN插2条、第三次访问IMDb插3条。
修复方案
1. 调整监听器注册逻辑(核心修复)
把监听器的注册代码从load()方法中移除,改为只在页面初始化阶段注册一次,比如放到FXML对应的Controller的initialize()方法中,全程仅保留一个生效的监听器:
// 放到Controller的initialize方法中,只执行一次 @Override public void initialize(URL url, ResourceBundle resourceBundle) { WebEngine webEngine = web.getEngine(); webEngine.getLoadWorker().stateProperty().addListener((obs, oldValue, newValue) -> { if (newValue == Worker.State.SUCCEEDED) { WebHistory history = web.getEngine().getHistory(); ObservableList<WebHistory.Entry> entries = history.getEntries(); addressBar.setText(entries.get(history.getCurrentIndex()).getUrl()); Stage stage = (Stage) addressBar.getScene().getWindow(); stage.setTitle(entries.get(history.getCurrentIndex()).getTitle()); collectData(); } else if (newValue == Worker.State.FAILED) { System.out.println("Loading failed"); } }); }
修正后的load()方法只保留加载逻辑即可:
@FXML private void load(ActionEvent event) { web.prefHeightProperty().bind(mainWindow.heightProperty()); web.prefWidthProperty().bind(mainWindow.widthProperty()); String targetUrl = addressBar.getText(); if(!targetUrl.contains("https://")) { targetUrl = "https://" + targetUrl; } web.getEngine().load(targetUrl); }
2. 额外优化建议
- 替换
Statement为PreparedStatement,避免你当前SQL拼接逻辑带来的SQL注入风险,同时也能处理URL里带特殊字符导致SQL执行报错的问题。 - 数据库层给
trackdata表的username、sitename、date三个字段添加联合唯一索引,即使业务逻辑出现异常也不会写入重复记录。 - 业务层可在插入前先判断最近的一条历史记录是否和当前要插入的记录完全一致,一致的话直接跳过插入,双重保障。
内容的提问来源于stack exchange,提问作者Jinyoung Chang
相关产品推荐
相关产品推荐

