Data Access
The Data Access essentially is UBStores persistence layer – data items can be be saved and retrieved from here. Every UBStore node has one data access.
The idea is to abstract away how data is actually stored. While the default implementation (MemoryDataAccess) stores data items in an in-memory list, it would be possible to build implementations that store data in relational databases, key-value stores, on the file system, the cloud, you name it.
Obtaining the data access
The easiest way to access the data access is via the
IUBStore.getDataAccess()
method.
However, since it is implemented as an OSGi service, it is
also possible to get a reference using OSGi services, be it
programmatically or by using declarative services.
Storing items
Data Items can be stored with the IDataAccess.put()
method.
It takes either a single data item or a list of data items
(bulk put). Obviously, the idea is to later be able to retrieve
stored data items exactly as they were put in.
Conditions
Data Items that are stored inside the data access need to fulfil certain conditions, or else the data access will (should) reject it and throw an Exception.
- The key must be neither null nor empty.
- The type must be neither null nor empty.
- Transaction and Validtime intervals must be left closed and right open.
- The Value must not be null.
Although the SimpleDataItem
, UBStores default implementation
of IDataItem
prevents items 1, 2 and 4, other implementations
might not, and custom implementations of the data access should
check for these conditions before accepting a put operation.
Creating storable data items
While it is perfectly possible to create data items that meet the aforementioned conditions, it can be a tedious and self-repeating process, especially if generating multiple data items at once.
The UBStore helper bundle
contains a factory that helps creating storable data items
easily. Have a look at the Build.dataItem()
method.
Retrieving items
The IDataAccess.get()
method is the counterpart to the put()
methods: it is used to retrieve stored items.
The get()
method takes exactly one data item as a parameter,
the so-called query item. While the query comes in the same
object as the data, the semantics are a little bit different.
Each of the fields (key, type, metadata and value) are
understood as query, not as an exact value:
- The key is interpreted as a regular expression (Java flavor) and should match all stored data items whose key match that regex.
- The same goes for the type.
- While the metadata keys have to match exactly (no regexes here), the metadata values are interpreted as regexes.
- Valid and transaction time intervals match data items whose valid and transaction time overlap the queries intervals.
- You cannot query for values. The data access will ignore the query items value when retrieving results. The reasoning is that the whole point of querying the data access is to retrieve values.
Note that none of the query item fields (except the value) may be null.
Example
Query:
Key: .*
Type: car
Metadata: color -> (red|blue)
Valid Time: unbounded
Transaction Time: [4,8]
Will return all data items that are of type car
and contain
a metadata entry with key color
and value red
or blue
.
The transaction time has to overlap the interval [4,8], e.g.
both data items with transaction time intervals [3,5[ and [5,10[
would match.
""
), it would not match
anything (except empty string, which is forbidden to store as a
key). We chose to strictly adhere to regex matching semantics for
consistency reasons as well as avoid forcing implementations of
IDataAccess
to check for specific corner cases.
Creating request data items
Sadly, it can be tedious to formulate queries manually using
plain data items. For example, in order to search for all data
items with a specific type, one has to set the key to .*
and
both the valid and transaction times to unbounded intervals.
To simplify formulating queries, the UBStore
helper bundle contains a
facility that helps formulating queries easily, for example
by defaulting all data item fields that you do not specify
explicitly to values that match everything.
Have a look at the Build.request()
method.
Deleting data
Deleting data is done via the IDataAccess.remove(IDataItem)
method. The parameter is a query data item, and the method deletes
all stored data items that would be returned if the same query
data item had been put into the get(IDataItem)
method.