web-dev-qa-db-ja.com

Javaでdbリスナーを実装する方法

レコードがdbテーブルに挿入される場合、自動的にJavaプロセスを実行する必要があります。dbリスナーを実装する最も簡単な方法は何ですか?

63
Krithika Vittal

Oracleのソリューションがあります。 OracleがJavaのリスナーをリリースしたので、独自に作成する必要はありません。内部でポーリングを使用しない限り、通知は=にプッシュされます。 Java side(おそらく何らかのトリガーに基づく):

public interface Oracle.jdbc.dcn.DatabaseChangeListener 
extends Java.util.EventListener {
    void onDatabaseChangeNotification(Oracle.jdbc.dcn.DatabaseChangeEvent arg0);
}

そして、次のように実装できます(これは単なるサンプルです):

public class DBListener implements DatabaseChangeListener {
    private DbChangeNotification toNotify;

    public BNSDBListener(DbChangeNotification toNotify) {
        this.toNotify = toNotify;
    }

    @Override
    public void onDatabaseChangeNotification(Oracle.jdbc.dcn.DatabaseChangeEvent e) {
        synchronized( toNotify ) {
            try {
                toNotify.notifyDBChangeEvent(e); //do sth
            } catch (Exception ex) {
                Util.logMessage(CLASSNAME, "onDatabaseChangeNotification", 
                    "Errors on the notifying object.", true);
                Util.printStackTrace(ex);
                Util.systemExit();                                       
            }
        }       
    }
}

編集:
次のクラスを使用して登録できます:Oracle.jdbc.OracleConnectionWrapper

public class Oracle.jdbc.OracleConnectionWrapper implements Oracle.jdbc.OracleConnection {...}

どこかにメソッドを作成するとします:

public void registerPushNotification(String sql) {
    Oracle.jdbc.driver.OracleConnection oracleConnection = ...;//connect to db

    dbProperties.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS, "true");
    dbProperties.setProperty(OracleConnection.DCN_QUERY_CHANGE_NOTIFICATION, "true");

    //this is what does the actual registering on the db end
    Oracle.jdbc.dcn.DatabaseChangeRegistration dbChangeRegistration= oracleConnection.registerDatabaseChangeNotification(dbProperties);

    //now you can add the listener created before my EDIT
    listener = new DBListener(this);
    dbChangeRegistration.addListener(listener);

    //now you need to add whatever tables you want to monitor
    Statement stmt = oracleConnection.createStatement();
    //associate the statement with the registration:
    ((OracleStatement) stmt).setDatabaseChangeRegistration(dbChangeRegistration); //look up the documentation to this method [http://docs.Oracle.com/cd/E11882_01/appdev.112/e13995/Oracle/jdbc/OracleStatement.html#setDatabaseChangeRegistration_Oracle_jdbc_dcn_DatabaseChangeRegistration_]

    ResultSet rs = stmt.executeQuery(sql); //you have to execute the query to link it to the statement for it to be monitored
    while (rs.next()) { ...do sth with the results if interested... }

    //see what tables are being monitored
    String[] tableNames = dbChangeRegistration.getTables();
    for (int i = 0; i < tableNames.length; i++) {
        System.out.println(tableNames[i]    + " has been registered.");
    }
    rs.close();
    stmt.close();
}

この例には、try-catch句や例外処理は含まれていません。

44
Adrian

ここで同様の回答: javaでデータベースリスナーを作成する方法?

トランザクションをサポートするメッセージキューを使用してこれを実行し、トランザクションがコミットされたとき、または通知をサポートしていないデータベースに対して(接続が閉じられたとき)メッセージを起動します。ほとんどの場合、手動で通知し、通知対象を追跡する必要があります。

