For a custom typehandler example we will try to write a very simple typehandler for StringBuffer(java) and StringBuilder(.NET). These classes are basically just another representation of String, so we can look at the StringHandler implementation in db4o source.
To keep it simple we will skip information required for indexing - please look at IndexableTypeHandler in db4o sources to get more information on how to handle indexes.
The first thing should be #write method, which determines how the object is persisted:
1public void write(WriteContext context, Object obj) { 2
String str = ((StringBuffer)obj).toString(); 3
WriteBuffer buffer = context; 4
buffer.writeInt(str.length()); 5
writeToBuffer(buffer, str); 6
}
1private static void writeToBuffer(WriteBuffer buffer, String str){ 2
final int length = str.length(); 3
char[] chars = new char[length]; 4
str.getChars(0, length, chars, 0); 5
for (int i = 0; i < length; i ++){ 6
buffer.writeByte((byte) (chars[i] & 0xff)); 7
buffer.writeByte((byte) (chars[i] >> 8)); 8
} 9
}
As you can see from the code above, there are 3 steps:
Next step is to read the same from the buffer. It is just opposite to the write method:
1public Object read(ReadContext context) { 2
ReadBuffer buffer = context; 3
String str = ""; 4
int length = buffer.readInt(); 5
if (length > 0) { 6
str = readBuffer(buffer, length); 7
} 8
return new StringBuffer(str); 9
}
1private static String readBuffer(ReadBuffer buffer, int length){ 2
char[] chars = new char[length]; 3
for(int ii = 0; ii < length; ii++){ 4
chars[ii] = (char)((buffer.readByte() & 0xff) | ((buffer.readByte() & 0xff) << 8)); 5
} 6
return new String(chars, 0, length); 7
}
Delete is simple - we just reposition the buffer offset to the end of the slot:
1public void delete(DeleteContext context) { 2
context.readSlot(); 3
}
Try to experiment with the #delete method by implementing cascade on delete. Use FirstClassObjectHandler as an example.
We are done with the read/write operations. But as you remember, in order to read an object, we must find it through a query, and that's where we will need a #compare method (well, you do not need it if your query does not contain any comparison criteria, but this is normally not the case):
1public PreparedComparison prepareComparison(Context ctx, final Object obj) { 2
return new PreparedComparison() { 3
public int compareTo(Object target) { 4
return compare((StringBuffer)obj, (StringBuffer)target); 5
} 6
}; 7
}
01private final int compare(StringBuffer a_compare, StringBuffer a_with) { 02
if (a_compare == null) { 03
if (a_with == null) { 04
return 0; 05
} 06
return -1; 07
} 08
if (a_with == null) { 09
return 1; 10
} 11
char c_compare[] = new char[a_compare.length()]; 12
a_compare.getChars(0, a_compare.length() - 1, c_compare, 0); 13
char c_with[] = new char[a_with.length()]; 14
a_with.getChars(0, a_with.length() - 1, c_with, 0); 15
16
return compareChars(c_compare, c_with); 17
}
1private static final int compareChars(char[] compare, char[] with) { 2
int min = compare.length < with.length ? compare.length : with.length; 3
for (int i = 0; i < min; i++) { 4
if (compare[i] != with[i]) { 5
return compare[i] - with[i]; 6
} 7
} 8
return compare.length - with.length; 9
}
The last method left: #defragment. This one only moves the offset to the beginning of the object data, i.e. skips Id and size information (to be compatible to older versions):
1public void defragment(DefragmentContext context) { 2
// To stay compatible with the old marshaller family 3
// In the marshaller family 0 number 8 represented 4
// length required to store ID and object length information 5
context.incrementOffset(8); 6
}
This Typehandler implementation can be tested with a class below. Please, pay special attention to #configure method, which adds StringBufferHandler/StringBuilderHandler to the database configuration:
001package com.db4odoc.typehandler; 002
003
import java.io.File; 004
import java.io.IOException; 005
006
import com.db4o.Db4o; 007
import com.db4o.ObjectContainer; 008
import com.db4o.ObjectSet; 009
import com.db4o.config.Configuration; 010
import com.db4o.defragment.Defragment; 011
import com.db4o.ext.DatabaseFileLockedException; 012
import com.db4o.query.Query; 013
import com.db4o.reflect.ReflectClass; 014
import com.db4o.reflect.generic.GenericReflector; 015
import com.db4o.reflect.jdk.JdkReflector; 016
import com.db4o.typehandlers.TypeHandlerPredicate; 017
018
public class TypehandlerExample { 019
020
private final static String DB4O_FILE_NAME = "reference.db4o"; 021
private static ObjectContainer _container = null; 022
023
024
public static void main(String[] args) throws IOException { 025
testReadWriteDelete(); 026
//testDefrag(); 027
testCompare(); 028
} 029
// end main 030
031
private static Configuration configure() { 032
Configuration configuration = Db4o.newConfiguration(); 033
// add a custom typehandler support 034
035
TypeHandlerPredicate predicate = new TypeHandlerPredicate() { 036
public boolean match(ReflectClass classReflector, int version) { 037
GenericReflector reflector = new GenericReflector( 038
null, new JdkReflector(Thread.currentThread().getContextClassLoader())); 039
ReflectClass claxx = reflector.forName(StringBuffer.class.getName()); 040
boolean res = claxx.equals(classReflector); 041
return res; 042
} 043
}; 044
045
configuration.registerTypeHandler(predicate, new StringBufferHandler()); 046
return configuration; 047
} 048
// end configure 049
050
051
private static void testReadWriteDelete(){ 052
storeCar(); 053
// Does it still work after close? 054
retrieveCar(); 055
// Does deletion work? 056
deleteCar(); 057
retrieveCar(); 058
} 059
// end testReadWriteDelete 060
061
private static void retrieveCar() { 062
ObjectContainer container = database(configure()); 063
if (container != null){ 064
try { 065
ObjectSet result = container.query(Car.class); 066
Car car = null; 067
if (result.hasNext()){ 068
car = (Car)result.next(); 069
} 070
System.out.println("Retrieved: " + car); 071
} finally { 072
closeDatabase(); 073
} 074
} 075
} 076
// end retrieveCar 077
078
private static void deleteCar() { 079
ObjectContainer container = database(configure()); 080
if (container != null){ 081
try { 082
ObjectSet result = container.query(Car.class); 083
Car car = null; 084
if (result.hasNext()){ 085
car = (Car)result.next(); 086
} 087
container.delete(car); 088
System.out.println("Deleted: " + car); 089
} finally { 090
closeDatabase(); 091
} 092
} 093
} 094
// end deleteCar 095
096
private static void storeCar() { 097
new File(DB4O_FILE_NAME).delete(); 098
ObjectContainer container = database(configure()); 099
if (container != null){ 100
try { 101
Car car = new Car("BMW"); 102
container.store(car); 103
car = (Car)container.query(Car.class).next(); 104
System.out.println("Stored: " + car); 105
106
} finally { 107
closeDatabase(); 108
} 109
} 110
} 111
// end storeCar 112
113
private static void testCompare() { 114
new File(DB4O_FILE_NAME).delete(); 115
ObjectContainer container = database(configure()); 116
if (container != null){ 117
try { 118
Car car = new Car("BMW"); 119
container.store(car); 120
car = new Car("Ferrari"); 121
container.store(car); 122
car = new Car("Mercedes"); 123
container.store(car); 124
Query query = container.query(); 125
query.constrain(Car.class); 126
query.descend("model").orderAscending(); 127
ObjectSet result = query.execute(); 128
listResult(result); 129
130
} finally { 131
closeDatabase(); 132
} 133
} 134
} 135
// end testCompare 136
137
public static void testDefrag() throws IOException{ 138
new File(DB4O_FILE_NAME + ".backup").delete(); 139
storeCar(); 140
Defragment.defrag(DB4O_FILE_NAME); 141
retrieveCar(); 142
} 143
// end testDefrag 144
145
private static ObjectContainer database(Configuration configuration) { 146
if (_container == null) { 147
try { 148
_container = Db4o.openFile(configuration, DB4O_FILE_NAME); 149
} catch (DatabaseFileLockedException ex) { 150
System.out.println(ex.getMessage()); 151
} 152
} 153
return _container; 154
} 155
// end database 156
157
private static void closeDatabase() { 158
if (_container != null) { 159
_container.close(); 160
_container = null; 161
} 162
} 163
// end closeDatabase 164
165
166
private static void listResult(ObjectSet result) { 167
System.out.println(result.size()); 168
while(result.hasNext()) { 169
System.out.println(result.next()); 170
} 171
} 172
// end listResult 173
174
}