Mit JDBC kann man problemlos von Java aus auf relationale Datenbanken zugreifen, wie in [1] gezeigt wurde. Das speichern von Java-Objekten ist damit zur Not auch möglich, aber bei komplexeren Objektbeziehungen stösst man dabei schnell an Grenzen. Objektorientierte Datenbanken bieten sich hier als Lösung an. Die kommerzielle OODB POET wird in diesem Artikel vorgestellt.
Aussen Hui, …
POET kenne ich schon seit der Version 4 für OS/2 und später NT als eine einfach zu benutzende OODB für C++. Die Ankündigung, dass POET jetzt auch für Linux verfügbar ist, war mir deshalb natürlich willkommen und dank einer Teststellung der Hamburger Software-Schmiede ([2]) konnte ich sehen, was sich in den vergangenen Jahren getan hatte.
Geliefert wurde das Paket in einem stabilen Schober mit, man staune, zwei Handbüchern. Dass es Software noch mit gedruckter Dokumentation gibt, ist inzwischen leider schon ungewöhnlich, dabei erlauben es gerade Bücher, dass man in der S-Bahn oder faul auf der Couch liegend sich mit der Software beschäftigt. Gerade letzteres geht auch mit einem Notebook nur begrenzte Zeit, aber man spart sich dann das Fitness-Studio :-). Die beiden mitgelieferten Handbücher sind jeweils ein Programmer’s Guide für C++ und Java (in Englisch). Flüssig geschrieben, vom Einfachen zum Komplexen lernt man wesentliche Dinge im Umgang mit POET kennen. Ausserdem erkennt man, dass die Software schon abgehangen ist, es bleiben wenig Wünsche offen.
Neben den Büchern wurden zwei CDs in edlen Hüllen aus Metall mitgeliefert: einmal die POET Object Server for Linux CD, einmal der POET SDK for Linux, der die Entwicklung von C++ und Java-Applikationen erlaubt. Ausserdem noch Lizenzvereinbarung und Lizenzschlüssel. Das Äussere macht also einen guten Eindruck, wie aber sind die inneren Qualitäten?
… und innen RPM
Ein Blick auf die CDs zeigt: eine ganze Reihe von RPM-Paketen, sowie verschiedene Handbücher im PDF-Format und eine Reihe von HTML-Dokumenten. Selbst der Acrobat-Reader für Linux wird in der aktuellen Version mitgeliefert. Die Installationsanweisung ist dann auch recht knapp: je nach verwendetem Desktop wird der entsprechende graphische Paketmanager empfohlen.
POET ist für Red-Hat 6.0 freigegeben, aber die Linux-Welt sieht halt anders aus: es tummeln sich eine Vielzahl von Distributionen. Deshalb wurde als erster Härtetest eine Installation auf SuSE versucht. Unter 6.1 klappt dies zurecht nicht, da diese Version nicht auf der glibc 2.1 basiert, aber mit der 6.3 gab es keinerlei Schwierigkeiten. Die Software installiert sich unter /opt/poet60 und belegt 33 MB, wenn man nur die Java-relevanten Teile installiert, ansonsten noch 8 MB mehr. Wer Probleme mit dem Plattenplatz hat, kann auch auf die Installation der Dokumentation verzichten, diese befindet sich auch auf den CDs (das spart nochmals 10 MB, die Beispiele sind 4 MB gross).
Nacharbeiten
Waren bis jetzt die Eindrücke rundherum positiv, so sind jetzt ein paar kleinere Mängel festzustellen. So sind einige Dinge noch manuell durchzuführen, die man auch automatisieren könnte. So muss man in /etc/services die beiden Einträge
01: [] 02: license=ZAAAA-AAAAA-AAAAA-AAAAZ 03: 04: [serversNodesServer] 05: name=LOCAL 06: 07: [schemataNodesDict] 08: oneFile = false 09: name = dict 10: 11: [databasesNodesDB] 12: oneFile = false 13: name = db 14: location = SAME
hinzufügen – eigentlich eine Aufgabe für ein Post-Install-Script. Ausserdem fehlt ein Start-Stop-Script für den Init-Prozess. Aber für einen SysAdmin sind dies Kleinigkeiten.
Neben den oben aufgeführten Dingen muss man noch in der Server-Konfigurationsdatei ptserver.cfg den mitgelieferten Lizenzschlüssel eintragen. Ausserdem ist darauf zu achten, dass die Umgebung stimmt. Das folgende kleine Script ändert die notwendigen Pfade:
#!/bin/sh POETROOT=/opt/poet60 export PATH=$POETROOT/bin:$PATH export LD_LIBRARY_PATH=$POETROOT/lib:$LD_LIBRARY_PATH export CLASSPATH=$POETROOT/lib/POETClasses.jar: $POETROOT/lib/collections.jar:$CLASSPATH
Die Datei collections.jar enthält dabei die Collection-Klassen des JDK 1.2, die somit auch unter 1.1.7 verwendbar sind.
Danach kann man den POET Object Server aus einer Shell mittels ptserver starten. Das Kommandozeilen-Tool ptsu erlaubt eine (primitive) Administration des Servers, z.B. den Shutdown aus einem Script heraus. Es bleibt noch zu erwähnen, dass es nur einen Server sowohl für C++-Objekte als auch für Java-Objekte gibt. Wenn man einige Dinge beachtet (z.B. in C++ auf Mehrfachvererbung verzichtet), so kann man dann auch von beiden Sprachen aus auf die gespeicherten Objekte zugreifen. Bevor es an ein Programm-Beispiel geht, möchte ich noch etwas auf die Konzepte hinter POET eingehen.
Konzepte
POET lässt sich auf drei Wegen einsetzen:
- Lokal, d.h. die Datenbank besteht aus Dateien auf dem Rechner des ausführenden Programms. Der Serverprozess ist hier nicht notwendig, genausowenig wie die Einträge in die /etc/services. Dieser Modus heisst auch Embedded Mode, da die Engine hier Bestandteil des Programms ist.
- Als klassische Client-Server Anwendung, hier greift die Client-Anwendung über die in der /etc/services definierten Ports auf den Poet-Server zu.
- Als 3-tier Anwendung, bei dem ein Client auf einen Applikations-Server zugreift, der wiederum seine Objekte in der POET-Datenbank (lokal oder als Client-Server-App) speichert.
Je nach Art des Einsatzes greift ein anderes Lizenzmodell. Darüberhinaus kostet natürlich eine Client-Server Anwendung mehr Zeit als ein lokaler Zugriff auf Dateien. Neben der Zugriffszeit ist aber auch der Umfang eines Clients wichtig. Das 3-tier Modell erlaubt schlanke Clients (weil die Applikationslogik auf einen Server ausgelagert werden kann), und wird ausdrücklich von POET unterstützt. So wird nicht nur ein Java-Applikationsserver mitgeliefert, der die Kommunikation mittels RMI oder CORBA unterstützt, sondern auch im Handbuch wird auf diese Situation ausführlich eingegangen.
Bei meinen Tests hat sich gezeigt, dass der Embedded Mode ohne Probleme funktionierte, während der Zugriff auf den Object Server stets mit Abstürzen endete (mal gab es einen Abort, weil eine interne Assertion nicht erfüllt war, mal eine Segment Violation). Inwiefern letzteres darauf zurückzuführen ist, dass ich nicht auf dem zertifizierten Red-Hat 6.0 arbeitete, konnte ich bis jetzt wegen der knappen Zeit nicht feststellen und deswegen auch nicht in die abschliessende Beurteilung einbeziehen. Vielleicht sollte POET die GPL-Version von Red-Hat mitliefern, damit man in einem Referenz-System das Problem testen kann. Der Test von 3-tier Anwendungen sowie des C++-Teils von POET wurde ebenso aus Zeitgründen nicht durchgeführt.
Die Speicherung von Objekten in einer POET-DB beruht darauf, dass es ein Dictionary oder Schema gibt, welches Informationen über die Objekte enthält. Das Dictionary selbst ist wieder eine POET-DB und wird durch einen Präprozessor (ptjavac) erzeugt bzw. aktualisiert. POET verzichtet auf proprietäre Erweiterungen von Java, deshalb müssen alle Meta-Informationen, die für POET relevant sind, in einer einfach strukturierten Konfigurationsdatei (ptjavac.opt) abgelegt werden. Listing 1 zeigt diese Datei für unser Beispiel. Neben Angaben wo und unter welchen Namen das Schema anzulegen ist, enthält es Informationen über die persistenten Klassen. ptjavac ruft intern javac auf, eine Möglichkeit, als Alternative jikes zu verwenden, wäre sehr wünschenswert, da dies die Entwicklung doch stark beschleunigen würde.
Listing 1: ptjavac.opt |
01: [schemataNodesDict] 02: oneFile = false 03: name = dict 04: server = LOCAL 05: 06: [databasesNodesDB] 07: oneFile = false 08: name = db 09: location = SAME 10: 11: [classesNode] 12: schema = NodesDict 13: persistent = true 14: hasExtent = true 15: useIndexes = NodeIndex 16: 17: [indexesNodeIndex] 18: class = Node 19: members = iDepth, iLeafNr |
Das Dictionary erfüllt darüberhinaus noch eine wichtige Funktion im Lebenszyklus einer Anwendung. Im Laufe der Zeit ändern sich Klassen, aber eventuell sind Objekte noch in einer alten Version in der Datenbank vorhanden. Als Programmierer muss man sich nicht darum kümmern. Beim Lesen werden die alten Objekte korrekt an die aktuelle Klassendefinition angepasst und beim Zurückschreiben werden sie dann mit der neuen Version ersetzt. Dies lässt sich mit den Admin-Utilities auch für alle Objekte einer Klasse durchführen, was aus Performancegründen sinnvoll sein kann.
Datenbanken bestehen unter POET entweder aus zwei Dateien (Daten und Indices getrennt), oder einer einzelnen Datei. Im ersten Fall ist der “Name” der Datenbank ein Verzeichnis, im zweiten Fall ist es der Name der Datei. Zur Laufzeit können diese Namen wieder mittels einer Konfigurationsdatei auf andere Namen gemappt werden, so dass man recht flexibel ist, was den Speicherort seiner Dateien anbelangt. Sinnigerweise verwendet man aus der Anwendung heraus sowieso nur Alias-Namen, und nicht absolute Pfade zu Dateien. Solange keine Anwendung läuft, ist auch das Backup kein Problem: man sichert einfach die Dateien. Bei Datenbanken, auf die mittels des POET-Servers eventuell rund um die Uhr zugegriffen wird, kann man eine Sicherung mittels des Tools ptadmin anstossen.
Die grundlegende Philosophie von POET bezüglich persistenter Objekte ist persistence-by-reachability. Dies bedeutet, dass die Speicherung eines Objekts auch zur Speicherung aller Objekte führt, auf die dieses Objekt Referenzen hält. Dies setzt sich rekursiv fort. Damit genügt es, nur die “Top-Level” Objekte zu speichern. Das Lesen erfolgt analog über intelligente Proxies: erst bei der Dereferenzierung eines Objekts erfolgt der Datenbankzugriff und seine Erstellung im Adressraum des Clients. Da es manchmal aus Performance-Gründen sinnvoll sein kann, Objekte im Voraus auszulesen, ist dieses Verhalten weitgehend konfigurierbar.
Ran an den Editor
Als Beispiel dient ein Programm, das eine Reihe von verallgemeinerten Binärbäumen erzeugt und in einer Datenbank abspeichert. Es ist eine modifizierte Version eines unter /opt/poet60/Examples/JavaODMG/test/performance/ verfügbaren Beispiels. Die mitgelieferte Version hat zwei Macken: einmal hat dort ein Binärbaum der Tiefe null drei Knoten (je nach Zählung vermutet man null oder einen Knoten), zum anderen wird dort nicht nur die Zeit für die Speicherung der Bäume in der Datenbank gemessen, sondern auch die Zeit für deren Erzeugung. Listing 2 gibt die Defintion der Knoten-Klasse. Gespeichert wird die Ebene, die Knoten-Nummer innerhalb der Ebene, der Name des Wurzel-Knotens sowie die absolute Nummer des Knotens. Da dies die einzige persistente Klasse ist, gibt es in der Konfigurationsdatei (Listing 1) nur Informationen hierzu.
Listing 2: Node.java |
001: import java.io.*; 002: import java.util.*; 003: import COM.POET.odmg.*; 004: import COM.POET.odmg.collection.*; 005: 013: public class Node { 014: 015: private static final transient String 016: DB_NAME = "NodesDB", 017: DB_INDEX = "NodeIndex", 018: DB_SERVER = "NodesServer"; 019: private transient int iNodes; 020: private static transient int iGlobalNr = 0; 021: private static transient Node[] iTop; 022: 023: private int iDepth, iLeafNr, iNumber; 024: private String iRootName; 025: private Node[] iLeafs; 026: 033: public Node(int currentDepth, int nodesPerLeaf, int leaf, String name) { 034: iDepth = currentDepth; 035: iNodes = nodesPerLeaf; 036: iLeafNr = leaf; 037: iLeafs = null; 038: iRootName = name; 039: setGlobalNr(); 040: } 041: 049: private synchronized void setGlobalNr() { 050: iGlobalNr += 1; 051: iNumber = iGlobalNr; 052: } 053: 060: public void createTree() { 061: if (iDepth == 0) 062: return; 063: else { 064: iLeafs = new Node[iNodes]; 065: for (int i=0;i<iNodes;++i) { 066: Node n = new Node(iDepth-1,iNodes,i,iRootName); 067: iLeafs[i] = n; 068: n.createTree(); 069: } 070: } 071: } 072: 079: public static int count() { 080: return iGlobalNr; 081: } 082: 089: public String getRootName() { 090: return iRootName; 091: } 092: 099: public String toString() { 100: return iRootName+"("+iDepth+","+iLeafNr+","+iNumber+")"; 101: } 102: 109: public static void writeDB() throws ODMGException { 110: Database db = null; 111: String dbName = "poet://"+DB_SERVER+"/"+DB_NAME; 112: long start, end; 113: System.out.println("Opening database " + dbName); 114: db = Database.open(dbName, Database.openReadWrite); 115: System.out.print("Saving nodes in db: "); 116: start = System.currentTimeMillis(); 117: for (int i=0; i<iTop.length; ++i) { 118: Transaction txn = new Transaction(); 119: txn.begin(); 120: db.bind(iTop[i],iTop[i].getRootName()); 121: txn.commit(); 122: } 123: end = System.currentTimeMillis(); 124: System.out.println(Long.toString(end-start) + " msec"); 125: db.close(); 126: } 127: 134: public static void queryDB(int depth) throws ODMGException { 135: Database db = null; 136: String dbName = "poet://"+DB_SERVER+"/"+DB_NAME; 137: String queryString = "SELECT n FROM NodeExtent AS n WHERE " + 138: "n.iDepth = " + depth; 139: long start, end; 140: 141: System.out.println("Opening database " + dbName); 142: db = Database.open(dbName, Database.openReadOnly); 143: System.out.println("Retrieving nodes with depth=="+depth+" from db: "); 144: start = System.currentTimeMillis(); 145: Transaction txn = new Transaction(); 146: txn.begin(); 147: OQLQuery query = new OQLQuery(queryString); 148: Object res = query.execute(); 149: if (res instanceof CollectionOfObject) { 150: CollectionOfObject result = (CollectionOfObject) res; 151: long n = result.size(); 152: System.out.println(" Number of objects found: " + n); 153: Enumeration enum = result.elements(); 154: while (enum.hasMoreElements()) 155: System.out.println(" " + enum.nextElement().toString()); 156: } 157: txn.commit(); 158: end = System.currentTimeMillis(); 159: System.out.println(Long.toString(end-start) + " msec"); 160: db.close(); 161: } 162: 169: public static void searchDB(int depth) throws ODMGException { 170: Database db = null; 171: String dbName = "poet://"+DB_SERVER+"/"+DB_NAME; 172: String queryPredicate = "WHERE this.iDepth = " + depth; 173: long start, end; 174: 175: System.out.println("Opening database " + dbName); 176: db = Database.open(dbName, Database.openReadOnly); 177: System.out.println("Searching nodes with depth=="+depth+" from db: "); 178: start = System.currentTimeMillis(); 179: Transaction txn = new Transaction(); 180: txn.begin(); 181: Extent nodeExtent = new Extent(db,"Node"); 182: System.out.println("Number of nodes: " + nodeExtent.size()); 183: nodeExtent.setIndex(DB_INDEX); 184: nodeExtent.setFilter(queryPredicate); 185: while (nodeExtent.hasMoreElements()) 186: System.out.println(" " + nodeExtent.nextElement().toString()); 187: txn.commit(); 188: end = System.currentTimeMillis(); 189: System.out.println(Long.toString(end-start) + " msec"); 190: db.close(); 191: } 192: 199: public static void create(int depth, int nodesPerLeaf) { 200: long start, end; 201: System.out.print("Creating trees: "); 202: start = System.currentTimeMillis(); 203: for (int i=0;i<iTop.length;++i) { 204: iTop[i] = new Node(depth,nodesPerLeaf,0,"Tree " + Integer.toString(i)); 205: iTop[i].createTree(); 206: } 207: end = System.currentTimeMillis(); 208: System.out.println(Long.toString(end-start) + " msec"); 209: } 210: 213: public static void main(String[] args) throws ODMGException { 214: if (args.length<3) { 215: System.out.println("usage: java Node depth nodes/leaf n_trees"); 216: System.exit(1); 217: } 218: 219: long start, stop; 220: 221: int depth = Integer.parseInt(args[0]); 222: int nodesPerLeaf = Integer.parseInt(args[1]); 223: int nTrees = Integer.parseInt(args[2]); 224: 225: System.out.println("Creating "+nTrees+" trees with depth "+depth+" and "+ 226: nodesPerLeaf+" nodes per leaf"); 227: start = System.currentTimeMillis(); 228: 232: iTop = new Node[nTrees]; 233: create(depth,nodesPerLeaf); 234: System.out.println("Number of nodes: " + count()); 235: writeDB(); 236: queryDB(depth-1); 237: searchDB(depth-1); 238: stop = System.currentTimeMillis(); 239: System.out.println("Total time: " + (stop-start) + " msec"); 240: } 245: }
|
Das Programm besteht aus mehreren Teilen. Zuerst werden die Bäume erzeugt, wobei die Tiefe, die Anzahl der Sub-Knoten und die Zahl der zu erzeugenden Bäume als Parameter mitzugeben sind. Hier sollte man nicht zu grosszügig sein, da die Gesamtzahl der Knoten exponentiell wächst. Erzeugt man z.B. fünf Bäume mit fünf Ebenen und jeweils vier Sub-Knoten pro Ebene, hat man am Ende 6825 Knoten erzeugt. Das dauert auf meinem 166MHz-Rechner ca. 200 Millisekunden, die Speicherung in der Datenbank dann ca. 46 Sekunden. Naiverweise sollte man annehmen, dass die Zeit zum Erzeugen der Bäume linear mit der Anzahl der Bäume steigt. Herumprobieren zeigt, dass dies nicht so ist. Man erkennt hier deutlich, dass die Speicherallokation ein kritischer Zeitfaktor in Java-Programmen ist. Setzt man die anfängliche Heapgrösse des Programms hoch genug (durch den Switch -ms), etwa mittels jre -ms64M -mx64M, dann verhält sich das Programm wie man es erwartet. Die Speicherung in der Datenbank wächst stärker als linear, hier scheint der interne Verwaltungsaufwand mit der Anzahl der Objekte zu steigen (interessanterweise verhält sich die Speicherung der Objekte mittels einfacher Objektserialisierung in eine Datei auch nicht linear).
Datenbanken und Transaktionen
Das API für Datenbanken ist recht einfach. Datenbanken werden mittels einer URL in der Form poet://DB-Server/DB-Name identifiziert, wobei sowohl der DB-Server als auch der DB-Name über die Laufzeit-Konfigurationsdatei poet.cfg wie oben beschrieben geändert werden können (siehe Listing 3). Datenbanken können nur lesend oder für Lesen und Schreiben geöffnet werden. Neben open() und close(), den beiden am meisten genutzten Methoden, gibt es noch eine Reihe von Hilfsfunktionen sowie administrative Methoden, die aber eher für Tools interessant sind und weniger für Anwendungen.
Listing 3: poet.cfg |
01: [] 02: license=ZAAAA-AAAAA-AAAAA-AAAAZ 03: 04: [serversNodesServer] 05: name=LOCAL 06: 07: [schemataNodesDict] 08: oneFile = false 09: name = dict 10: 11: [databasesNodesDB] 12: oneFile = false 13: name = db 14: location = SAME |
Änderungen an der Datenbank werden über Transaktionen gesteuert. Hier werden alle klassischen Mechanismen unterstützt (commit, checkpoint, abort). Transaktionen erlauben auch, den Lock-Level der Objekte zu spezifizieren. Die Java-APIs erlauben hierbei nur einen Subset der Levels, die unter C++ zur Verfügung stehen, aber die Einschränkungen sind für normale Anwendungen irrelevant. Ein sehr nützliches Feature in diesem Zusammenhang ist Watch and Notify. Über ein Event-Framework kann man sich benachrichtigen lassen, wenn Objekte in der Datenbank geändert werden. Dieser Mechanismus erlaubt optimistisches Locking und ist für Applikationen interessant, in denen die meisten Objekte von den Anwendern nur gelesen werden, Änderungen aber selten sind (z.B. ein Wörterbuch, das von mehreren Autoren betreut wird: hier tritt es selten auf, dass zwei Autoren gleichzeitig dieselben Einträge editieren).
Objekte können in der Datenbank mit oder ohne Namen gespeichert werden, wobei wie oben beschrieben alle referenzierten Objekte mit in die Datenbank wandern. Der Name eines Objekts muss eindeutig sein, sonst kommt es zu einer Exception (dieser Fall wird im Beispielprogramm nicht abgefangen, weshalb man zwischen zwei Läufen immer wieder für eine Neuanlage der Datenbank sorgen muss). Die entsprechenden Methoden sind bind() und lookup(). Während bind() das Objekt speichert, entfernt die Methode unbind() dagegen nur die Namenszuordnung, ohne das Objekt in der Datenbank zu löschen. Im Beispiel sieht man das Speichern von Objekten in der Methode writeDB().
Suchfunktionen
Objekte mit Namen können wie oben beschrieben direkt gelesen werden. Für Anwendungen, bei denen die Navigation sehr stark über Referenzen erfolgt, ist dies meist schon ausreichend: die Top-Level Objekte werden etwa dem Benutzer in einer GUI angezeigt, von dort klickt er sich weiter zu den referenzierten Objekten, die dann aus der Datenbank geladen werden. Bei anderen Anwendungen sind aber Suchfunktionen unerlässlich, und hier bietet POET verschiedene Ansätze. So wird die Object Query Language, kurz OQL unterstützt, die an die SQL angelehnt ist und von der Object Data Management Group (ODMG; siehe [3]) standardisiert ist. Im Beispiel wird in der Methode queryDB() über eine solche Query alle Objekte gesucht, die sich auf der zweitobersten Ebene der Bäume befinden.
Neben dem klassischen Ansatz über Queries unterstützt POET noch einen Mechanismus, der sogenannte Extents verwendet. Extents sind gewissermassen readonly Collections aller Objekte einer Klasse (einschliesslich Sub-Klassen) innerhalb einer Datenbank. Sie sind mit einem Cursor einer SQL-DB zu vergleichen. Extents erlauben über setFilter() nur definierte Elemente anzusprechen, ausserdem kann man über setIndex() einen definierten Index setzen und so bei Iteration über alle Elemente eine Reihenfolge gemäß eines Index erzwingen (siehe Methode searchDB() in Listing 2). Die Suche über Extents ist deutlich schneller (in meinem Beispiel dreimal so schnell), weshalb ich vermute, dass die OQL-Suche nicht den Datenbank-Index verwendet.
Neben den hier beschriebenen Funktionalitäten bietet POET noch eine Reihe von Dingen, die die Erstellung von komplexen Anwendungen erleichtert. So gibt es ein Constraints-Interface, welches über die Methoden postRead(), preWrite() und preDelete() es erlaubt, die Erzeugung von Objekten (z.B. von transienten Feldern) zu beeinflussen und vor Änderungen an der Datenbank Datenintegritätsprüfungen einzufügen. Ausserdem gibt es ein ausgefeiltes Event-Framework, welches die Überwachung von Datenbanken, Transaktionen sowie Lese- und Schreibaktionen vereinfacht. Und auf die Unterstützung von 3-Tier Applikationen über den mitgelieferten Java-Applikationsserver und dem Application Server Cache habe ich schon hingewiesen.
finally{}
Zum Schluss bleibt noch der Blick in die Preisliste. Laut Webseite muss man für den Object-Server DM 7500,- zahlen, der SDK schlägt mit DM 3500,- zu Buche. Dies scheint viel zu sein (bekommt man doch die Single-User Developer-Edition von IBMs DB2 schon für unter DM 100,-), doch im Bereich der kommerziellen OODBs gibt es noch ganz andere Preise. Die Preise unterscheiden sich nicht von den Preisen der Windows-Versionen, obwohl es für diese Systeme etwas mehr Leistung fürs Geld gibt, z.B. eine graphische Oberfläche für die Datenbankadministration (und wahrscheinlich ein besseres, weil intensiv benutztes und ausgetestetes System). Die Client-Lizenzen sind unter Windows (DM 245,-) nur halb so teuer wie unter Linux (DM 490,-), was ich etwas unverständlich finde.
Betrachtet man die heutigen Kosten, die bei der Software-Entwicklung anfallen, so haben sich die Lizenzpreise schnell amortisiert, denn die Lernkurve ist durch die einfachen und sinnvollen Konzepte flach (die gute Dokumentation tut ihr übriges). Insbesondere für das Aufsetzen und die Pflege der Datenbank bedarf es wenig Einarbeitung. Wegen seiner Einfachheit wäre POET auch in vielen kleinen Projekten die Datenbank der Wahl, doch dagegen spricht der Preis, der unterhalb einer gewissen Projektgrösse nicht vermittelbar ist. Aber ob sich höhere Umsätze bei niedrigeren Preisen insgesamt rentieren, liesse sich wahrscheinlich nur durch Ausprobieren herausfinden.
Infos |
| [1] Coffee-Shop: Artikel-Verwaltung mit JDBC und MySQL, Linux-Magazin 05/99, S. 98ff. |
| [2] POET-Homepage: http://www.poet.de |
| [3] ODMG-Homepage: http://www.odmg.org |
Der Autor |
| Bernhard Bablok arbeitet bei der AGIS mbH (Allianz Gesellschaft für Informatik Service mbH) als Systemprogrammierer im Systems-Management Bereich. Wenn er nicht Musik hört, mit dem Radl oder zu Fuß unterwegs ist, beschäftigt er sich mit Themen rund um Objektorientierung. |
Copyright © 2000 Linux-New-Media AG




Wers gerne simpler (ist Geschmachssache) und und umsonst möchte kann auch GOODS verwenden.
[1] http://www.ispras.ru/~knizhnik/goods.html
Von den Umstrukturierungen im IT Bereich sind die ojektorientierten Datenbanken auch nicht verschont geblieben. Die OODB POET ist seit 2004 bei Versant. Der obige Link fuehrt daher nicht mehr zur OODB POET (Stand Oktober 2005).