【问题标题】:Hibernate postgresql notify functionalityHibernate postgresql 通知功能
【发布时间】:2014-08-16 20:53:52
【问题描述】:

我正在编写一个使用 hibernate + JPA 作为 ORM 和 postgresql 9.3 作为数据库后端的应用程序,我需要使用一些 java 代码对一些数据库事件做出反应。
更准确地说,我想构建一个在向表中插入新行时使用 pg_notify() 的触发器。
我已经阅读了相关内容,但所有教程都使用直接 jdbc 连接,而不是通过休眠。
我(认为我)不能使用休眠事件,因为行不是通过休眠插入的,而是由第 3 方应用程序插入的。

有什么方法可以让我通过 hibernate 接收 pg_notify 发送的通知?

-- 更新
现在我有一个 classCastException :

java.lang.ClassCastException: com.sun.gjc.spi.jdbc40.ConnectionWrapper40 cannot be cast to org.postgresql.PGConnection
    at com.xxx.core.impl.dao.PostgresqlLowLevelNotificationDAOImpl$1.execute(PostgresqlLowLevelNotificationDAOImpl.java:36)
    at com.xxx.core.impl.dao.PostgresqlLowLevelNotificationDAOImpl$1.execute(PostgresqlLowLevelNotificationDAOImpl.java:1)

不得不提的是,我使用 Glassfish 4.0 作为 AS。连接池是在 glassfish 上创建的,应用程序通过 jndi 访问。 EntityManager 也是由带有 Spring 的容器注入的。这是我的代码:

@Named
public class PostgresqlLowLevelNotificationDAOImpl implements PostgresqlLowLevelNotificationDAO{

    @PersistenceContext(type =PersistenceContextType.TRANSACTION,synchronization=SynchronizationType.SYNCHRONIZED,unitName="CCPU")
    private EntityManager em;

@Override
public ArrayList<PGNotification> getNotifications(){

    Session session = em.unwrap(Session.class);

    PGNotification[] notifications = session.doReturningWork(new ReturningWork<PGNotification[]>() {

        @Override
        public PGNotification[] execute(Connection connection) throws SQLException {
            PGNotification[] notifications = ((PGConnection) connection).getNotifications();
            return notifications;
        }

    });

    return (ArrayList) Arrays.asList(notifications);
}   

}

-- 更新
我已经修复了 classcast 异常:

