Java jdbc:oracle数据库更改通知&;重复事件

Java jdbc:oracle数据库更改通知&;重复事件,java,oracle,duplicates,Java,Oracle,Duplicates,我需要一些对Oracle数据库表进行任何更改(更新、插入、删除)的侦听器 问题:我通过在我的表上的一次更新得到很多检测 我认为是oracle缓存等 是否可能只检测到真实的变化 我的代码: import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import oracle.jdbc.OracleConnection; import

我需要一些对Oracle数据库表进行任何更改(更新、插入、删除)的侦听器

问题:我通过在我的表上的一次更新得到很多检测

我认为是oracle缓存等

是否可能只检测到真实的变化

我的代码:

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleStatement;
import oracle.jdbc.dcn.DatabaseChangeEvent;
import oracle.jdbc.dcn.DatabaseChangeListener;
import oracle.jdbc.dcn.DatabaseChangeRegistration;

public class OracleDCN {
    static final String USERNAME = "scott";
    static final String PASSWORD = "tiger";
    static String URL = "jdbc:oracle:thin:@localhost:1521:stingdev";

    public static void main(String[] args) {
        OracleDCN oracleDCN = new OracleDCN();
        try {
            oracleDCN.run();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void run() throws Exception{
        OracleConnection conn = connect();
        Properties prop = new Properties();
        prop.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS, "true");
        DatabaseChangeRegistration dcr = conn.registerDatabaseChangeNotification(prop);

        try{
            dcr.addListener(new DatabaseChangeListener() {

                public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
                    System.out.println("Changed row id : "+dce.getTableChangeDescription()[0].getRowChangeDescription()[0].getRowid().stringValue());
                }
            });

            Statement stmt = conn.createStatement();
            ((OracleStatement) stmt).setDatabaseChangeRegistration(dcr);
            ResultSet rs = stmt.executeQuery("select * from EXAMPLE where ID=1");
            while (rs.next()) {
            }
            rs.close();
            stmt.close();
        }catch(SQLException ex){
            if (conn != null)
            {
                conn.unregisterDatabaseChangeNotification(dcr);
                conn.close();
            }
            throw ex;
        }
    }

    OracleConnection connect() throws SQLException {
        OracleDriver dr = new OracleDriver();
        Properties prop = new Properties();
        prop.setProperty("user", OracleDCN.USERNAME);
        prop.setProperty("password", OracleDCN.PASSWORD);
        return (OracleConnection) dr.connect(OracleDCN.URL, prop);
    }
}
输出:

Changed row id : AAAFSzAAAAAAAG8AAA
Changed row id : AAAFSzAAAAAAAG8AAA
Changed row id : AAAFSzAAAAAAAG8AAA
Changed row id : AAAFSzAAAAAAAG8AAA

从目前为止我所读到的,我也有点困惑

您是否尝试打印transactionId以查看它是否实际上是正在生成的唯一事件

System.out.println("DCE : regId="+dce.getRegristrationId()+"; transactionId="+dce.getTransactionId());
我为JMS使用的侦听器需要确认,但我在Oracle文档()中没有看到任何类似的内容

我还发现,如果事件处理是非线程的,它可能会干扰oracle缓存,但看起来您也已经在这样做了


祝你好运

问题是我多次注册数据库。解决方案是在注册之前检查注册用户的更改事件

我用这个查询检查了一下:

select TABLE_NAME 
from USER_CHANGE_NOTIFICATION_REGS

关键是根据 您有责任通过在连接中注销来释放DatabaseChangeNotification。否则,如所述“此连接关闭后,注册将继续有效。您需要显式取消注册以在服务器中销毁它,并释放驱动程序中的资源。”


这意味着,如果您测试了示例代码并将其删除,那么您的注册将保留在服务器上,并且在注册下一个DatabaseChangeNotification之后,您将收到额外的通知。不幸的是,我还没有发现重新连接Live registration的问题——我需要regId,但我还不知道如何从OracleConnection获取它。

每次提交都会得到一个事件,该事件会修改查询中使用的一个表(在代码示例中,只有一个名为“EXAMPLE”的表)。可以将其视为“表更改通知”TCN。换句话说,您可能会得到很多误报,因为您只对一行感兴趣,但如果其他行发生更改,您将收到通知。然后由您过滤事件。这就是为什么此功能只能用于以读取为主的表的原因


在11gR2中,Oracle改进了此通知机制,以允许更精细的通知,称为“查询更改通知”。这一次,您将只收到影响查询结果的更改的通知。有一个选项需要打开以启用QCN而不是TCN。请注意,服务器可能并不总是能够启用QCN。如果查询太复杂,它将返回到TCN。

请按如下方式修改事件处理程序:

public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
    if (e.getRegId() == dcr.getRegId()) 
        System.out.println("Changed row id : "+dce.getTableChangeDescription()[0].getRowChangeDescription()[0].getRowid().stringValue());
}
public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
   if (dce.getRegId() == dcr.getRegId()) 
                    System.out.println("Changed row id : "+dce.getTableChangeDescription()[0].getRowChangeDescription()[0].getRowid().stringValue());
                }
更正打字错误:


请按如下方式修改事件处理程序:

public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
    if (e.getRegId() == dcr.getRegId()) 
        System.out.println("Changed row id : "+dce.getTableChangeDescription()[0].getRowChangeDescription()[0].getRowid().stringValue());
}
public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
   if (dce.getRegId() == dcr.getRegId()) 
                    System.out.println("Changed row id : "+dce.getTableChangeDescription()[0].getRowChangeDescription()[0].getRowid().stringValue());
                }

如前所述,您负责通过在连接中注销DatabaseChangeNotification来释放它们。在我的例子中,由于某种原因,我是在应用服务器在tomcat上启动时这样做的。首先,我发布旧的注册,然后创建新的。我想我可以重新使用旧的注册,但无论如何我不是

我正在做这样的事情:

此查询返回现有注册。您必须以注册通知的用户身份登录

SELECT REGID, CALLBACK FROM USER_CHANGE_NOTIFICATION_REGS
然后,类似于此伪代码的代码将注销通知

SELECT REGID, CALLBACK FROM USER_CHANGE_NOTIFICATION_REGS
unregisterDatabaseChangeNotification(REGID,CALLBACK)对查询返回的每一行调用该函数


我还发现,当使用包含的编辑器使用PL/SQL Developer更新行时,我会收到同一更新的多个通知。当使用sql更新行时,我收到了一个预期的通知。我不知道为什么会发生这种情况,但它正在发生。

DCE:regId=81;事务ID=[B@7367cb55DCE:regId=80;事务ID=[B@3b0a0018DCE:regId=79;事务ID=[B@74439682DCE:regId=78;事务ID=[B@43b12dbbDCE:regId=77;事务ID=[B@5161dc26DCE:regId=76;事务ID=[B@3b794c60Okay...因此,侦听器被注册了四次?transactionId表示四次独立的事务。可以尝试打印Thread.currentThread().getId();查看Java代码是否在多个单独的线程中执行。只有事件的注册ID和事务ID不同。REST是相同的。您在代码中的何处注册了多次?我没有看到类似的内容。不在此代码中注册多次,只要我多次运行此程序即可。DatabaseChangeEvent订阅接收包含注册ID信息,因此您可以将其与当前注册(从DatabaseChangeRegistration.getRegId()接收)匹配。请不要进行此类多索引对象访问。这很容易出错。请不要发布新答案,而是编辑旧帖子。