jdbc: уведомление об изменении базы данных 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 ответов

Решение

Проблема была, я был зарегистрирован в базу данных много раз. Решением было проверить зарегистрированного пользователя на наличие изменений перед регистрацией.

Я проверил это с помощью этого запроса:

select TABLE_NAME 
from USER_CHANGE_NOTIFICATION_REGS

Как уже было сказано, вы несете ответственность за выпуск вашей DatabaseChangeNotification путем отмены регистрации в вашем соединении. В моем случае по какой-то причине я делаю это, когда сервер приложений запускается на tomcat. Сначала я освобождаю старые регистрации, а затем создаю новые. Я думаю, что могу повторно использовать старую регистрацию, но в любом случае это не так.

Я делаю что-то вроде этого:

Этот запрос возвращает существующие регистрации. Вы должны войти в систему как тот же пользователь, который зарегистрировал уведомление.

SELECT REGID, CALLBACK FROM USER_CHANGE_NOTIFICATION_REGS

затем код, подобный этому псевдокоду, отменяет регистрацию уведомления.

connection.unregisterDatabaseChangeNotification (REGID, CALLBACK) вызывается для каждой строки, возвращаемой запросом.

Что-то еще я обнаружил, что при обновлении строки с помощью PL/SQL Developer с помощью включенного редактора я получаю несколько уведомлений об одном и том же обновлении. При использовании sql для обновления строки я получаю одно уведомление, как и ожидалось. Я не знаю, почему это происходит, но это происходит.

Дело в том, что в соответствии с https://docs.oracle.com/cd/E18283_01/appdev.112/e13995/oracle/jdbc/OracleConnection.html вы несете ответственность за выпуск вашей DatabaseChangeNotification путем отмены регистрации в вашем подключении. В противном случае, как описано "Регистрация будет продолжать действовать после закрытия этого подключения. Вам необходимо явно отменить регистрацию, чтобы уничтожить его на сервере и освободить ресурсы в драйвере".

Это означает, что если вы тестируете свой пример кода и уничтожаете его, ваши регистрации остаются на сервере, и вы получаете дополнительные уведомления после регистрации следующего DatabaseChangeNotification. К сожалению, я еще не обнаружил горячую возможность восстановить связь с живой регистрацией - для этого мне потребуется регистрация, но я пока не знаю, как получить ее из OracleConnection.

Из того, что я прочитал до сих пор, я немного сбит с толку.

Вы пытались распечатать идентификатор транзакции, чтобы увидеть, действительно ли это генерируется уникальное событие?

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

Слушатели, с которыми я работал для JMS, требуют подтверждения, но я не вижу ничего подобного в документации Oracle ( http://docs.oracle.com/cd/B28359_01/java.111/b31224/dbmgmnt.htm).

Я также обнаружил, что это может нарушить кеш оракула, если обработка события не обработана, но похоже, что вы уже делаете это тоже...

Удачи!

Вы получите событие для каждого коммита, который изменяет одну из таблиц, которые вы используете в своем запросе (в вашем примере кода только одна таблица называется "ПРИМЕР"). Думайте об этом как "Уведомление об изменении таблицы" 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());
                }
Другие вопросы по тегам