@Override
public ArrayList<PGNotification> getNotifications(){

    Session session = em.unwrap(Session.class);


    PGNotification[] notifications = session.doReturningWork(new ReturningWork<PGNotification[]>() {

        @Override
        public PGNotification[] execute(Connection connection) throws SQLException {

            PGConnection pgc = null; 

            if (connection.isWrapperFor(PGConnection.class)) {
                pgc = (PGConnection) connection.unwrap(PGConnection.class);
            }                               

            PGNotification[] notifications = pgc.getNotifications();                
            return notifications;
        }
    });

但我似乎仍然没有收到通知。

更新---
在我实施了 Neil 提出的解决方案后,当我取消部署应用程序时,glassfish 日志中出现此错误:

    2014-06-27T11:03:24.278+0300|SEVERE: The web application [/myApp] created a ThreadLocal    with key of type [io.netty.buffer.PooledByteBufAllocator$1] (value [io.netty.buffer.PooledByteBufAllocator$1@28ad6479]) and a value of type [io.netty.buffer.PoolThreadCache] (value [io.netty.buffer.PoolThreadCache@f9f58cc]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.279+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.Recycler$1] (value [io.netty.util.Recycler$1@267ec117]) and a value of type [io.netty.util.Recycler.Stack] (value [io.netty.util.Recycler$Stack@4bb6e0bf]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.279+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.Recycler$1] (value [io.netty.util.Recycler$1@535d426e]) and a value of type [io.netty.util.Recycler.Stack] (value [io.netty.util.Recycler$Stack@fb46e84]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.280+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.internal.ThreadLocalRandom$2] (value [io.netty.util.internal.ThreadLocalRandom$2@ec3a42a]) and a value of type [io.netty.util.internal.ThreadLocalRandom] (value [io.netty.util.internal.ThreadLocalRandom@4e4ec8f8]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.280+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.Recycler$1] (value [io.netty.util.Recycler$1@166c39f2]) and a value of type [io.netty.util.Recycler.Stack] (value [io.netty.util.Recycler$Stack@1b504a5e]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.281+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.Recycler$1] (value [io.netty.util.Recycler$1@34426f54]) and a value of type [io.netty.util.Recycler.Stack] (value [io.netty.util.Recycler$Stack@759b0e99]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.282+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.concurrent.DefaultPromise$1] (value [io.netty.util.concurrent.DefaultPromise$1@16db9b21]) and a value of type [java.lang.Integer] (value [0]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.282+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.Recycler$1] (value [io.netty.util.Recycler$1@166c39f2]) and a value of type [io.netty.util.Recycler.Stack] (value [io.netty.util.Recycler$Stack@2ba59f40]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.282+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.buffer.PooledByteBufAllocator$1] (value [io.netty.buffer.PooledByteBufAllocator$1@28ad6479]) and a value of type [io.netty.buffer.PoolThreadCache] (value [io.netty.buffer.PoolThreadCache@67a3923]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.283+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.Recycler$1] (value [io.netty.util.Recycler$1@166c39f2]) and a value of type [io.netty.util.Recycler.Stack] (value [io.netty.util.Recycler$Stack@423d2c27]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.283+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.util.Recycler$1] (value [io.netty.util.Recycler$1@535d426e]) and a value of type [io.netty.util.Recycler.Stack] (value [io.netty.util.Recycler$Stack@3e1dd66a]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
2014-06-27T11:03:24.283+0300|SEVERE: The web application [/myApp] created a ThreadLocal with key of type [io.netty.buffer.PooledByteBufAllocator$1] (value [io.netty.buffer.PooledByteBufAllocator$1@28ad6479]) and a value of type [io.netty.buffer.PoolThreadCache] (value [io.netty.buffer.PoolThreadCache@18e7e902]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.

我不得不提一下,在取消部署应用程序时会调用 destroy() 方法。 如果我使用 visualVM 进行监视,则在取消部署应用程序后线程仍然存在。

public void destroy(){

    try{
        Statement statement = pgConnection.createStatement();                  
        statement.addBatch("UNLISTEN xxxTest");
        statement.executeBatch();
        statement.close();          
    }catch(SQLException sqle)   {
        sqle.printStackTrace(); 
    }

}

【问题讨论】:

    标签: java database hibernate postgresql jpa


    【解决方案1】:
    1. 为什么在这里需要休眠?只需使用 JDBC。无论如何,您可能都想触发一个应用程序范围的事件(可能发送一个 websocket msg 或破坏一个 ehcache),如果您需要做一些休眠的事情,请订阅该事件并使用休眠做一些事情。

      李>
    2. http://impossibl.github.io/pgjdbc-ng/ 是你的朋友。无需投票。

    http://blog.databasepatterns.com/2014/04/postgresql-nofify-websocket-spring-mvc.html

    【讨论】:

    • 我需要休眠,因为我想成为应用程序的通用设置并使用相同的资源。谢谢你的提示,我去看看。
    • 我已经用 pgjdbc-ng 实现并且没有休眠,它似乎工作:) 谢谢尼尔。
    • 这里似乎有两个问题: 1. 即使应用程序未部署,也有一个线程保持活动状态。 2. 仅当我部署应用程序两次时才有效。我想这与剩余的开放线程有关....
    • 确保调用 UNLISTEN channelName,并从 pgconnection 注销监听器
    • 我确定 UNLISTEN 被调用了。我已经用日志条目更新了我的问题。
    【解决方案2】:

    Hibernate 不直接支持 PostgreSL 通知,因为这是一个特定的数据库功能,并非所有数据库供应商都支持,并且不符合 ORM 框架的职责。

    尽管如此,您仍然可以尝试将其挂接到您当前正在运行的事务中。你需要访问当前数据库连接做add the listening hook

    这可以通过Session.doWork() 支持来完成:

    session.doWork(new Work() {
        @Override
        public void execute(Connection connection) throws SQLException {
            //add some statement if it's required
            PGNotification notifications[] = ((PGConnection) connection).getNotifications();           
        }
    });
    

    【讨论】:

    • 谢谢弗拉德,我会测试一下,然后返回一个可接受的答案:)
    • 请参阅jdbc.postgresql.org/documentation/92/listennotify.html 中的注释 - 您需要像空语句一样的往返。我现在正在添加一个函数,它将改为执行同步协议消息。
    • 我更新了示例,感谢您的回复。虽然我以前从未尝试过,但看起来可行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 2017-09-28
    • 2021-09-04
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多