0

H2 トリガー機能を使用して、自動混合モード (AUTO_SERVER=TRUE) で H2 データベースに接続されているクライアントが、データベース テーブルで何かが変更されたときに通知を受信できるようにしようとしています。

test(id INTEGER NOT NULL AUTO_INCREMENT, message varchar(1024))

これまでのところ、H2 サーバーのみが TRIGGER 通知を受信しますが、クライアントは通知を受信できないため、データベースへの変更を確認する唯一の方法は、テーブルへのクエリでポーリングすることですが、この方法では TRIGGER 自体は役に立ちません。すべてのクライアントとサーバーはデータベースに変更がないかポーリングします!

トリガーが接続されているすべてのクライアントに通知するか、各クライアント内のメソッドを呼び出して、挿入によってテーブルが変更されたことを認識させる方法はありますか (削除または更新のケースは気にしません)。

Thomas Mueller (H2 データベース作成者) によるこの回答に基づいたコードを以下に投稿します。

import java.sql.*;
import java.util.concurrent.atomic.AtomicLong;
import org.h2.api.Trigger;

public class TestSimpleDb
{
    public static void main(String[] args) throws Exception
    {
        final String url = "jdbc:h2:test;create=true;AUTO_SERVER=TRUE;multi_threaded=true";

        boolean isSender = false;
        for (String arg : args)
        {
            if (arg.contains("receiver"))
            {
                System.out.println("receiver starting");
                isSender = false;
            }
            else if (arg.contains("sender"))
            {
                System.out.println("sender starting");
                isSender = true;
            }
        }

        if (isSender)
        {
            Connection conn = DriverManager.getConnection(url);
            Statement stat = conn.createStatement();
            stat.execute("create table test(id INTEGER NOT NULL AUTO_INCREMENT, message varchar(1024))");
            stat.execute("create trigger notifier "
                    + "before insert, update, delete, rollback "
                    + "on test FOR EACH ROW call \""
                    + TestSimpleDb.Notifier.class.getName() + "\"");

            Thread.sleep(500);

            for (int i = 0; i < 10; i++) {
                System.out.println("Sender: I change something...");
                stat.execute("insert into test(message) values('my message')");
                Thread.sleep(1000);
            }
            conn.close();
        }
        else 
        {
            new Thread() {
                public void run() {
                    try {
                        Connection conn = DriverManager.getConnection(url);
                        while (true) {
                            ;
                            //this loop is just to keep the thread alive..
                        }
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }

    public static class Notifier implements Trigger
    {
        @Override
        public void init(Connection cnctn, String string, String string1, String string2, boolean bln, int i) throws SQLException {
            // Initializing trigger
        }

        @Override
        public void fire(Connection conn, Object[] oldRow, Object[] newRow) throws SQLException {
            if (newRow != null) {
                System.out.println("Received: " + (String) newRow[1]);
            }
        }

        @Override
        public void close() {
            // ignore
        }

        @Override
        public void remove() {
            // ignore
        }
    }
}
4

1 に答える 1

0

すべてのトリガーと同様に、このトリガーはサーバー上で呼び出されます。つまり、データベースを最初に開いたプロセスで自動混合モードを使用する場合です。したがって、最初に「送信者」を起動すると、次の出力が得られます。

sender starting
Sender: I change something...
Received: my message
Sender: I change something...
Received: my message

その後、「レシーバー」を起動すると、次のメッセージが表示されます。

Receiver: event received
Receiver: event received
Receiver: event received

「レシーバー」が変更された行を表示できるようにする場合は、別のアーキテクチャが必要になります。たとえば、タイムスタンプ列をテーブル (およびこの列のインデックス) に追加し、受信側でタイムスタンプが新しい行をクエリできます。これは、追加および変更された行に対してのみ機能します。削除された行については、時間 x 以降に削除された行を含む新しいテーブルを追加する必要がある場合があります。このテーブルは、永久に大きくならないように、時々ガベージ コレクションを行う必要があります。

于 2013-10-11T14:53:28.697 に答える