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:

Java: 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.java
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02package com.db4odoc.f1.blobs; 03 04 05public class Car { 06 private String model; 07 private CarImage img; 08 09 10 public Car(String model) { 11 this.model=model; 12 img=new CarImage(); 13 img.setFile(model+".jpg"); 14 } 15 16 public CarImage getImage() { 17 return img; 18 } 19 20 public String toString() { 21 return model +"(" + img.getFile() + ")" ; 22 } 23}

CarImage is a wrapper to BLOB, representing the photo:

CarImage.java
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02 03package com.db4odoc.f1.blobs; 04 05import java.io.File; 06 07import com.db4o.ext.Status; 08import com.db4o.types.Blob; 09 10 11public class CarImage { 12 Blob blob; 13 private String fileName = null; 14 private String inFolder = "blobs\\in\\"; 15 private String outFolder = "blobs\\out\\"; 16 17 public CarImage() { 18 19 } 20 21 public void setFile(String fileName){ 22 this.fileName = fileName; 23 } 24 25 public String getFile(){ 26 return fileName; 27 } 28 29 public boolean readFile() throws java.io.IOException { 30 blob.readFrom(new File(inFolder + fileName)); 31 double status = blob.getStatus(); 32 while(status > Status.COMPLETED){ 33 try { 34 Thread.sleep(50); 35 status = blob.getStatus(); 36 } catch (InterruptedException ex){ 37 System.out.println(ex.getMessage()); 38 } 39 } 40 return (status == Status.COMPLETED); 41 } 42 43 public boolean writeFile() throws java.io.IOException { 44 blob.writeTo(new File(outFolder + fileName)); 45 double status = blob.getStatus(); 46 while(status > Status.COMPLETED){ 47 try { 48 Thread.sleep(50); 49 status = blob.getStatus(); 50 } catch (InterruptedException ex){ 51 System.out.println(ex.getMessage()); 52 } 53 } 54 return (status == Status.COMPLETED); 55 } 56}

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:

Java: Blob.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.java: storeCars
01public static void storeCars() { 02 new File(YAPFILENAME).delete(); 03 ObjectContainer db=Db4o.openFile(YAPFILENAME); 04 try { 05 Car car1=new Car("Ferrari"); 06 db.set(car1); 07 storeImage(car1); 08 Car car2=new Car("BMW"); 09 db.set(car2); 10 storeImage(car2); 11 } finally { 12 db.close(); 13 } 14 }

BlobExample.java: storeImage
1public static void storeImage(Car car) { 2 CarImage img = car.getImage(); 3 try { 4 img.readFile(); 5 } catch (java.io.IOException ex) { 6 System.out.println(ex.getMessage()); 7 } 8 }

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.java: retrieveCars
01public static void retrieveCars() { 02 ObjectContainer db=Db4o.openFile(YAPFILENAME); 03 try { 04 Query query = db.query(); 05 query.constrain(Car.class); 06 ObjectSet result = query.execute(); 07 getImages(result); 08 } finally { 09 db.close(); 10 } 11 }

and get BLOB data using retrieved Car references:

BlobExample.java: getImages
01private static void getImages(ObjectSet result){ 02 while(result.hasNext()) { 03 Car car = (Car)(result.next()); 04 System.out.println(car); 05 CarImage img = car.getImage(); 06 try { 07 img.writeFile(); 08 } catch (java.io.IOException ex){ 09 System.out.print(ex.getMessage()); 10 } 11 } 12 }

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.