Db4o
| |
---|---|
Basisdaten
| |
Entwickler | Versant Corporation |
Aktuelle Version | 8.0 (1. Februar 2011) |
Betriebssystem | Unix-Derivate, Linux, Windows |
Programmiersprache | C#, Java |
Kategorie | Datenbanksystem, Server |
Lizenz | Duales Lizenzsystem (Proprietär und GPL) |
deutschsprachig | nein |
www.db4o.com im Web Archiv |
db4o (database for objects) ist eine Objektdatenbank für die Java-Plattform und das .Net-Framework, deren Weiterentwicklung im Jahr 2011 eingestellt wurde. Der Quellcode ist noch auf Github verfügbar.[1] Sie gehört zu den NoSQL-Datenbanken. Die db4o-Programmbibliothek zeichnet sich durch einen vergleichsweise geringen Speicherbedarf von etwa 600 kB aus. Dadurch eignet sie sich besonders für die Einbettung in andere Programme und für Umgebungen mit beschränkten Speicher-Ressourcen, wie beispielsweise PDAs. Db4o konnte entweder unter den Bedingungen der GPL oder mit kommerziellen Lizenzen verwendet werden.
db4o unterstützte Javas JDK 1.1.x bis 6.0 und lief nativ auf Jakarta EE, J2SE und allen J2ME-Konfigurationen, deren J2ME-Profil die Reflection-API bereitstellt. Daneben unterstützte db4o alle Sprachen, die auf der virtuellen Maschine CLR der .NET-Plattform aufsetzen, wie beispielsweise C# oder VB.NET. Der für die .NET-Plattform notwendige C#-Quellcode der db4o-Programmbibliothek wurde dabei größtenteils aus dem Java-Quellcode generiert und ist auch auf der Mono-Implementierung der .NET-Plattform lauffähig.
Das db4o-Projekt begann im Jahr 2000 unter Führung von Carl Rosenberger. Entwickelt wurde db4o von der Firma db4objects, Inc., die 2004 mit Hilfe von Investoren wie Mark Leslie (VERITAS Software), Vinod Khosla (Sun Microsystems) und Jerry Fiddler (Wind River) gegründet wurde.
Im Dezember 2008 wurde die Datenbanksparte an Versant verkauft.[2]
db4o wird derzeit nicht mehr weiterentwickelt. Die Webseite www.db4o.com ist nicht mehr erreichbar, und die letzte Änderung bei Sourceforge war 2015, weiterhin gehört Versant seit Dezember 2012 zu Actian.[3]
db4o wurde seit 2004 wahlweise unter der GPL als freie Software lizenziert oder unter einer kommerziellen Lizenz, die die Verwendung in nicht-GPL-Projekten erlaubt. Daneben gab es eine Classpath-ähnliche Lizenz, die es erlaubt, db4o in Open-Source-Projekte zu integrieren, die nicht db4o / GPL konform sind.
API (application programming interface) bezeichnet die Programmierschnittstelle, mit der die Datenbank programmiert wird. Dabei zeigt sich, dass der Unterschied zu anderen Programmen wie Hibernate oder JPA / EJB nicht allzu groß ist.
Ein einfaches Codebeispiel, das zusammenhängend ausgeführt werden sollte:
// Öffne eine Datenbank
ObjectContainer db = Db4o.openFile("C:/beispiel.db");
try {
// Speichere zwei Personen
db.store(new Person("John Doe","TOP INC",45));
db.store(new Person("Clara Himmel","BB Radio",30));
// Iteriere über alle Personen
ObjectSet result = db.queryByExample(new Person());
while (result.hasNext()) {
System.out.println(result.next());
}
// Verändere eine Person
result = db.queryByExample(new Person("Clara Himmel"));
Person found = (Person) result.next();
found.setAge(25);
db.store(found);
// Lösche eine Person
db.delete(found);
// Schreibe die Änderungen fest
db.commit();
}
finally {
// Schließe die Datenbank
db.close();
}
EntityManager
) wird zuerst ein ObjectContainer
als Objektmanager erstellt. Dieser Container kann entweder auf eine Datei verweisen oder in einer Client-Server-Architektur auf eine von einem Server bereitgestellte Datenbank. Im ersten Fall werden alle Daten in einer Datei gespeichert.
Danach werden alle CRUD-Operationen (Create, Read, Update und Delete) ausgeführt:
db.store(object)
wird ein beliebiges Objekt gespeichert. Dies ist analog zum JPA-Standard, wo em.persist(object)
aufgerufen wird. Allerdings muss in letzterem Fall das Domain-Objekt mindestens mit @Entity
annotiert oder in XML registriert worden sein.found
enthält daher das vollständige Objekt, das oben gespeichert wurde.found
-Objekt wird jetzt intern referenziert und kann daher verändert .setAge(25)
und nochmals gespeichert werden. Dann ist es aktualisiert. Im JPA Standard entspricht dieses einem em.merge(object)
, wobei das Objekt immer attached, also von der Session referenziert (gemerkt) werden muss.db.delete(object)
löscht ein Objekt, das dem Objektmanager bekannt sein muss.db4o bietet drei Abfragesprachen, die alle nicht allzu weit von dem entfernt sind, was Hibernate und JPA in EJB 3.0 bieten:
Query by Example steht für eine Suche anhand von Beispielen. Die Grundlagen hierfür wurden bereits in den 1970er Jahren von Moshé M. Zloof in einigen Artikeln gelegt. Hibernate und db4o bieten diesen Mechanismus in einer leicht abgeschwächten Version an.
Bei db4o kann, wie oben gezeigt, ein beliebiges Objekt erstellt werden. Danach wird entweder:
Person.class
): In diesem Fall werden alle in der Datenbank gespeicherten Instanzen der Klasse zurückgeliefert.null
übergeben: Dieses liefert alle Objekte aus der Datenbank zurück.Komplexe Abfragekriterien lassen sich bei Verwendung dieser Art der Abfrage nicht ausdrücken.
Intern werden alle Abfragen bei db4o auf SODA abgebildet. SODA ist die Programmierschnittstelle des auf SourceForge gehosteten Projekts S.O.D.A. – Simple Object Database Access, das teilweise von den Entwicklern von db4o betreut, aber unabhängig von db4o entwickelt wird.
Die SODA-Abfragen ähneln denen von Hibernate-Criteria-Abfragen stark. Ein Beispiel:
Query query=db.query();
query.constrain(Person.class);
query.descend("alter").constrain(50).greater();
ObjectSet result=query.execute();
Hier wird die Abfrage zunächst auf die Klasse Person eingeschränkt, danach weiter auf alle Personen, die älter als 50 sind.
Es handelt sich bei dieser Abfrageart um ein Beispiel für Query by API, bei der Bäume von Objekten erstellt werden, mit denen Abfragen eingeschränkt werden. Sie ist daher mächtiger als die anderen Abfragemechanismen, allerdings auch schwerer zu erstellen, da der Code schwerer zu lesen ist. Durch die Verwendung von Zeichenketten als Abfrageparameter wird die Typsicherheit zum Kompilationszeitpunkt nicht gewährleistet.
Native Abfragen (engl. native queries) sind Abfragen, die in der Programmiersprache der Client-Anwendung formuliert sind, also bei db4o beispielsweise in Java oder C#.
Der Code von nativen Abfragen wird bei db4o normalerweise nicht ausgeführt, sondern zur Laufzeit analysiert. Basierend auf dem Ergebnis der Analyse wird eine SODA-Criteria-Query erstellt. Dieses hat den Vorteil, dass die persistierten Objekte nicht zeitaufwändig instanziiert werden müssen. Nur wenn die Analyse aufgrund der Komplexität der Abfrage fehlschlägt, wird die Objektinstanz erzeugt und der Programmcode der nativen Abfrage muss ausgeführt werden.
Zum Vergleich von nativen Abfragen und JPQL oder HQL sei hier auf Microsofts LINQ verwiesen. Microsoft konnte die Sprache verändern und so mächtige Abfragekonstrukte direkt in C# einbetten.
Fehlerhafte Abfragebefehle werden aus diesen Gründen frühzeitiger erkannt oder durch die Vermeidung häufiger Ursachen eher vermieden, als dieses bei Verwendung spezieller Abfragensprachen der Fall ist.
Entscheidend für den komfortablen Einsatz ist, ob und wie die verwendete Sprache Closures unterstützt. Ohne Closure-Unterstützung muss mit inneren Klassen oder ungebundenen Methodenzeigern beziehungsweise Delegaten gearbeitet werden, um die Abfrage als „freischwebender Code“ einer Methode zu übergeben.
C# .NET 3.0 (Verwendung eines Lambda-Ausdrucks);
var persons = db.Query<Person>( s => s.Age > 50 );
C# .NET 2.0 (Verwendung eines ungebundenen Methodenzeigers):
IList<Person> persons = db.Query<Person>( delegate(Person person){
return person.Age > 50;
});
Java JDK 5 (Verwendung einer generischen anonymen Klasse):
List<Person> personen = db.query(new Predicate<Person>() {
public boolean match(Person person) {
return person.getAge() > 50;
}
});
Java JDK 1.2 bis 1.4 (Verwendung einer anonymen Klasse):
List personen = db.query(new Predicate() {
public boolean match(Object object) {
Person person = (Person)object;
return person.getAge() > 50;
}
});
Java JDK 1.1 (Verwendung einer inneren Klasse):
ObjectSet personen = db.query(new PersonFilter());
public static class PersonFilter extends Predicate {
public boolean match(Object object) {
Person person = (Person)object;
return person.getAge() > 50;
}
}
Mit dem Öffnen des Containers aus obigem Beispiel wird eine Transaktion erstellt. Diese kann jederzeit mit db.commit()
oder db.rollback()
auf die Datenbank geschrieben oder verworfen werden. db4o garantiert dabei ein ACID-Verhalten. Alles innerhalb einer Transaktion entspricht den ACID-Anforderungen. Intern werden dabei alle Zugriffe auf die Datenbank innerhalb der Transaktion serialisiert. Dies hat starke Konsequenzen für das Datenbanksystem:
Erfahrungen zufolge sind normalerweise bis zu einem Dutzend Zugriffe pro Sekunde realisierbar, was für viele Anwendungsfälle ausreichen kann. db4o arbeitet jedoch wegen der obigen Punkte an einer Serverversion, die auch bei nebenläufigen Zugriffen skaliert.
db4o bietet mehrere Arbeitsmodi an:
Beispiel für den Server:
ObjectServer server = Db4oClientServer.openServer("c:/liveserver.db", 8732);
server.grantAccess("user1", "password");
server.grantAccess("user2", "password");
Hier wird ein Thread geöffnet, der auf Clientanfragen auf dem Port 8732 wartet. Der Server selbst kann über Monitore warten und beispielsweise Statusausgaben machen. Zugriffsrechte können nicht direkt von außen mittels eines Tools verwaltet werden. Entweder werden diese im Code angelegt oder per Konsole etc. eingelesen.
Ein Client könnte initial so eingerichtet werden:
aClient = Db4oClientServer.openClient("196.42.103.142", 8732, "user2", "password");
Mittels eines sogenannten „Out-Of-Band Signalling“-Kommunikationsprotokolls können dem Server beliebige Objekte gesendet werden, mit denen ein Server administriert werden kann. Beispielsweise kann die Aufforderung zum Defragmentieren der Datei übermittelt werden.
Mittels weniger Zeilen können beliebig viele Server repliziert (geclustert) werden. Dies geschieht mit dem db4o-dRS-Modul.
Die Objekte bekommen dazu eine eindeutige UUID. Dadurch können einzelne Objekte oder die gesamte Datenbank uni- oder bidirektional verteilt werden, d. h., jedes neue Objekt wird an alle anderen Datenbanken gesendet. Dies erlaubt es große und redundante Architekturen aufzusetzen.
Sehr interessant ist ebenfalls die Option, eine db4o-Datenbank in relationale Datenbanken zu replizieren. Dies bedeutet eine Brücke / Verbindung zwischen beiden Welten, die oft als getrennt angesehen werden. db4o verwendet dazu Hibernate. Nach der Konfiguration der dRS-Bridge können ebenfalls einzelne Objekte oder ganze Datenbanken per Trigger wahlweise in eine oder beide Richtungen transferiert werden. In vielen Fällen können in der Industrie so die Vorteile beider Welten gut ausgenutzt werden.
Bei db4o gibt es drei Modi für das Laden von Objekten:
IMMEDIATE
LAZY
ObjectSet
zurückgeliefert, jedoch ist noch kein Objekt ermittelt worden. Ein Thread sucht alle weiteren Ergebnisse. Dieses Verhalten ist ideal, wenn die ersten Ergebnisse schnell angezeigt werden sollen. Bei diesem Lademodus muss beachtet werden, dass die Abfrage nicht zu einem definierten Zeitpunkt ausgewertet wird. Hierdurch kann es zu Nebeneffekten kommen, wenn zwischen dem Absetzen und dem vollständigen Auswerten der Anfrage Änderungen am Datenbestand vorgenommen werden.SNAPSHOT
ObjectSet
freigegeben werden kann.Objekte bilden normalerweise tiefe Referenzstrukturen. In db4o kann beim Laden von Objekten angegeben werden, bis zu welcher Tiefe referenzierte Objekte implizit mit dem Objekt geladen werden sollen. Zusätzlich besteht die Möglichkeit, diese referenzierten Objekte explizit zu einem späteren Zeitpunkt zu aktivieren. Nähere Informationen zu Aktivierung, Deaktivierung, Abfrage und Aktualisierung referenzierter Objekte können in der db4o-Referenz gefunden werden.
Bei db4o gibt es interne und externe Rückruffunktionen (callbacks). Diese ähneln in ihrer Bedeutung den Datenbanktriggern anderer Datenbanken. Rückrufmechanismen gibt es in vielen Produkten zur Datenbankanbindung. Sie sind ein mächtiges Werkzeug um beispielsweise Überprüfungen durchzuführen, Standardwerte zu setzen oder Sicherheitsaspekte zu berücksichtigen.
Interne Callbacks werden den Klassen der zu speichernden Objekte als Methoden hinzugefügt. Diese werden von db4o dynamisch unter Verwendung der Reflexion erkannt und aufgerufen. Hierzu ist es nicht notwendig, die Klassen von speziellen Schnittstellen abzuleiten. Vergleichbar mit den Join-Points der aspektorientierten Programmierung sind Aufrufe interner Callbacks vor und nach datenbankbezogenen Ereignissen möglich. Der Aufrufzeitpunkt wird hierbei durch das Präfix des Methodennamens festgelegt: objectCan...
–Methoden werden vor und objectOn...
–Methoden werden nach dem Ereignis aufgerufen. Durch den Rückgabewert der objectCan...
–Methoden kann gesteuert werden, ob die mit dem Ereignis verbundene Aktion stattfinden soll. Für die internen Callbacks stehen folgende Ereignisse zur Verfügung: New
, Update
, Delete
, Activate
und Deactivate
.
Externe Callbacks sind keinen Klassen zugeordnet. Stattdessen werden sie beim ObjectContainer
registriert. Zusätzlich zu den bei den internen Callbacks möglichen Ereignissen stehen für diese Callbacks QueryStarted
und QueryFinished
zur Verfügung. Die Verwendung externer Callback ermöglicht eine Ereignisbehandlung ohne Abänderung der zu speichernden Klassen. Dieses kann den Vorteil haben, dass Aspekte der Persistenz nicht mit der Geschäftslogik vermischt werden müssen. Wenn der Quelltext der zu speichernden Klassen nicht zur Verfügung steht, muss mit externen Callbacks gearbeitet werden.