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

时间:2014-02-11 09:13:45

标签: 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.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

7 个答案:

答案 0 :(得分:3)

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

我用这个查询检查过:

select TABLE_NAME 
from USER_CHANGE_NOTIFICATION_REGS

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

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

我正在做这样的事情:

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

SELECT REGID, CALLBACK FROM USER_CHANGE_NOTIFICATION_REGS

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

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

我发现的其他内容是,使用附带的编辑器使用PL / SQL Developer更新行时,我会收到同一更新的多个通知。当使用sql更新行时,我按预期收到一个通知。我不知道为什么会这样,但它正在发生。

答案 3 :(得分:0)

从我到目前为止所读到的内容来看,我也有点困惑。

您是否尝试过打印出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缓存,但它看起来你已经在做了......

祝你好运!

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

请修改事件处理程序,如下所示:

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

答案 6 :(得分:0)

纠正拼写错误:

请修改事件处理程序,如下所示:

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