Db4o Blob Implementation

Built-in db4o Blob type helps you to get rid of the problems of byte[] array, though it has its own drawbacks. Pros and Cons for the points, mentioned above:

  1. every Blob gets it's own file
  2. + main database file stays a lot smaller

    + backups are possible over individual files

    + the BLOBs are accessible without db4o

    - multiple files need to be managed

  3. C/S communication runs asynchronous in separate thread
  4. + asynchronous storage allows the main application thread to continue its work, while blobs are being stored

  5. special code is necessary to store and load
  6. - it is more difficult to move objects between db4o database files

  7. no concerns about activation depth
  8. + big objects won't be loaded into memory as part of the activation process

Let's look, how it works.

First, BLOB storage should be defined:

c#: Db4o.Configure().SetBlobPath(storageFolder);

VB: Db4o.Configure().SetBlobPath(storageFolder);

where storageFolder is a String value representing local or server path to store BLOBs. If that value is not defined, db4o will use the default folder "blobs" in user directory.

We will use a modified Car class, which holds reference to the car photo:

Car.cs
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02 03using System; 04 05namespace Db4objects.Db4odoc.Blobs 06{ 07 public class Car 08 { 09 string _model; 10 CarImage _img; 11 12 public Car(string model) 13 { 14 _model = model; 15 _img=new CarImage(); 16 _img.FileName = _model+".jpg"; 17 } 18 19 public CarImage CarImage 20 { 21 get 22 { 23 return _img; 24 } 25 } 26 27 override public string ToString() 28 { 29 return string.Format("{0}({1})", _model, _img.FileName); 30 } 31 } 32}

Car.vb
01' Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com 02 03Imports System 04 05Namespace Db4objects.Db4odoc.Blobs 06 Public Class Car 07 Dim _model As String 08 Dim _img As CarImage 09 10 Public Sub New(ByVal model As String) 11 _model = model 12 _img = New CarImage() 13 _img.FileName = _model + ".jpg" 14 End Sub 15 16 Public ReadOnly Property CarImage() As CarImage 17 Get 18 Return _img 19 End Get 20 End Property 21 22 Public Overrides Function ToString() As String 23 Return String.Format("{0}({1})", _model, _img.FileName) 24 End Function 25 End Class 26End Namespace

CarImage is a wrapper to BLOB, representing the photo:

CarImage.cs
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02 03namespace Db4objects.Db4odoc.Blobs 04{ 05 06 using Db4objects.Db4o.Ext; 07 using Db4objects.Db4o.Types; 08 using Sharpen.Lang; 09 using Sharpen.IO; 10 11 public class CarImage { 12 IBlob _blob = null; 13 private string _file = null; 14 private string inFolder = "blobs\\in\\"; 15 private string outFolder = "blobs\\out\\"; 16 17 public CarImage() { 18 19 } 20 21 public string FileName 22 { 23 get 24 { 25 return _file; 26 } 27 28 set 29 { 30 _file = value; 31 } 32 } 33 34 public bool ReadFile() 35 { 36 _blob.ReadFrom(new File(inFolder + _file)); 37 double status = _blob.GetStatus(); 38 while(status > Status.COMPLETED){ 39 Thread.Sleep(50); 40 status = _blob.GetStatus(); 41 } 42 return (status == Status.COMPLETED); 43 } 44 45 public bool WriteFile() 46 { 47 _blob.WriteTo(new File(outFolder + _file)); 48 double status = _blob.GetStatus(); 49 while(status > Status.COMPLETED){ 50 Thread.Sleep(50); 51 status = _blob.GetStatus(); 52 } 53 return (status == Status.COMPLETED); 54 } 55 } 56}

CarImage.vb
01' Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com 02Imports System.Threading 03Imports Sharpen.IO 04Imports Db4objects.Db4o.Ext 05Imports Db4objects.Db4o.Types 06 07 08Namespace Db4objects.Db4odoc.Blobs 09 10 Public Class CarImage 11 Dim _blob As IBlob 12 Private _file As String = Nothing 13 Private inFolder As String = "blobs\in\" 14 Private outFolder As String = "blobs\out\" 15 16 Public Sub New() 17 18 End Sub 19 20 Public Property FileName() As String 21 Get 22 Return _file 23 End Get 24 Set(ByVal Value As String) 25 _file = Value 26 End Set 27 End Property 28 29 Public Function ReadFile() As Boolean 30 _blob.ReadFrom(New File(inFolder + _file)) 31 Dim s As Double = _blob.GetStatus() 32 While s > Status.COMPLETED 33 Thread.Sleep(50) 34 s = _blob.GetStatus() 35 End While 36 Return (s = Status.COMPLETED) 37 End Function 38 39 Public Function WriteFile() As Boolean 40 _blob.WriteTo(New File(outFolder + _file)) 41 Dim s As Double = _blob.GetStatus() 42 While s > Status.COMPLETED 43 Thread.Sleep(50) 44 s = _blob.GetStatus() 45 End While 46 Return (s = Status.COMPLETED) 47 End Function 48 End Class 49End Namespace

