【问题标题】:jdbc: oracle database change notification & duplicate eventsjdbc:oracle 数据库更改通知和重复事件
【发布时间】:2014-03-08 23:34:18
【问题描述】:

我需要一些监听器来处理 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

【问题讨论】:

  • 只有事件的注册ID和交易ID不同。休息是一样的。

标签: java oracle duplicates


【解决方案1】:

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

我用这个查询检查过:

select TABLE_NAME 
from USER_CHANGE_NOTIFICATION_REGS

【讨论】:

  • 您在代码的哪个位置多次注册?我没有看到这样的东西。
  • 不在这个代码寄存器中多次,就像我多次运行这个程序一样。
【解决方案2】:

如前所述,您有责任通过在您的连接上取消注册来释放您的 DatabaseChangeNotification。就我而言,出于某种原因,当应用程序服务器在 tomcat 上启动时,我正在这样做。首先,我发布旧注册,然后创建新注册。我想我可以重复使用旧的注册,但无论如何我不能。

我正在做这样的事情:

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

SELECT REGID, CALLBACK FROM USER_CHANGE_NOTIFICATION_REGS

然后类似这个伪代码的代码取消注册通知。

connection.unregisterDatabaseChangeNotification(REGID, CALLBACK) 为查询返回的每一行调用。

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

【讨论】:

    【解决方案3】:

    关键是根据 https://docs.oracle.com/cd/E18283_01/appdev.112/e13995/oracle/jdbc/OracleConnection.html 您有责任通过在您的连接中取消注册来释放您的 DatabaseChangeNotification。否则如所述“关闭此连接后注册将继续存在。您需要显式取消注册以在服务器中销毁它并释放驱动程序中的资源。”

    这意味着如果您测试您的示例代码并将其终止,您的注册仍然存在于服务器上,并且您在注册下一个 DatabaseChangeNotification 后会收到额外的通知。不幸的是,我还没有发现重新连接生活注册的热点 - 我需要 regId,但我不知道如何从 OracleConnection 获取它。

    【讨论】:

    • 您从订阅中收到的 DatabaseChangeEvent 包含 RegistrationId 信息,因此您可以将其与您当前的注册(从 DatabaseChangeRegistration.getRegId() 接收)相匹配。
    【解决方案4】:

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

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

    System.out.println("DCE : regId="+dce.getRegristrationId()+"; transactionId="+dce.getTransactionId());
    

    我一直在为 JMS 工作的侦听器需要确认,但我在 Oracle 文档 (http://docs.oracle.com/cd/B28359_01/java.111/b31224/dbmgmnt.htm) 中没有看到任何此类内容。

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

    祝你好运!

    【讨论】:

    • DCE : regId=81; transactionId=[B@7367cb55 DCE : regId=80; transactionId=[B@3b0a0018 DCE : regId=79; transactionId=[B@74439682 DCE : regId=78; transactionId=[B@43b12dbb DCE : regId=77; transactionId=[B@5161dc26 DCE : regId=76; transactionId=[B@3b794c60
    • 好的...所以监听器被注册了四次?而transactionId表示四个独立的交易。
    • 也许尝试打印 Thread.currentThread().getId();查看 Java 代码是否在多个单独的线程中执行。
    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

      • 请不要进行这种多方向的对象访问。这很容易出错。
      【解决方案7】:

      纠正拼写错误:

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

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

      【讨论】:

      • 请不要发布新答案,而是编辑您的旧帖子。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多