Skip to content
This repository has been archived by the owner on Nov 11, 2020. It is now read-only.

Examples

ashleyfrieze edited this page Sep 2, 2014 · 13 revisions

Basic Persistence

To persist something you need a POJO that contains its data in a form that Jackson can map it to JSON. It is possible to use other ways of converting the data, but it is expected that you'll use JSON and Jackson in most cases.

The POJO needs to have a field marked with the Key annotation and needs a getter and setter for that field.

You can either provide the key field when you save the object or can provide a key generator.

Pojo

Example of a simple POJO with a key field:

public class IntegerKeyedDocument {
    @Key
    private int id;

    private String name;

    public int getId() {
            return id;
    }

    public void setId(int id) {
            this.id = id;
    }

    public String getName() {
            return name;
    }

    public void setName(String name) {
            this.name = name;
    }
} 

Engine

To persist this, you need an instance of the Engine class for which EngineImpl is the key implementation. The engine needs a storage manager for which there's StorageManagerInMemory for use if you're doing testing or running an in-memory only database for some reason. Most commonly you would use `StorageManagerFileSystem" which will persist every POJO into a directory of your choice in subdirectories according to the type of object it is storing.

// Construct an engine to store files in "tempTestDb" directory
Engine engine = new EngineImpl(new StorageManagerFileSystem("tempTestDb/"));

Store

Storing an object is very straightforward. Providing it has either a value in its key field or a null key and a key generator specified in the @Key attribute, then you simply can call store or storeNew

// Save an object
Pojo someObject = new Pojo( ... ); // construct your object
engine.store(someObject);          // save the object (assumes singleton instance of the engine)

storeNew is the same as store, but will throw an exception if the object already exists.

For objects with generated keys, the store method keeps calling storeNew with new keys until it stores a unique one.

Retrieve

Retrieving an object requires the key field:

// retrieves an object of type ExampleDocument with key "myKey"
ExampleDocument found = engine.find("myKey", ExampleDocument.class);

Deleting

To delete an object provide its key to the engine

// delete object with key "toDelete"
engine.delete("toDelete", SomeClass.class);

All Keys

To find all the keys for a particular type:

// retrieve all keys - note you get a set of Strings, this is the internal representation
// not sorted, not converted into the original value of a key
Set<String> allKeys = engine.findAllKeys(SomeClass.class);

You can sort the keys into order by their string contents if this is useful - in reality it might be more useful to order the objects by some other criteria. However, for cases where the keys have meaning as string you can specify your own comparator or use one of the ones provided.

// retrieve keys in ascending string order
List<String> allKeysSorted = engine.findAllKeys(SomeClass.class, new StringAscending());

Queries

Queries return a ListWithKeys object, which can be used as a list of keys with the getKey(int index) function, or can be used as though all the objects from the datasource are in memory within the list. In actual fact, the items are loaded just in time as you iterate through the list and you get a fresh instance of the object every time you use a list get method.

Note: the list that is returned can be used as a front end to the database for adding and removing objects. Use it carefully. While the list starts life as a filter of the objects you originally extracted, it becomes a view of that data that you're modifying with changes happening live. List changes are applied to the engine, but object changes are not - you need to re-save an object for its changes to be persisted.

// retrieve all items
List<MyItem> allItems = engine.findAll(MyItem.class); // actually returns ListWithKeys which extends List

The Predicate class is used to filter items for inclusion in the target set. The Mapping class can be used to extract from an item into something else which can be used for sorting, perhaps a single value or some custom pojo. This leads to the most complex find method which allows you to specify the predicate for whether an item is extracted, the mapping for how that item will be seen by the sort algorithm and then the comparator used in the sort.

// retrieve all items and sort by the numeric key field
ListWithKeys<IntegerKeyedPojo> = engine.findAll(IntegerKeyedPojo.class, 
    new SomePredicate(),      // custom class to decide if an IntegerKeyedPojo is to be included
    new SomeMapping(),        // in this case a class than implements Mapping<IntegerKeyedPojo, Integer>
    new IntegerAscending());  // would implement Comparator<Integer> in this instance

See the unit tests for some examples of this.

Calculations

A transformation can be performed like a query. It would normally be used to find the max or sum of a type of field, for which there are default implementations. A transformation is run according to the following pseudo code:

for each item that passes through the filter defined in predicate
    if this is the first item
        build the transformation from the item 
    else
        aggregate to the current transformation with the item
end

Example of a max transformation:

Integer max = engine.createTransformation(IntegerKeyedDocument.class,  
    new All<IntegerKeyedDocument>(),                        // all is a predicate which returns all
    new IntegerMax<IntegerKeyedDocument>(getIntegerKeyedDocumentKey()));   // IntegerMax needs a mapping to integer from the custom type
Clone this wiki locally