Springは[〜#〜] amqp [〜#〜]および[〜#〜の自動トランザクションサポートを提供します] jms [〜#〜]。使用できるより単純な代替手段は GuavaのAsyncEventBus ですが、これは1つのJVMでのみ機能します。以下のすべてのオプションについては、プラットフォームの残りの部分にメッセージキューで通知することをお勧めします。

オプション-非ポーリング、非データベース固有

ORMオプション

Hibernate JPAエンティティリスナーがある のようなライブラリは、これを簡単にしますが、CRUDingのすべてを管理することを前提としているためです。

通常の[〜#〜] jdbc [〜#〜]については、独自のブックキーピングを行う必要があります。つまり、接続がコミットまたは閉じられた後、何かが更新されたことを示すメッセージをMQに送信します。

JDBC解析

ブックキーピングの複雑なオプションの1つは、Java.sql.DataSourceおよび/またはJava.sql.Connectioncommit() (そして閉じる)でメッセージを送信するようなカスタムのものでラップ/装飾することです。いくつかのフェデレーションキャッシングシステムがこれを行うと考えています。実行されたSQLをトラップして、INSERTまたはUPDATEであるかどうかを確認できますが、非常に複雑な解析とメタデータがないと、行レベルのリッスンを取得できません。残念ながら、これが[〜#〜] orm [〜#〜]が提供する利点の1つであることを認めなければなりません。

Daoオプション

ORMを使用するnotの場合の最適なオプションは、トランザクションが閉じられた後、行が更新された後、DAOでメッセージを手動で送信することです。 メッセージを送信する前に、トランザクションが閉じられていることを確認してください。

オプション-非データベース固有のポーリング

@GlenBestの推奨事項にある程度従ってください。

異なる方法で行ういくつかのこと。タイマーを外部化するか、タイマーを実行するサーバーが1つだけになるようにします(スケジューラーなど)。私はクォーツの代わりにScheduledExecutorService(グアバのListenerScheduledExecutorServiceでラッピングするのが望ましい)を使用します(ポーリングでクォーツを使用してIMHO)。

監視するすべてのテーブルには、「通知」列を追加する必要があります。

次に、次のようなことを行います:

// BEGIN Transaction
List<String> ids = execute("SELECT id FROM table where notified = 'f'");
//If db not transactional either insert ids in a tmp table or use IN clause
execute("update table set notified = 't' where notified = 'f'")
// COMMIT Transaction
for (String id : ids) { mq.sendMessage(table, id); }

オプション-データベース固有

Postgres NOTIFYを使用すると、ある程度ポーリングする必要があるため、上記のほとんどを実行してからメッセージをバスに送信します。

25
Adam Gent

一般的な解決策は、おそらく興味のあるテーブルでトリガーを作成し、INSERTイベントについてリスナーに通知することです。一部のデータベースには、このようなプロセス間通知の正式な手段があります。例えば:

Oracle:

Postgres:

  • NOTIFY ステートメントは、そのような通知の簡単な手段です

その他:

  • 他のデータベースにも同様の通知メカニズムがあるかもしれませんが、私は知りません。
  • Javaプロセスによって消費/ポーリングされるイベントをイベントテーブルに挿入することにより、独自のイベント通知キューテーブルをいつでも実装できます。
22
Lukas Eder

仮定:

  • Javaプログラムを即時にリアルタイムで実行するよりも、標準のポータブルコードを保持することが重要です。将来の代替テクノロジへの移植性を許可する必要があります(たとえば、独自のDBイベント、外部トリガーを避ける)。Javaプロセスは、テーブルにレコードが追加された後わずかに実行できます(たとえば、10秒後)。つまり、スケジュール+ポーリングまたはリアルタイムトリガー/メッセージ/イベントの両方が受け入れられます。

  • 複数の行を一度にすべてテーブルに追加する場合、多くではなく1つのプロセスを実行します。 DBトリガーは、行ごとにJavaプロセスを開始します-不適切です。

  • サービスの質は重要です。ハードウェアまたはソフトウェアの致命的なエラーが発生した場合でも、Javaプログラムを再度実行して、不完全なデータを処理します。

  • 環境に強力なセキュリティ標準を適用したい(たとえば、JavaまたはDBがOSコマンドを直接実行するのを避ける)

  • コードを最小限にしたい

    1. コアJava独自のDB機能に依存しない標準コード:

      • ScheduledExecutorServiceまたはQuartzスケジューラー(またはUNIX cronジョブまたはWindowsタスクスケジューラー)を使用してJavaプログラムを毎分(または10秒ごとに実行できます)。これはスケジューラーとウォッチドッグの両方として機能し、プログラムは24時間実行されます。Quartzはアプリサーバーにも展開できます。
      • Javaプログラムをわずか1分(または10秒)実行し、ループし、JDBCを介してDBを照会し、数秒間スリープし、最後に終了します。
    2. アプリサーバーにアプリがある場合:タイマーサービスを使用するセッションBeanを作成し、JDBC セッションBeanタイマーサービス を使用してテーブルを再度クエリします。

    3. ファイルに書き込み/追加するDBトリガーがあります。 Java 7 filewatcherを使用して、ファイルが変更されたときにロジックをトリガーします Java 7 File Watcher

別のオプションがあります:DBアダプタートリガーロジック(例:FuseまたはMuleまたはOpenAdapter)を備えたオープンソースESBを使用しますが、これは規定の要件を超える強力な機能を提供し、インストールと学習に時間がかかり複雑です。

@Scheduleを使用したEJBタイマーの例:

public class ABCRequest {
   // normal Java bean with data from DB
}

@Singleton
public class ABCProcessor {    
    @Resource DataSource myDataSource;   
    @EJB ABCProcessor abcProcessor;
    // runs every 3 minutes 
    @Schedule(minute="*/3", hour="*")
    public void processNewDBData() {
        // run a JDBC prepared statement to see if any new data in table, put data into RequestData
        try
        {
           Connection con = dataSource.getConnection();
           PreparedStatement ps = con.prepareStatement("SELECT * FROM ABC_FEED;");
           ...
           ResultSet rs = ps.executeQuery();
           ABCRequest abcRequest
           while (rs.hasNext()) {
               // population abcRequest
           }
           abcProcessor.processABCRequest(abcRequst);
        } ...
    }    
}

@Stateless
public class class ABCProcessor {
    public void processABCRequest(ABCRequest abcRequest) {
    // processing job logic
    }
}

関連項目: この回答を参照 CDIイベントオブジェクトをEJBからWebコンテナに送信する場合。

9
Glen Best

このソリューションがあなたのニーズをどこまで満たすかはわかりませんが、オプションとして考えられます。 Oracleを使用している場合は、Oracle Javaプログラムを作成してOracle関数としてコンパイルできます。挿入後トリガーからJavaプログラムを呼び出すことができます。 。

Oracle DBのJavaプログラム

1
Chandru