2013-10-10 42 views
0

我成功地在AUTO_SERVER模式下使用H2數據庫,以便透明地在網絡上的多個桌面客戶端之間共享數據庫文件。 這樣一個服務器在客戶端和所有其他客戶端從tcp服務器讀取選出。使用H2數據庫服務器如何將更改通知給客戶端(JMS消息傳送)

我缺少的是客戶端或服務器如何通知所有其他桌面客戶端數據庫中的某些內容已更改。 現在我正在使用JGroups渠道讓所有客戶互相交流,但這是另一個失敗點,另一個與H2並行運行的領導者選舉算法。

沒有其他方法嗎? 我已閱讀了某些數據庫支持的JMS(Java消息服務Java API)。任何提示H2?

感謝

編輯

下面的代碼是當前的答案的適應,如果我第一次開始發件人(設置ARGS爲「發件人」),他連接的服務器的H2數據庫,然後在遠程機器上執行Receiver(將args設置爲「receiver」),並將它們作爲客戶端連接。

然而只有服務器收到通知,客戶端不會收到任何東西。

這從我目前所知道的情況來看是有意義的:只在服務器上調用觸發器,在客戶端或服務器上調用從客戶端或服務器調用的用戶定義函數,但不在所有連接到的客戶端(和服務器)數據庫。

那麼是否有一種方法可以調整以下方式來通知所有連接的數據庫更改實例?

import java.io.File; 
import java.sql.*; 
import java.util.concurrent.atomic.AtomicLong; 
import org.h2.tools.TriggerAdapter; 

public class TestSimpleDB2 
{ 

    public static void main(String[] args) throws Exception 
    { 
     //final String url = "jdbc:h2:mem:test;multi_threaded=true"; 
     final String url = "jdbc:h2:" + File.separator + "mnt/testdir/PlanIGS" + File.separator 
       + "persondb;create=true;AUTO_SERVER=TRUE;multi_threaded=true"; 
     Connection conn = DriverManager.getConnection(url); 
     Statement stat = conn.createStatement(); 

     boolean isSender = false; 
     args = new String[] 
     { 
      "sender" 
     }; 
     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) 
     { 
      stat.execute("create alias wait_for_change for \"" 
        + TestSimpleDB2.class.getName() 
        + ".waitForChange\""); 
      stat.execute("create table test(id identity)"); 
      stat.execute("create trigger notifier " 
        + "before insert, update, delete, rollback " 
        + "on test call \"" 
        + TestSimpleDB2.Notifier.class.getName() + "\""); 

      Thread.sleep(1000); 
      for (int i = 0; i < 10; i++) 
      { 
       System.out.println("Sender: I change something..."); 
       stat.execute("insert into test values(null)"); 
       Thread.sleep(2000); 
      } 
     } 
     else 
     { 
      new Thread() 
      { 
       public void run() 
       { 
        try 
        { 
         Connection conn = DriverManager.getConnection(url); 
         for (int i = 0; i < 10; i++) 
         { 
          conn.createStatement().execute(
            "call wait_for_change(100000)"); 
          System.out.println("Receiver: event received"); 
         } 
        } 
        catch (Exception e) 
        { 
         e.printStackTrace(); 
        } 
       } 
      }.start(); 
     } 
     conn.close(); 
    } 

    static AtomicLong modCount = new AtomicLong(); 

    public static void waitForChange(long maxWaitMillis) 
    { 
     synchronized (modCount) 
     { 
      try 
      { 
       modCount.wait(maxWaitMillis); 
      } 
      catch (InterruptedException e) 
      { 
       // ignore 
      } 
     } 
    } 

    public static class Notifier extends TriggerAdapter 
    { 

     public void fire(Connection conn, ResultSet oldRow, ResultSet newRow) 
       throws SQLException 
     { 
      modCount.incrementAndGet(); 
      synchronized (modCount) 
      { 
       modCount.notifyAll(); 
      } 
     } 
    } 
} 
+0

確定所有客戶端都連接到同一個數據庫嗎?我可能會用'create = false'來替換'create = true'來確保。另外,你可以檢查文件'persondb.lock.db'中的內容嗎?它是一個純文本文件,應該包含服務器地址和一個隨機密鑰(以及其他一些數據)。 –

回答

1

H2沒有實現JMS(事實上我不知道有一個數據庫可以)。但是,您可以使用觸發器和用戶定義的函數在H2中構建一個簡單的通知機制,如下所示。請注意,這需要H2中的多線程模式,尚未完全測試。因此,使用單獨的數據庫進行消息傳遞可能比您用於數據的數據庫更有意義。

import java.sql.*; 
import java.util.concurrent.atomic.AtomicLong; 
import org.h2.tools.TriggerAdapter; 

public class TestSimpleDb { 

    public static void main(String[] args) throws Exception { 
     final String url = "jdbc:h2:mem:test;multi_threaded=true"; 
     Connection conn = DriverManager.getConnection(url); 
     Statement stat = conn.createStatement(); 
     stat.execute("create alias wait_for_change for \"" + 
       TestSimpleDb.class.getName() + 
       ".waitForChange\""); 
     stat.execute("create table test(id identity)"); 
     stat.execute("create trigger notifier " + 
       "before insert, update, delete, rollback " + 
       "on test call \"" + 
       TestSimpleDb.Notifier.class.getName() + "\""); 
     new Thread() { 
      public void run() { 
       try { 
        Connection conn = DriverManager.getConnection(url); 
        for (int i = 0; i < 10; i++) { 
         conn.createStatement().execute(
           "call wait_for_change(10000)"); 
         System.out.println("Receiver: event received"); 
        } 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }.start(); 
     Thread.sleep(500); 
     for (int i = 0; i < 10; i++) { 
      System.out.println("Sender: I change something..."); 
      stat.execute("insert into test values(null)"); 
      Thread.sleep(1000); 
     } 
     conn.close(); 
    } 

    static AtomicLong modCount = new AtomicLong(); 

    public static void waitForChange(long maxWaitMillis) { 
     synchronized (modCount) { 
      try { 
       modCount.wait(maxWaitMillis); 
      } catch (InterruptedException e) { 
       // ignore 
      } 
     } 
    } 

    public static class Notifier extends TriggerAdapter { 
     public void fire(Connection conn, ResultSet oldRow, ResultSet newRow) 
       throws SQLException { 
      modCount.incrementAndGet(); 
      synchronized (modCount) { 
       modCount.notifyAll(); 
      } 
     } 
    } 

} 
+0

你的代碼看起來非常酷,但AtomicLong modCount上的同步使我認爲它只能在同一個JVM內部工作,而不能跨越在網絡驅動器上共享數據庫的不同計算機上運行的JVM。你能否確認或否認?謝謝 – dendini

+0

你是對的,這隻適用於同一個JVM。但是,這不是問題,因爲數據庫和觸發器以及用戶定義的函數都運行在同一個JVM中。如果使用AUTO_SERVER模式,則觸發器和用戶定義的函數都在服務器中運行(在首先打開數據庫的進程中)。 –

+0

嘿,等等..但你是H2數據庫的創造者!我覺得現在你寫了我的一個搖滾明星:)無論如何我無法弄清楚爲什麼你的代碼在上面Notifier.fire()沒有在遠程客戶端上調用(我想用newRow和oldRow來交換消息) 。我爲這個http://ow.ly/pIOMN創建了一個單獨的問題。謝謝 – dendini

相關問題