Java程序如何监听SQLite3数据库表的增删操作?
解决SQLite3表变更通知Java程序的方案
SQLite本身没法让触发器直接调用外部Java代码,因为它的触发器只能执行数据库内部的SQL逻辑。给你两个实用的方案解决这个问题:
方案一:触发器+轮询中间通知表
这个思路是用触发器把表的增删操作记录到一个专门的通知表,然后Java程序定时轮询这个表获取变更通知。
步骤1:创建通知表和触发器
首先在SQLite里创建一个用于记录变更的表:
CREATE TABLE IF NOT EXISTS table_changes ( id INTEGER PRIMARY KEY AUTOINCREMENT, table_name TEXT NOT NULL, operation_type TEXT NOT NULL CHECK(operation_type IN ('INSERT', 'DELETE')), record_id INTEGER, change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_processed INTEGER DEFAULT 0 );
然后给目标表(比如叫target_table)创建INSERT和DELETE触发器:
-- INSERT触发器 CREATE TRIGGER IF NOT EXISTS trigger_target_insert AFTER INSERT ON target_table FOR EACH ROW INSERT INTO table_changes (table_name, operation_type, record_id) VALUES ('target_table', 'INSERT', NEW.id); -- DELETE触发器 CREATE TRIGGER IF NOT EXISTS trigger_target_delete AFTER DELETE ON target_table FOR EACH ROW INSERT INTO table_changes (table_name, operation_type, record_id) VALUES ('target_table', 'DELETE', OLD.id);
步骤2:Java程序轮询处理
用Java的定时任务框架(比如ScheduledExecutorService)定期查询table_changes表,处理未被标记的变更记录:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class SQLiteChangeNotifier { private static final String DB_URL = "jdbc:sqlite:/path/to/your/database.db"; public static void main(String[] args) { ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // 每5秒轮询一次,可根据需求调整间隔 scheduler.scheduleAtFixedRate(SQLiteChangeNotifier::checkChanges, 0, 5, TimeUnit.SECONDS); } private static void checkChanges() { try (Connection conn = DriverManager.getConnection(DB_URL)) { // 查询未处理的变更 String querySql = "SELECT id, table_name, operation_type, record_id FROM table_changes WHERE is_processed = 0"; try (PreparedStatement stmt = conn.prepareStatement(querySql); ResultSet rs = stmt.executeQuery()) { while (rs.next()) { int changeId = rs.getInt("id"); String tableName = rs.getString("table_name"); String opType = rs.getString("operation_type"); int recordId = rs.getInt("record_id"); // 这里写你的业务处理逻辑,比如打印通知、触发其他操作 System.out.printf("检测到变更:表%s,操作%s,记录ID%d%n", tableName, opType, recordId); // 标记为已处理 String updateSql = "UPDATE table_changes SET is_processed = 1 WHERE id = ?"; try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) { updateStmt.setInt(1, changeId); updateStmt.executeUpdate(); } } } } catch (Exception e) { e.printStackTrace(); } } }
方案二:WAL日志监听(进阶)
如果轮询的延迟不符合需求,可以开启SQLite的WAL(Write-Ahead Logging)模式,然后监听WAL文件的变化,解析日志内容来实时获取变更。
步骤1:开启WAL模式
连接数据库时执行:
PRAGMA journal_mode=WAL;
步骤2:Java监听WAL文件
你可以用Java的WatchService监听WAL文件(通常是your_db.db-wal)的修改事件,当文件变化时,解析WAL日志里的操作记录。不过WAL格式比较复杂,你可以借助SQLite的sqlite3命令行工具导出日志内容,或者自行实现解析逻辑:
// 示例:用WatchService监听WAL文件变化 import java.nio.file.*; public class WALWatcher { public static void main(String[] args) throws Exception { Path walPath = Paths.get("/path/to/your/database.db-wal"); Path dir = walPath.getParent(); WatchService watcher = FileSystems.getDefault().newWatchService(); dir.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY); while (true) { WatchKey key = watcher.take(); for (WatchEvent<?> event : key.pollEvents()) { if (event.context().equals(walPath.getFileName())) { // WAL文件已修改,这里可以调用解析逻辑获取变更 System.out.println("WAL文件更新,检测到可能的表变更"); // 解析逻辑需要自行实现或借助工具 } } key.reset(); } } }
这个方案的实时性更好,但实现复杂度高,适合对延迟要求高的场景。
注意事项
- 方案一的轮询间隔要根据业务需求调整,间隔太小会增加数据库负载,太大则延迟高。
- 如果数据库有多个程序操作,要确保触发器和通知表的权限设置正确,避免写入失败。
- 方案二的WAL解析需要处理SQLite的日志格式,可参考SQLite官方文档的WAL格式说明。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

