H2 H2-Connection bei WebStart

hansmueller

Bekanntes Mitglied
Hallo,

ich habe eine H2-Datenbank und möchte diese über WebStart verteilen.
Der Anwender erhält das Programm, das DBMS (H2) und natürlich die Datenbank (read only). Das ganze soll nämlich auch offline funktionieren.
Meine DB habe ich einfach in eine Jar gepackt.

Wie muß ich jetzt die Connection zu der Datenbank machen?

Ich habe schon folgendes probiert:

Code:
jdbc:h2:MeineDB;IFEXISTS=TRUE;ACCESS_MODE_DATA=r;
Code:
org.h2.jdbc.JdbcSQLException: URL Format Fehler; erwartet "jdbc:h2:{ {.|mem:}[name] | [file:]fileName | {tcp|ssl}:[//]server[:port][,server2[:port]]/name }[;key=value...]", erhalten "jdbc:h2:MeineDB"
URL format error; must be "jdbc:h2:{ {.|mem:}[name] | [file:]fileName | {tcp|ssl}:[//]server[:port][,server2[:port]]/name }[;key=value...]" but is "jdbc:h2:MeineDB" [90046-159]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
	at org.h2.message.DbException.get(DbException.java:169)

Code:
jdbc:h2:file:MeineDB;IFEXISTS=TRUE;ACCESS_MODE_DATA=r;
Code:
org.h2.jdbc.JdbcSQLException: URL Format Fehler; erwartet "jdbc:h2:{ {.|mem:}[name] | [file:]fileName | {tcp|ssl}:[//]server[:port][,server2[:port]]/name }[;key=value...]", erhalten "jdbc:h2:file:MeineDB"
URL format error; must be "jdbc:h2:{ {.|mem:}[name] | [file:]fileName | {tcp|ssl}:[//]server[:port][,server2[:port]]/name }[;key=value...]" but is "jdbc:h2:file:MeineDB" [90046-159]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
	at org.h2.message.DbException.get(DbException.java:169)

Bei WebStart muß man über den Classpath auf die Dateien zugreifen. Da (unter Windows) die heruntergeladen Jars vor dem Anwender versteckt werden, gibt es leider auch keinen Pfad, den man ermitteln könnte.
In der H2-Anleitung Advanced steht zwar, das man
Code:
classpath:
benutzen kann, aber da bringt er mir die gleiche Fehlermeldung wie oben.

Bei Derby würde es so aussehen:
Code:
jdbc:derby:classpath:MeineDB;create=false;

Wie macht man das bei H2, bzw. geht das überhaupt mit H2?

Ich benutze die Version h2-1.3.159.

Alle Jars sind signiert und die JNLP ist auch in Ordnung.

MfG
hansmueller
 
Hallo,

ich habe jetzt herausbekommen, das man am Ende der Connection-URL kein
Code:
;
setzen darf. Das mag H2 offenbar nicht.

Aber das Problem besteht immer noch.

Bei
Code:
jdbc:h2:MeineDB;IFEXISTS=TRUE;ACCESS_MODE_DATA=r
und
Code:
jdbc:h2:file:MeineDB;IFEXISTS=TRUE;ACCESS_MODE_DATA=r
bekomme ich jetzt folgende Fehlermeldung:
Code:
org.h2.jdbc.JdbcSQLException: Datenbank "C:\Programme\Mozilla Firefox\MeineDB" nicht gefunden
Database "C:\Programme\Mozilla Firefox\MeineDB" not found [90013-159]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
	at org.h2.message.DbException.get(DbException.java:169)
bzw. beim IE
Code:
org.h2.jdbc.JdbcSQLException: Datenbank "C:\Dokumente und Einstellungen\hansmueller\Desktop\MeineDB" nicht gefunden
Database "C:\Dokumente und Einstellungen\hansmueller\Desktop\MeineDB" not found [90013-159]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
	at org.h2.message.DbException.get(DbException.java:169)
	at org.h2.message.DbException.get(DbException.java:146)

Hat jemand einen Tipp oder eine Idee?

MfG
hansmueller
 
Hallo,

anscheinend kann H2 noch nicht mit Datenbanken, die im Classpath liegen umgehen. (Siehe: how can I provide a relative path for H2 database file, using hibernate under Tomcat? - Stack Overflow)

Ich sehe daher für mich jetzt 2 Lösungsmöglichkeiten:
  1. Ich kopiere beim Programmstart die DB in einen Temp-Ordner und greife dann darauf zu.
  2. Ich probiere mal HSQLDB aus. Laut deren Doku kann diese auch mit WebStart. Sie ist wie H2 schön klein und laut diverser Tests nur ein bißchen langsamer als H2.

Meine H2-Datenbank hat momentan ca. 5MB. Da wäre die Lösung 1 noch vertretbar, auch wenn das nicht so sauber ist.
Geschwindigkeit spielt hier schon eine Rolle. Ich hatte zuerst mit Derby gearbeitet. Das macht allerdings bei komplizierten Abfragen und langen Listen immer wieder schlapp. Dann habe ich es mal testweise mit H2 probiert und der Geschwindigkeitsunterschied hat mich wirklich überrascht. Bei den Abfragen ist H2 um mindestens 50% schneller und kommt auch mit Abfragen zurecht, bei denen Derby versagt.

Was ist eure Meinung? Oder kennt jemand doch einen Kniff, das H2 auch mit WebStart funktioniert?

MfG
hansmueller
 
Hallo,

ich habe mal die Lösung 1 umgesetzt.

