描述:我在 linux 上有一个没有任何框架的 java web 应用程序(jsp + tomcat)。我使用 postgresql 作为 DBMS。
我有一个表(options)和一个封装它的 Singleton 类(Options)。Options 实例在应用程序启动时加载并无限期地保留在那里。
如果用户修改选项,则方法(.refreshData())会更新保存在内存中的实例。
现在的麻烦是:有一个远程服务可以直接访问数据库并更新选项表中的一些字段。我无法控制这段代码。
我想在外部服务更新选项表时触发刷新方法。我也知道该服务每天下午 3 点开始一次,但我不知道它什么时候结束。
postgresql 提供的LISTEN - NOTIFY功能(用于更新 Java 缓存的 Postgres 触发器)在我看来是实现这一目标的最优雅的方式。按照这个主题,我正在尝试一个简单的监听器并“适应”我的需要(Postgress 文档中的代码示例)。
在@Craig 建议后编辑:
public class OptionsListener extends Thread {
private int threadMills = 1000;
private Connection conn;
private org.postgresql.PGConnection pgconn;
private Options optionsInstance;
private static final String DB_URL;
private static final String DB_USERNAME;
private static final String DB_PASSWORD;
static {
try {
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
DB_URL = (String) envContext.lookup("application/DB/url");
DB_USERNAME = (String) envContext.lookup("application/DB/username");
DB_PASSWORD = (String) envContext.lookup("application/DB/password");
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
OptionsListener(Options instance, int threadMillis) {
optionsInstance = instance;
this.threadMills = threadMillis;
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
pgconn = (PGConnection) DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
Statement stmt = conn.createStatement();
stmt.execute("LISTEN otionsUpdate");
stmt.close();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public void run() {
while (true) {
Log.addItem("Polling ?");
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1");
rs.close();
stmt.close();
PGNotification notifications[] = pgconn.getNotifications();
if (notifications != null) {
Log.addItem("NOTIFY received");
optionsInstance.loadDbData();
}
Thread.sleep(threadMills); //tempo di attesa in millisecondi
} catch (Exception e) {
Log.addItem(getClass().getName() + " " + e.getMessage());
}
}
}
}
在选项类中,我使用此方法手动启动侦听器:
public static void startExternalChangesListener(Options instance, int millis) {
OptionsListener listener = new OptionsListener(instance, millis);
listener.start();
}
最后
Options.startExternalChangesListener(options, 5000);
这是我第一次篡改线程...
我创建了一个通知频道的AFTER UPDATE 触发器,并通过 PGAdmin3 对其进行了测试。它就像一个魅力,但java似乎没有注意到......