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 - 2007 db4objects Inc. http://www.db4o.com */ 02namespace Db4objects.Db4odoc.Blobs 03{ 04 public class Car 05 { 06 string _model; 07 CarImage _img; 08 09 public Car(string model) 10 { 11 _model = model; 12 _img=new CarImage(); 13 _img.FileName = _model+".jpg"; 14 } 15 16 public CarImage CarImage 17 { 18 get 19 { 20 return _img; 21 } 22 } 23 24 override public string ToString() 25 { 26 return string.Format("{0}({1})", _model, _img.FileName); 27 } 28 } 29}

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

CarImage is a wrapper to BLOB, representing the photo:

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

CarImage.vb
01' Copyright (C) 2004 - 2007 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
01private static void StoreCars() 02 { 03 File.Delete(Db4oFileName); 04 IObjectContainer db = Db4oFactory.OpenFile(Db4oFileName); 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
01Private Shared Sub StoreCars() 02 File.Delete(Db4oFileName) 03 Dim db As IObjectContainer = Db4oFactory.OpenFile(Db4oFileName) 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
01private 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
1Private 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
01private static void RetrieveCars() 02 { 03 IObjectContainer db = Db4oFactory.OpenFile(Db4oFileName); 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
01Private Shared Sub RetrieveCars() 02 Dim db As IObjectContainer = Db4oFactory.OpenFile(Db4oFileName) 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.