db4o does not deliver a field auto increment feature, which
is common in RDBMS. Normally you don't need any additional ids, since db4o manages objects by object-identity. However cases where you have disconnected objects, you need additional ids. One of then possibilities it to use auto incremented ids.
If your application logic requires this feature you can implement it using external callbacks. One of the possible solutions is presented below. Note that this example only works in embedded-mode.
This example assumes that all object which need an auto incremented id are subclasses of the IDHolder class. This class contains the auto-incremented id.
private int id; public int getId() { return id; } public void setId(int id) { this.id = id; }
First create a class which keeps the state of the auto-increment numbers. For example a map which keeps the latest auto incremented id for each class.
private static class PersistedAutoIncrements { private final Map<Class, Integer> currentHighestIds = new HashMap<Class, Integer>(); public int nextNumber(Class forClass) { Integer number = currentHighestIds.get(forClass); if (null == number) { number = 0; } number += 1; currentHighestIds.put(forClass,number); return number; } }
Then create two methods, which are called later. One which returns the next auto-incremented id for a certain class. Another which stores the current state of the auto-increments.
public synchronized int getNextID(Class forClass) { PersistedAutoIncrements incrementState = ensureLoadedIncrements(); return incrementState.nextNumber(forClass); } public synchronized void storeState(){ if(null!=state){ container.ext().store(state,2); } }
The last part is to ensure that the existing auto-increments are loaded from the database. Or if not existing a new instance is created.
private PersistedAutoIncrements ensureLoadedIncrements() { if(null==state){ state = loadOrCreateState(); } return state; } private PersistedAutoIncrements loadOrCreateState() { ObjectSet<PersistedAutoIncrements> existingState = container.query(PersistedAutoIncrements.class); if(0==existingState.size()){ return new PersistedAutoIncrements(); } else if(1==existingState.size()){ return existingState.get(0); } else{ throw new IllegalStateException("Cannot have more than one state stored in database"); } }
Now it's time to use the callbacks. Every time when a new object is created, assign a new id. For this the creating-event is perfect. When commiting also make the auto increment-state persistent, to ensure that no id is used twice.
final AutoIncrement increment = new AutoIncrement(container); EventRegistry eventRegistry = EventRegistryFactory.forObjectContainer(container); eventRegistry.creating().addListener(new EventListener4<CancellableObjectEventArgs>() { public void onEvent(Event4<CancellableObjectEventArgs> event4, CancellableObjectEventArgs objectArgs) { if(objectArgs.object() instanceof IDHolder){ IDHolder idHolder = (IDHolder) objectArgs.object(); idHolder.setId(increment.getNextID(idHolder.getClass())); } } }); eventRegistry.committing().addListener(new EventListener4<CommitEventArgs>() { public void onEvent(Event4<CommitEventArgs> commitEventArgsEvent4, CommitEventArgs commitEventArgs) { increment.storeState(); } });
Last, don't forget to index the id-field. Otherwise looks-ups will be slow.
configuration.common().objectClass(IDHolder.class).objectField("id").indexed(true);