レコードがdbテーブルに挿入される場合、自動的にJavaプロセスを実行する必要があります。dbリスナーを実装する最も簡単な方法は何ですか?
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句や例外処理は含まれていません。
ここで同様の回答: javaでデータベースリスナーを作成する方法?
トランザクションをサポートするメッセージキューを使用してこれを実行し、トランザクションがコミットされたとき、または通知をサポートしていないデータベースに対して(接続が閉じられたとき)メッセージを起動します。ほとんどの場合、手動で通知し、通知対象を追跡する必要があります。
Springは[〜#〜] amqp [〜#〜]および[〜#〜の自動トランザクションサポートを提供します] jms [〜#〜]。使用できるより単純な代替手段は GuavaのAsyncEventBus ですが、これは1つのJVMでのみ機能します。以下のすべてのオプションについては、プラットフォームの残りの部分にメッセージキューで通知することをお勧めします。
ORMオプション
Hibernate JPAエンティティリスナーがある のようなライブラリは、これを簡単にしますが、CRUDingのすべてを管理することを前提としているためです。
通常の[〜#〜] jdbc [〜#〜]については、独自のブックキーピングを行う必要があります。つまり、接続がコミットまたは閉じられた後、何かが更新されたことを示すメッセージをMQに送信します。
JDBC解析
ブックキーピングの複雑なオプションの1つは、Java.sql.DataSource
および/またはJava.sql.Connection
を commit()
(そして閉じる)でメッセージを送信するようなカスタムのものでラップ/装飾することです。いくつかのフェデレーションキャッシングシステムがこれを行うと考えています。実行された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
を使用すると、ある程度ポーリングする必要があるため、上記のほとんどを実行してからメッセージをバスに送信します。
一般的な解決策は、おそらく興味のあるテーブルでトリガーを作成し、INSERT
イベントについてリスナーに通知することです。一部のデータベースには、このようなプロセス間通知の正式な手段があります。例えば:
DBMS_ALERT
は、そのような通知の簡単な手段ですNOTIFY
ステートメントは、そのような通知の簡単な手段です仮定:
Javaプログラムを即時にリアルタイムで実行するよりも、標準のポータブルコードを保持することが重要です。将来の代替テクノロジへの移植性を許可する必要があります(たとえば、独自のDBイベント、外部トリガーを避ける)。Javaプロセスは、テーブルにレコードが追加された後わずかに実行できます(たとえば、10秒後)。つまり、スケジュール+ポーリングまたはリアルタイムトリガー/メッセージ/イベントの両方が受け入れられます。
複数の行を一度にすべてテーブルに追加する場合、多くではなく1つのプロセスを実行します。 DBトリガーは、行ごとにJavaプロセスを開始します-不適切です。
サービスの質は重要です。ハードウェアまたはソフトウェアの致命的なエラーが発生した場合でも、Javaプログラムを再度実行して、不完全なデータを処理します。
環境に強力なセキュリティ標準を適用したい(たとえば、JavaまたはDBがOSコマンドを直接実行するのを避ける)
コードを最小限にしたい
コアJava独自のDB機能に依存しない標準コード:
アプリサーバーにアプリがある場合:タイマーサービスを使用するセッションBeanを作成し、JDBC セッションBeanタイマーサービス を使用してテーブルを再度クエリします。
ファイルに書き込み/追加する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コンテナに送信する場合。
このソリューションがあなたのニーズをどこまで満たすかはわかりませんが、オプションとして考えられます。 Oracleを使用している場合は、Oracle Javaプログラムを作成してOracle関数としてコンパイルできます。挿入後トリガーからJavaプログラムを呼び出すことができます。 。