如何使用 Postgres 数据库在 Java 中获得异步/事件驱动的 LISTEN/NOTIFY 支持?

Posted

技术标签:

【中文标题】如何使用 Postgres 数据库在 Java 中获得异步/事件驱动的 LISTEN/NOTIFY 支持?【英文标题】:How do I get asynchronous / event-driven LISTEN/NOTIFY support in Java using a Postgres database? 【发布时间】:2014-03-05 03:07:27 【问题描述】:

据我所知,Java 中用于 LISTEN/NOTIFY 的 JDBC 驱动程序不支持真正的事件驱动通知。您必须经常轮询数据库以查看是否有新通知。

如果有的话,我在 Java 中有哪些选项(可能不是 JDBC?)以真正的事件驱动方式异步获取通知而无需轮询?

【问题讨论】:

如果你想要通知,你需要一个实体来发出事件。如果 PostgreSQL 不发出事件(关于 JMS 主题等),则您不能拥有事件驱动的通知。 PostgreSQL 提供了 LISTEN/NOTIFY,我理解这是一种异步通知机制。问题是 Java JDBC 不支持异步通知,需要轮询。 对不起。我明白你的意思了......再次查看了驱动程序文档。 用其他语言可以做到的方式是,您向数据库句柄询问连接套接字描述符的底层文件描述符(承诺永远不会自己读取或写入该描述符),然后您就睡着了描述符变得可读。一旦它可读,您就可以像往常一样查询数据库句柄上的通知。不过,我不知道您是如何将其翻译成 Java 语言的。 @jasons2645 抱歉,每当我尝试用它做任何不平凡的事情时,Java 都会让我大发雷霆。如果连接只用于在 LISTEN 完成后获取通知,那么最好将连接放入专用线程,并让它阻塞直到发生某些事情。我最好的猜测是公开 org/postgresql/core/v3/ProtocolConnectionImpl.java 的 Peek() 直到它对 org.postgresql.PGConnection 可见 【参考方案1】:

使用 pgjdbc-ng 驱动程序。

http://impossibl.github.io/pgjdbc-ng/

它支持异步通知,无需轮询。我已经使用成功了。

见https://database-patterns.blogspot.com/2014/04/postgresql-nofify-websocket-spring-mvc.html

Oleg 也有一个很好的示例答案

【讨论】:

另见下面@Oleg Mikhailov 的示例代码,它使用了pgjdbc-ng。 好东西,我想建立网络流,我将在其中流式传输插入数据库中的一些新记录,我知道我可以在不先将它们推送到某个消息代理的情况下做到这一点,但可以直接使用数据库队列,太棒了。 与其指向博客文章,不如在此处包含示例代码会更有用。 @Neil McGuigan 博客网址无效,请查看。【参考方案2】:

这是一个使用 com.impossibl.postgres.api (pgjdbc-ng-0.6-complete.jar) 和 JDK 1.8 的异步模式:

import com.impossibl.postgres.api.jdbc.PGConnection;
import com.impossibl.postgres.api.jdbc.PGNotificationListener;
import com.impossibl.postgres.jdbc.PGDataSource;    
import java.sql.Statement;

public static void listenToNotifyMessage()
    PGDataSource dataSource = new PGDataSource();
    dataSource.setHost("localhost");
    dataSource.setPort(5432);
    dataSource.setDatabase("database_name");
    dataSource.setUser("postgres");
    dataSource.setPassword("password");

    PGNotificationListener listener = (int processId, String channelName, String payload) 
        -> System.out.println("notification = " + payload);

    try (PGConnection connection = (PGConnection) dataSource.getConnection())
        Statement statement = connection.createStatement();
        statement.execute("LISTEN test");
        statement.close();
        connection.addNotificationListener(listener);
        while (true) 
     catch (Exception e) 
        System.err.println(e);
    

为你的数据库创建一个触发器函数:

CREATE OR REPLACE FUNCTION notify_change() RETURNS TRIGGER AS $$
    BEGIN
        SELECT pg_notify('test', TG_TABLE_NAME);
        RETURN NEW;
    END;
$$ LANGUAGE plpgsql;

为您要跟踪的每个表分配一个触发器:

CREATE TRIGGER table_change 
    AFTER INSERT OR UPDATE OR DELETE ON table_name
    FOR EACH ROW EXECUTE PROCEDURE notify_change();

【讨论】:

我也在你的帖子里做。请问while(true)的意义是什么??我还发布了一个关于我之前的问题的类似问题:***.com/questions/37916489/… 不需要 while(true) ,除非 try 循环将终止 - 并关闭连接 - 如果没有什么可以阻止它。连接需要保持打开状态,NOTIFY 消息才能正常工作。 在 PL/PgSQL 过程中使用 SELECT pg_notify() 有什么意义,而仅使用 NOTIFY test 的简单纯 SQL 过程就可以了?【参考方案3】:

似乎没有办法解决这个问题。您可以解决它,因为已经根据这些思路提出了一些建议,但最终您将进行投票。

【讨论】:

这个答案不再有效。

以上是关于如何使用 Postgres 数据库在 Java 中获得异步/事件驱动的 LISTEN/NOTIFY 支持?的主要内容,如果未能解决你的问题,请参考以下文章

Postgres如何创建一个在查询中使用的函数

如何使用hibernate将图像存储到postgres数据库中

使用 Java 和 Postgres 数据库进行客户端加密

Java 枚举、JPA 和 Postgres 枚举——如何让它们一起工作?

如何在 postgresql 中使用 postgres_fdw 从另一个数据库中获取记录

如何使用 JPA 和休眠映射 Java/Kotlin 字符串数组和 Postgres SQL 数组