Java jdbc:oracle数据库更改通知&;重复事件
我需要一些对Oracle数据库表进行任何更改(更新、插入、删除)的侦听器 问题:我通过在我的表上的一次更新得到很多检测 我认为是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
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()接收)匹配。请不要进行此类多索引对象访问。这很容易出错。请不要发布新答案,而是编辑旧帖子。