我尝试创建客户端 - >数据库通知/更新系统。客户端是用Java / Hibernate编写的。要通知客户端我使用触发器:
CREATE OR REPLACE FUNCTION notifyUsers() RETURNS TRIGGER AS $$
DECLARE
data integer;
notification json;
BEGIN
IF (TG_OP = 'DELETE') THEN
data = OLD.id;
ELSE
data = NEW.id;
END IF;
-- Contruct the notification
notification = json_build_object(
'table',TG_TABLE_NAME,
'action', TG_OP,
'id', data);
-- Execute pg_notify(channel, notification)
PERFORM pg_notify('events',notification::text);
-- Result is ignored since this is an AFTER trigger
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
CREATE TRIGGER notifyUsersAccountData AFTER INSERT OR UPDATE OR DELETE ON document FOR EACH ROW EXECUTE PROCEDURE notifyUsers();
但是当客户收到通知时,我无法确定是谁触发了它。 我可以发送任何其他参数来确定是谁触发它(session_id?在我的应用程序中有自定义用户,但我想避免发送User.id,因为2台PC登录同一用户会破坏我的通知/更新系统)< / p>
通知接收后,客户端更新值,&#34;会话ID&#34;需要确定是否&#34; THIS客户&#34;做了改变(当然在这种情况下客户端不应该更新)或其他人(在这种情况下应该应用更新)
编辑 - - -
我想刷新这个主题,因为它没有解决。调用pg_backend_pid的解决方案有时会导致&#34;执行SQL查询的调用者的PID&#34;与PGNotification.getPid不同。代码:
public int getSessionPid() {
int lResult = -1;
try
{
connect();
Session session = _sessionFactory.getCurrentSession();
Transaction lTransaction = session.beginTransaction();
SQLQuery lQuery = session.createSQLQuery("SELECT pg_backend_pid()");
List lResultQuery = lQuery.list();
lResult = Integer.parseInt(lResultQuery.get(0).toString());
lTransaction.commit();
}catch(org.hibernate.SessionException e)
{
e.printStackTrace();
}
return lResult;
}
和出现比较的代码:
int lPid = Facade.databaseConnector.warehouse.getSessionPid();
for(int i = 0; i < notifications.length; ++i)
{
if(lPid == notifications[i].getPID())
...
此外,我在表中看到当前后端连接可见select * from pg_stat_activity
,每个程序实例有3个连接。
SELECT 1
- 只是为了更新通知,因为没有&#34;触摸&#34;数据库,通知不会被触发所有后端连接在pg_stat_activity中都有不同的端口和PID。
有什么想法吗?
答案 0 :(得分:1)
pg_notify的PostgreSQL文档似乎解决了这个问题。
执行NOTIFY的客户端通常会在同一通知通道上进行侦听。在这种情况下,它将返回通知事件,就像所有其他侦听会话一样。根据应用程序逻辑,这可能会导致无用的工作,例如,读取数据库表以查找该会话刚刚写出的相同更新。通过注意通知会话的服务器进程PID(在通知事件消息中提供)是否与自己的会话的PID(可从libpq获得)相同,可以避免这种额外的工作。当它们相同时,通知事件是一个人自己的工作反弹,可以忽略。
所以你的客户端得到这样的有效载荷:
SELECT pg_notify('foo', 'payload');
Asynchronous notification of 'foo' received from backend pid 13976
Data: payload
你可以解析它的pid。它有点像session_id我猜。
你可以像这样得到你当前的会话pid:
SELECT pg_backend_pid();
13976
答案 1 :(得分:0)
我花了一段时间来研究解决方案,但我发现它非常酷。我希望在我的软件的一个实例中独立于打开的会话数量,并确保软件实例正确地确定触发器是否来自此实例(即使一个实例可能打开多个会话,并且同一台PC可以打开多个实例)。这是:
SELECT * from pg_stat_activity
EXECUTE 'SELECT application_name from pg_stat_activity where pid IN (SELECT pg_backend_pid())' INTO session_app_name;
notification = json_build_object(
'session', session_app_name, ...);
当PC软件实例收到触发器时,它会比较本地Application_name是否与json('session')中发送的相同,这样我确定触发器是由来自此实例的操作创建的