Best Practice
Storing Hidden Encapsulated Objects
In some cases, it can be necessary to store modified encapsulated objects that cannot be accessed from your code.
public class ForeignObject
{
...
private HiddenObject hidden;
...
}
In the upper code snippet, the "hidden" object cannot be accessed by store(myForeignObject.hidden) if no getter is available.
To allow such hidden objects to be stored after they have been modified, you have two options:
-
Set the global storing strategy of the EclipseStore instance to eager storing or
-
Implement and set a custom
PersistenceEagerStoringFieldEvaluatorfor this field.
EmbeddedStorageManager storage = EmbeddedStorage.Foundation()
.onConnectionFoundation(
f -> f.setReferenceFieldEagerEvaluator(
new CustomEagerStoringFieldEvaluator()
)
)
.start();
Prefer Composition Over Inheritance
When designing your data model, prefer composition over inheritance, especially when working with collection types.
EclipseStore provides specialized type handlers for commonly used JDK types such as ArrayList, HashMap, HashSet, and others.
These type handlers are designed to work with the exact types they are registered for.
If you create a subclass of such a type, the specialized type handler will not be used.
Instead, EclipseStore falls back to generic type analysis, which may lead to unexpected behavior, degraded performance, or serialization issues.
|
Extending collection types (e.g., |
Don’t do this:
// Bad: extending a collection type
public class CustomerList extends ArrayList<Customer>
{
private String name;
// ...
}
Do this instead:
// Good: using composition
public class CustomerList
{
private final List<Customer> customers = new ArrayList<>();
private String name;
// ...
}
By using composition, ArrayList retains its own specialized type handler, while your custom class is handled generically through its fields.
This pattern also results in a cleaner, more flexible design that is easier to maintain and evolve.
Use Immutable data models
To increase performance, use immutable sub-graphs as often as possible.
Storing those with the provided convenience storing methods or using a thread-local storer to insert those sub-graphs concurrently can give a great performance boost.
Get objects that are persisted by a storer
Sometimes, it can be useful to get all objects and/or their assigned storage ID that are persisted by a store operation.
To do so, you can register a custom PersistenceObjectRegistrationListener implementation to a BinaryStorer to collect all objects registered by that storer instance.
The default storer will call the onObjectRegistration method for each object registered to be stored during the store phase. Implementers should be aware that this has an impact on the storer’s performance.
public static class PersistenceObjectRegistrationListenerImpl implements PersistenceObjectRegistrationListener {
private Hashtable<Long, Object> persistenceObjects = new Hashtable<Long, Object>();
@Override
public void onObjectRegistration(long objectID, Object object) {
this.persistenceObjects.put(objectID, object);
}
public void clear() {
this.persistenceObjects.clear();
}
public Hashtable<Long, Object> get() {
return this.persistenceObjects;
}
}
//create a storer
BinaryStorer storer = (Default) storage.createStorer();
//register PersistenceObjectRegistrationListenerImpl implementation
PersistenceObjectRegistrationListenerImpl registrationListener = new PersistenceObjectRegistrationListenerImpl();
storer.registerRegistrationListener(registrationListener);
//store some data
storer.store(...);
storer.commit();
//get and process persisted objects
registrationListener.get().forEach(...);
//clean up
registrationListener.clear();