inFolder ( "blobs\in\") is used as a location of existing files, which are to be stored into db4o, and outFolder ( "blobs\out\") will be the place for images, retrieved from the database.

readFile method allows blob to be read from the specified location into db4o storage:

c#: IBlob.ReadFrom( File )

VB: IBlob.ReadFrom( File )

As reading is done in a dedicated thread, you can use Blob#getStatus() in a loop to create a progress window.

The same applies to the write operation, which copies BLOB, stored with db4o, to the specified filesystem location.

Let's store some cars together with their images in our database:

BlobExample.cs: storeCars
01public static void StoreCars() 02 { 03 File.Delete(YapFileName); 04 IObjectContainer db = Db4oFactory.OpenFile(YapFileName); 05 try 06 { 07 Car car1=new Car("Ferrari"); 08 db.Set(car1); 09 StoreImage(car1); 10 Car car2=new Car("BMW"); 11 db.Set(car2); 12 StoreImage(car2); 13 } 14 finally 15 { 16 db.Close(); 17 } 18 }

BlobExample.vb: storeCars
01Public Shared Sub StoreCars() 02 File.Delete(YapFileName) 03 Dim db As IObjectContainer = Db4oFactory.OpenFile(YapFileName) 04 Try 05 Dim car1 As Car = New Car("Ferrari") 06 db.Set(car1) 07 StoreImage(car1) 08 Dim car2 As Car = New Car("BMW") 09 db.Set(car2) 10 StoreImage(car2) 11 Finally 12 db.Close() 13 End Try 14 End Sub

BlobExample.cs: storeImage
01public static void StoreImage(Car car) 02 { 03 CarImage img = car.CarImage; 04 try 05 { 06 img.ReadFile(); 07 } 08 catch (Exception ex) 09 { 10 Console.WriteLine(ex.Message); 11 } 12 }

BlobExample.vb: storeImage
1Public Shared Sub StoreImage(ByVal car As Car) 2 Dim img As CarImage = car.CarImage 3 Try 4 img.ReadFile() 5 Catch ex As Exception 6 Console.WriteLine(ex.Message) 7 End Try 8 End Sub

CarImage is stored in the database just like normal object, no BLOB data is transferred before explicit call (Blob#readFrom in CarImage#readFile method), which copies the images to the storageFolder.

Please, note, that CarImage reference should be set to the database before uploading actual data. To get the images back to the filesystem we can run a usual query:

BlobExample.cs: retrieveCars
01public static void RetrieveCars() 02 { 03 IObjectContainer db = Db4oFactory.OpenFile(YapFileName); 04 try 05 { 06 IQuery query = db.Query(); 07 query.Constrain(typeof(Car)); 08 IObjectSet result = query.Execute(); 09 GetImages(result); 10 } 11 finally 12 { 13 db.Close(); 14 } 15 }

BlobExample.vb: retrieveCars
01Public Shared Sub RetrieveCars() 02 Dim db As IObjectContainer = Db4oFactory.OpenFile(YapFileName) 03 Try 04 Dim query As IQuery = db.Query() 05 query.Constrain(GetType(Car)) 06 Dim result As IObjectSet = query.Execute() 07 GetImages(result) 08 Finally 09 db.Close() 10 End Try 11 End Sub

and get BLOB data using retrieved Car references:

BlobExample.cs: getImages
01private static void GetImages(IObjectSet result) 02 { 03 while(result.HasNext()) 04 { 05 Car car = (Car)(result.Next()); 06 Console.WriteLine(car); 07 CarImage img = car.CarImage; 08 try 09 { 10 img.WriteFile(); 11 } 12 catch (Exception ex) 13 { 14 Console.WriteLine(ex.Message); 15 } 16 } 17 }

BlobExample.vb: getImages
01Private Shared Sub GetImages(ByVal result As IObjectSet) 02 While result.HasNext() 03 Dim car As Car = CType((result.Next()), Car) 04 Console.WriteLine(car) 05 Dim img As CarImage = car.CarImage 06 Try 07 img.WriteFile() 08 Catch ex As Exception 09 Console.WriteLine(ex.Message) 10 End Try 11 End While 12 End Sub

Retrieved images are placed in CarImage.outFolder ("blobs\out").

So query interface operates on references - no BLOB data is loaded into memory until explicit call (Blob#writeTo). This also means, that activationDepth does not affect Blob objects and best querying performance is achieved without additional coding.