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. 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.

Historie 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.

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.

Lizenz 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.

Systemeigenschaften und Objektmanagement Neben dem kleinen Speicherbedarf für die Programmbibliothek ist der Bedarf an Arbeitsspeicher zur Laufzeit ebenfalls gering und überschreitet normalerweise 1 MB nicht. db4o kann nicht von außen administriert werden. Sämtliche Einstellungen müssen über den Programmcode vorgenommen werden. Die Datenbank kann in gemischten Umgebungen eingesetzt werden: Beispielsweise können zwei in C# oder Java programmierte Client-Anwendungen gemeinsam auf eine Datenbank zugreifen, die von einer in Java oder C# programmierten Server-Anwendung betrieben wird. Mit Hilfe des Alias-Features von db4o können dabei Klassennamen umbenannt werden, was beispielsweise wegen der unterschiedlichen Benennungskonventionen notwendig ist. Eine der wichtigsten Vorteile von db4o: Objekte können gespeichert werden, ohne dass hierfür besondere Vorkehrungen getroffen werden müssen. Es ist weder notwendig, die zu speichernden Klassen von speziellen Oberklassen abzuleiten, noch müssen spezielle Schnittstellen implementiert werden. Auch eine Anreicherung des Bytecodes, wie sie beispielsweise die Aspektorientierte Programmierung verwendet, ist nicht nötig. Die Objekte können dabei beliebige Typen enthalten und zudem beliebig tief verschachtelt sein. Mit dieser Eigenschaft ist es möglich, Persistenz mit wenig Aufwand umzusetzen.

Grundlegende API 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();

Ähnlich zu EJB / JPA (dort 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. Nachteil: Die Daten sind also nicht komplett unabhängig von der Klasse der Programmiersprache. Das Format ist proprietär und kann gegebenenfalls von nachfolgenden Programmiersprachen nicht mehr so einfach gelesen werden. Vorteil: Diese kann sehr leicht zwischen Firmen ausgetauscht werden. Der Datenzugriff ist sehr schnell.

Danach werden alle CRUD-Operationen (Create, Read, Update und Delete) ausgeführt: CREATE Mit 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. READ Es folgt eine einfache Suche mit der einfachsten Abfragesprache Query by Example. Eine Personen-Instanz wird als Muster erstellt und beliebige Felder werden gesetzt. Zurückgegeben werden alle Objekte in der Datenbank, bei denen die Felder passen. found enthält daher das vollständige Objekt, das oben gespeichert wurde. UPDATE Dieses 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. DELETE db.delete(object) löscht ein Objekt, das dem Objektmanager bekannt sein muss.

Abfragesprachen 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 (QBE) 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: Kein Feld gesetzt, so dass alle Felder null sind, oder es wird direkt eine Referenz auf die Klasse übergeben (z. B. Person.class): In diesem Fall werden alle in der Datenbank gespeicherten Instanzen der Klasse zurückgeliefert. Es werden beliebig viele Felder gesetzt: Dann werden alle gespeicherten Objekte zurückgeliefert, bei denen die im Muster gefüllten Felder mit dem Wert im Objekt übereinstimmen. Es wird null übergeben: Dieses liefert alle Objekte aus der Datenbank zurück.

Komplexe Abfragekriterien lassen sich bei Verwendung dieser Art der Abfrage nicht ausdrücken.

SODA / Criteria Queries 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 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.

Vorteile Der Programmierer muss keine spezielle Abfragesprache, wie z. B. SQL, lernen. Die Abfragen verwenden die Elemente der Programmiersprache und sind daher typsicher. Bei der Umgestaltung (Refactoring) des Programms können die Abfragen berücksichtigt werden. 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.

Nachteile Die Erstellung der Abfrage ist komplett in der Hand der Entwickler und nicht unter Kontrolle von Administratoren, die beispielsweise bei Verwendung von Stored Procedures Sicherheitsaspekte der Datenbank angemessen berücksichtigen können. Native Abfragen sind nicht so mächtig wie entsprechende Konstrukte in speziellen Abfragesprachen wie HQL oder JPQL. In diesen Sprachen können besondere Abfragekriterien, wie beispielsweise Verdichtung, explizit ausgedrückt werden.

Beispiele 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;

Transaktionen 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: Durch das proprietäre Datenformat ist der Zugriff für einen Thread zwar meistens schneller als andere Kombinationen aus relationaler Datenbank und objektrelationalem Mapping.