基于通知的实时PostgreSQL客户端更新

时间:2017-03-17 08:29:20

标签: java sql postgresql hibernate

我尝试创建客户端 - >数据库通知/更新系统。客户端是用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个连接。

  1. SELECT 1 - 只是为了更新通知,因为没有&#34;触摸&#34;数据库,通知不会被触发
  2. 一个连接与hibernate相关
  3. 一个连接与数据库中的SQL查询相关(如插入/更新/删除行等...)
  4. 所有后端连接在pg_stat_activity中都有不同的端口和PID。

    有什么想法吗?

2 个答案:

答案 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可以打开多个实例)。这是:

  1. 登录数据库时,我使用Application_name命名会话中的应用程序记录(在数据库中):jdbc:postgresql:// localhost:5432 /?ApplicationName = based_on_run_time_hash,您可以使用SELECT * from pg_stat_activity
  2. 在触发器功能中我
  3. 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')中发送的相同,这样我确定触发器是由来自此实例的操作创建的