Java环境下Oracle数据库变更通知无法生效的问题求助
Oracle 19c Database Change Notification 无法接收通知排查思路
环境信息
- JDK 11
- ojdbc10/ojdbc11
- Oracle 19c 企业版
- 客户端通过VPN连接数据库
已执行操作
SQL语句
-- 授予变更通知权限 grant change notification to <user>; -- 创建测试表 CREATE TABLE myuser.Test (TEST VARCHAR2(255)); -- 授予用户权限 GRANT CONNECT, CREATE TABLE, CREATE PROCEDURE, CREATE SEQUENCE TO myuser.test; GRANT CHANGE NOTIFICATION TO myuser.test; GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO myuser.test;
Java代码
import java.sql.*; 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 Main { public static void main(String[] args) { try { // Connect to the database Class.forName("oracle.jdbc.driver.OracleDriver"); //jdbc:oracle:thin:@//HOSTNAME:PORT/SERVICENAME OracleConnection conn = (OracleConnection) DriverManager.getConnection("jdbc:oracle:thin:@//<serverIP>:1521/orcl7", "username", "password"); Properties prop = new Properties(); prop.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS,"true"); prop.setProperty(OracleConnection.DCN_BEST_EFFORT,"true"); prop.setProperty(OracleConnection.DCN_QUERY_CHANGE_NOTIFICATION,"true"); DatabaseChangeRegistration dcr = conn.registerDatabaseChangeNotification(prop); try { // add the listenerr: DCNDemoListener list = new DCNDemoListener(); dcr.addListener(list); // second step: add objects in the registration: Statement stmt = conn.createStatement(); // associate the statement with the registration: ((OracleStatement) stmt).setDatabaseChangeRegistration(dcr); ResultSet rs = stmt.executeQuery("select * from myuser.test where test='TEST'"); while (rs.next()) { } String[] tableNames = dcr.getTables(); for (int i = 0; i < tableNames.length; i++) System.out.println(tableNames[i] + " is part of the registration."); rs.close(); stmt.close(); } catch (SQLException ex) { // if an exception occurs, we need to close the registration in order // to interrupt the thread otherwise it will be hanging around. if (conn != null) conn.unregisterDatabaseChangeNotification(dcr); throw ex; } finally { try { // Note that we close the connection! // conn.unregisterDatabaseChangeNotification(dcr); conn.close(); } catch (Exception innerex) { innerex.printStackTrace(); } } } catch (SQLException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } } class DCNDemoListener implements DatabaseChangeListener { public void onDatabaseChangeNotification(DatabaseChangeEvent e) { System.out.println("Caught"); System.out.println(e.toString()); } }
当前问题
- 程序输出显示注册的表为
TMCDB7.TEMP,而非目标表myuser.test - 数据库端
USER_CHANGE_NOTIFICATION_REGS可查看到注册记录,但Java客户端无法接收任何变更通知 - 曾出现ORA-12535错误,现已消失,数据库无其他异常日志
- 客户端通过VPN连接数据库
已尝试的解决方法
- 调整ORA-12535超时时间
- 确认数据库1521端口已白名单放行
- 使用IP地址而非主机名连接数据库
- 添加DCN属性(
DCN_BEST_EFFORT、NTF_LOCAL_TCP_PORT、NTF_LOCAL_HOST)无效 - 升级JDK至11,ojdbc升级至ojdbc11
- 更新监听器队列大小为
QUEUESIZE=99
针对性排查思路
1. 修正DCN注册的目标表问题
程序输出注册的是TMCDB7.TEMP,这是核心异常点,直接导致无法接收myuser.test的变更通知:
- 检查连接用户的默认Schema:若连接用户是
TMCDB7,执行select * from myuser.test时需确认用户拥有该表的SELECT权限,可执行GRANT SELECT ON myuser.test TO <连接用户>补充权限 - 验证表名大小写:Oracle默认将未加双引号的表名转为大写,创建的
myuser.Test实际为MYUSER.TEST,代码中myuser.test会自动解析为大写,但需避免因引号导致的表名不匹配 - 排查查询是否触发临时表:若查询涉及复杂逻辑或权限不足,Oracle可能生成临时表,但此场景下应先确保
myuser.test的查询能正常返回(哪怕结果为空),可在代码中添加查询结果打印,确认查询是否正常执行
2. 确保客户端JVM持续运行
当前代码在conn.close()后主线程直接退出,JVM终止导致监听线程无法存活,无法接收通知:
- 在
finally块的conn.close()后添加阻塞逻辑,例如:
或使用// 阻塞主线程,保持监听线程运行 synchronized (Main.class) { Main.class.wait(); }CountDownLatch:private static final CountDownLatch latch = new CountDownLatch(1); // 在main方法末尾添加 latch.await();
3. 排查VPN环境下的双向网络连通性
DCN是数据库主动向客户端推送通知,需确保数据库能访问客户端的监听端口:
- 确认
NTF_LOCAL_HOST配置的是VPN分配给客户端的内网IP(而非本地环回或局域网IP),确保数据库能通过VPN路由到该IP - 确认
NTF_LOCAL_TCP_PORT指定的端口已在客户端防火墙开放,且VPN策略允许数据库端发起的连接请求 - 在数据库服务器上使用
telnet <客户端VPN IP> <指定端口>测试连通性,若无法连接需调整VPN路由或防火墙规则
4. 验证数据库端DCN配置与权限
- 检查数据库初始化参数:确保
JOB_QUEUE_PROCESSES值≥1(建议设置为10),DCN依赖后台JOB进程推送通知 - 查看
USER_CHANGE_NOTIFICATION_REGS细节:执行以下SQL确认注册的表、通知类型、状态:
若注册的表不是SELECT regid, table_name, operations, notification_type, status FROM USER_CHANGE_NOTIFICATION_REGS;MYUSER.TEST,需重新执行正确的查询关联DCN注册 - 确认用户权限:除已授予的权限外,确保连接用户拥有
ALTER SESSION权限(部分DCN场景需要)
5. 确认ojdbc依赖完整性
JDK11需搭配ojdbc11.jar,且需包含以下依赖包:
- oraclepki.jar
- osdt_core.jar
- osdt_cert.jar
缺少这些依赖可能导致DCN通知推送失败或网络连接异常
内容的提问来源于stack exchange,提问作者Rajesh Ingole
相关产品推荐
相关产品推荐