Damit das Kopieren der Datenbank in den Temp-Ordner richtig schnell funktioniert, mußte ich eine ganze Weile im Netz suchen und habe auf der Seite Fast stream copy using java.nio channels waffel’s Weblog folgendes gefunden:
Java:
public final class ChannelTools {
  public static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
    final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
    while (src.read(buffer) != -1) {
      // prepare the buffer to be drained
      buffer.flip();
      // write to the channel, may block
      dest.write(buffer);
      // If partial transfer, shift remainder down
      // If buffer is empty, same as doing clear()
      buffer.compact();
    }
    // EOF will leave buffer in fill state
    buffer.flip();
    // make sure the buffer is fully drained.
    while (buffer.hasRemaining()) {
      dest.write(buffer);
    }
  }
}

Und so benutzt man diese Klasse:
Java:
// allocate the stream ... only for example
final InputStream input = new FileInputStream(inputFile);
final OutputStream output = new FileOutputStream(outputFile);
// get an channel from the stream
final ReadableByteChannel inputChannel = Channels.newChannel(input);
final WritableByteChannel outputChannel = Channels.newChannel(output);
// copy the channels
ChannelTools.fastChannelCopy(inputChannel, outputChannel);
// closing the channels
inputChannel.close();
outputChannel.close();

Die meisten Beispiele im Netz basieren auf dem
Code:
FileChannel inputChannel = fileInputStream.getChannel();
. Das Problem hierbei ist, daß das nur mit Files funktioniert bzw. man zuerst ein File braucht.
Mit dem oberen Code kann man z. B. auch mit einen einfachen InputStream arbeiten.

Ich habe also mit
Java:
InputStream is = getClass().getClassLoader().getResourceAsStream("MeineDatenbank.h2.db");
einen InputStream auf meine Datenbank im Classpath erzeugt und mit
Java:
File tmpH2Datenbank = new File(System.getProperty("java.io.tmpdir"), strDBName);
FileOutputStream fos = new FileOutputStream(tmpH2Datenbank);
eine Datei im Temp-Ordner erstellt.
Mit
Java:
final ReadableByteChannel inputChannel = Channels.newChannel(is);
final WritableByteChannel outputChannel = Channels.newChannel(fos);
// copy the channels
fastChannelCopy(inputChannel, outputChannel);
kopiert man nun die Datenbank aus dem Classpath in den Temp-Ordner.
Und somit habe ich (unter WebStart) eine Datenbank auf die auch H2 zugreifen kann.

Mit diesen Channels geht das Kopieren wesendlich schneller als wenn man den InputStream bitweise in den FileOutputStream schreibt.

Falls jemand einen Verbesserungsvorschlag hat, oder eine andere Idee, immer her damit.

MfG
hansmueller
 
Hallo Hans,
sieht alles ganz vernünftig aus. Ich würde nur aus dieser Zeile:
InputStream is = getClass().getClassLoader().getResourceAsStream("MeineDatenbank.h2.db");
diese Zeile machen:
InputStream is = getClass().getResourceAsStream("MeineDatenbank.h2.db");
Gruß
 
Hast du hier schon reingeschaut :Database URL Overview
Wenn die DB in einem zip/jar file liegt musst du das anscheinend angeben.
Read Only Databases in Zip or Jar File

To create a read-only database in a zip file, first create a regular persistent database, and then create a backup. The database must not have pending changes, that means you need to close all connections to the database first. To speed up opening the read-only database and running queries, the database should be closed using SHUTDOWN DEFRAG. If you are using a database named test, an easy way to create a zip file is using the Backup tool. You can start the tool from the command line, or from within the H2 Console (Tools - Backup). Please note that the database must be closed when the backup is created. Therefore, the SQL statement BACKUP TO can not be used.

When the zip file is created, you can open the database in the zip file using the following database URL:

jdbc:h2:zip:~/data.zip!/test

Databases in zip files are read-only. The performance for some queries will be slower than when using a regular database, because random access in zip files is not supported (only streaming). How much this affects the performance depends on the queries and the data. The database is not read in memory; therefore large databases are supported as well. The same indexes are used as when using a regular database.

If the database is larger than a few megabytes, performance is much better if the database file is split into multiple smaller files, because random access in compressed files is not possible. See also the sample application ReadOnlyDatabaseInZip.
 
Hallo,

@bERt0r: Ja, mit diesem
Code:
zip:
habe ich auch schon herumgespielt. Ich habe sogar die Jar mit der Datenbank in eine weitere Jar gepackt, damit ich bei WebStart im Classpath dann die Jar mit der DB habe. (Es ist dabei egal, ob es sich dabei um eine zip- oder jar-Datei handelt.) Hat aber leider auch nicht funktioniert.
Das Problem ist, daß H2 einen Pfad zu dieser Zip-Datei haben will. (Ich vermute mal, daß H2 die Pfadangaben letztendlich immer in einen absoluten Pfad umwandelt.) Bei WebStart kann man aber (unter Windows) für die heruntergeladenen jar-Dateien keinen Pfad ermitteln. Weiß auch nicht, was sich die WebStart-Entwickler damals dabei gedacht haben die heruntergeladenen Dateien vor dem Anwender (und auch vor dem Programmierer) zu verstecken. Das ist halt so eine Besonderheit von WebStart.

@nillehammer: Danke für den Tipp. Gibt es denn da einen Unterschied, außer das es kürzer ist? Es gibt da ja eine Menge unterschiedlicher Möglichkeiten an die Methode
Code:
getResourceAsStream
zu kommen.

MfG
hansmueller
 

Neue Themen


Zurück
Oben