Objekt-orienterte databaser (del2): Db4objects


fredag 27. februar 2009 Databaser db4o

Del 1 i denne serien finner du her.

De første objekt-orienterte databasene kom ut av forskningsmiljøene tidlig på 70-tallet, men begrepet ble ikke tatt i bruk før 1985. På 90-tallet dukket det opp mange, komersielle løsninger, hvor objektdatabaser bl.a. ble integrert i programmeringsspråk for persistering av objekter. C++ var lenge størst på dette området.

Rundt 2004 fikk objektdatabaser en ny vekst gjennom diverse open source-prosjekter. Det finnes i dag et hav av løsninger under diverse lisenser og for diverse programmeringspråk.

Objektdatabasene har hatt liten effekt på mainstream, kommersiell dataprosessering, og mange utviklere vet derfor lite om dem. Men ODBMS er mye brukt innenfor enkelte spesialfelt, som f.eks. telekommunikasjon, fysikk og molekylærbiologi. Det er verdt å merke seg at verdens største database er en objektdatabase: Stanford Linear Accelerator Center's ODBMS var den første databasen over 1000 terabytes. Objektdatabaser er også attraktive for embedded devices og innefor real-time systemer.

Den objektdatabase-teknologien jeg har valgt å eksperimentere med heter db4o (database for objects). Db4o er en objektdatabase både for Java og .NET, og er gratis for ikke-kommerisell bruk. Produktet fikk prisen Best of Open Source Developer Tools i 2008, og har et stort community. Det finnes allerede et hav av open source sideprosjekter som implementerer støtteverktøy og lignende ting - intergasjon til Castle, Spring.NET og CSLA, støtte for bruk i Ruby, JRuby, Eiffel.NET, Scala og PHP, Ant tasks, Silverlight-støtte, ASP.NET providers osv. Dokumentasjonen er relativt bra, og teknologien er i produksjon mange steder: bl.a. sitter det en db4o-database i hjertet av Spanias nye kontrollsenter for høyhastighetståg.

Databasespørringer i db4o

I del 1 viste jeg hvor enkelt det er å lagre objekter i objektdatabasen. Oppdatering av objekter er like enkelt - man bruker den samme Store() metoden faktisk - den legger inn nye objekter og oppdaterer eksisterende objekter. Når det gjelder å hente ut data har db4o flere muligheter.

For det første kan man bruke QueryByExample (QBE). Dette er noe f.eks. NHibernate også støtter. Man oppretter et nytt objekt av den typen man skal hente ut - ofte kalt en prototype - og setter properties som man er interessert i å matche på. Vil man f.eks. hente ut alle Personer som heter "Torbjørn", oppretter man en person som heter nettopp Torbjørn og sender det til QBE metoden:

public IEnumerable<Person> FindAllTorbjorns()

{

    using (IObjectContainer db = Db4oFactory.OpenFile("foo.db"))

    {

        Person prototype = new Person

        {

            GivenName = "Torbjørn"

        };

        return db.QueryByExample(prototype).Cast<Person>();

    }

}

QBE kan selvsagt ikke brukes til alt, men det er overraskende kraftig, og godt nok i mange tilfeller.

Den neste muligheten i db4o er det de kaller native queries. Her bruker man elementer i programmeringspåket man jobber i til å gjøre spørringer mot objektene. Man bruker typisk delegater / anonyme metoder til å definere kriterier for hvilke objekter som skal returneres. En variant av native queries i .NET er db4o's støtte for LINQ. Jeg er veldig glad i LINQ, og synes det er den mest naturlige måten å jobbe med databasen på. Skal jeg f.eks. hente ut alle personer som har enter en shipping-adresse eller en e-post adresse (ja, jeg har utvidet domenemodellene min litt nå), så kan jeg gjøre det slik:

public IList<Person> FindPersonsWeCanContact()

{

    using (IObjectContainer db = Db4oFactory.OpenFile("foo.db"))

    {

        var query = from Person p in db

                    where p.ShippingAddress != null

                       || p.Email != null

                    select p;

        return query.ToList();

    }

}

Gjennom LINQ kan jeg uttrykke akkurat hva jeg er ute etter. Jeg kan jobbe med databasen som om det var en avansert kolleksjon i minnet, og få tilbake mine egne domeneobjekter uten å måtte gjøre noen form for mapping. Å jobbe med persistert data har aldri vært enklere!

Det finnes selvsagt noen detaljer jeg har utelatt. F.eks. vil ikke db4o returnere den komplette objektstrukturen (Person + alle underobjekter) uten at jeg eksplisitt ber den om det. Db4o kan derimot konfigureres til f.eks. å bruke eager fetching / load all levels hver gang jeg henter ut personobjekter - eller jeg kan definere nøyakig hvor mange nivåer som skal lastes. Samme teknikk må man bruke når man oppdaterer og sletter - fortelle db4o hvor mange nivåer som skal påvirkes.

Det finnes også en siste måte å gjøre spørringer, og det er via db4o's SODA Query API. Dette er den mest kraftige måten å gjøre spørringer på, men også mer low level, og krever at man setter seg inn i API'et. Foreløpig har jeg ikke hatt behov for det - LINQ har gitt meg alt jeg trenger til nå, og er den måten dokumentasjonen anbefaler å bruke - men om du vil lære mer har db4o documentert SODA ganske bra også.

Dette er andre del av en artikkelserie om objekt-orienterte databaser, basert på mitt NNUG-foredrag om samme tema. Fortsettelsen følger..

Knagger: ,


comments powered by Disqus