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:
+ 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
+ asynchronous storage allows the main application thread to continue its work, while blobs are being stored
- it is more difficult to move objects between db4o database files
+ 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:
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02
package com.db4odoc.f1.blobs; 03
04
05
public 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:
01/* Copyright (C) 2004 - 2006 db4objects Inc. http://www.db4o.com */ 02
03
package com.db4odoc.f1.blobs; 04
05
import java.io.File; 06
07
import com.db4o.ext.Status; 08
import com.db4o.types.Blob; 09
10
11
public 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:
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
}
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:
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:
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.