71

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

4

5 に答える 5

47

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

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 句や例外処理は含まれていません。

于 2012-11-08T15:51:45.693 に答える
29

同様の回答がここにあります: How to make a database listener with java?

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

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

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

ORM オプション

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

通常のJDBCの場合は、独自の簿記を行う必要があります。つまり、接続がコミットまたはクローズされた後、何かが更新されたというメッセージを MQ に送信します。

JDBC 解析

ブックキーピングの複雑なオプションの 1 つは、メッセージを送信する (および閉じる)ように、java.sql.DataSourceおよび/またはjava.sql.Connectionカスタムのものでラップ/装飾することです。commit()一部のフェデレーテッド キャッシング システムがこれを行っていると思います。実行された SQL をトラップし、解析して INSERT か UPDATE かを確認できますが、非常に複雑な解析とメタデータがなければ、行レベルのリッスンは得られません。悲しいことに、これはORMが提供する利点の 1 つであることを認めざるを得ません。ORM は更新内容を認識しています。

ダオオプション

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

オプション - データベース固有でないポーリング

@GlenBestの推奨に従ってください。

私が別の方法で行うことをいくつか。タイマーを外部化するか、1 つのサーバーだけがタイマーを実行するようにします (つまり、スケジューラー)。私はQuartz(スーパーオーバーキルのポーリングにQuartzを使用するIMHO)の代わりに(ScheduledExecutorServiceGuava'sでラップすることをお勧めします)を使用します。ListenerScheduledExecutorService

監視したいテーブルのほとんどすべてに、「通知済み」列を追加する必要があります。

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

// 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); }

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

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

于 2012-11-05T21:20:40.140 に答える
24

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

オラクル:

ポストグル:

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

その他:

  • 私が知らない他のデータベースにも同様の通知メカニズムがあるかもしれません。
  • Java プロセスによって消費/ポーリングされるイベント テーブルにイベントを挿入することにより、独自のイベント通知キュー テーブルをいつでも実装できます。ただし、これを適切に実行してパフォーマンスを向上させるのは非常に難しい場合があります。
于 2012-09-27T10:14:22.917 に答える
10

仮定:

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

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

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

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

  • コードを最小限に抑えたい

    1. プロプライエタリDB機能に依存しないコアJava標準コード:

      • ScheduledExecutorServiceまたはQuartzスケジューラー(またはunix cronジョブまたはWindowsタスクスケジューラー)を使用して、Javaプログラムを1分ごとに実行します(または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
    }
}

関連項目:EJBからWebコンテナへのCDIイベントオブジェクトの送信については、この回答を参照してください。

于 2012-11-06T10:05:49.800 に答える
1

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

Oracle DB の Java プログラム

于 2012-11-05T23:50:53.447 に答える