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

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

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

我用这个查询检查过:

 select TABLE_NAME from USER_CHANGE_NOTIFICATION_REGS 

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

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

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

我正在做这样的事情:

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

 SELECT REGID, CALLBACK FROM USER_CHANGE_NOTIFICATION_REGS 

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

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

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

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

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

祝你好运!

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

在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()); }