-
Notifications
You must be signed in to change notification settings - Fork 4
Propoid db
Working with Sqlite is very easy with propoid-db, create a repository and start right away without any setup:
Repository repository = new Repositiory(context, "foos");
All required tables will automagically be created (or altered in case of new properties) once you perform operations on the repository.
Propoid supports many features commonly expected by an object-relational mapper:
- arbitrary property types (provided adequate Mappers are registered)
- inheritance
- lazy loading of referenced propoids
- transactions
- optional object identity
- typed queries
- live query results backed by a database cursor
To insert a new object just pass it to the Repository:
repository.insert(new Foo());
An object can similarly be updated:
repository.update(foo);
Deletion is simple too:
repository.delete(foo);
Objects can be queried with a fully typed API:
Match<Foo> match = repository.query(Where.equal(new Foo().bar, "BAR"));
Note that a database operation is performed only after you invoke a method on the Match, e.g.:
Foo foo = match.first(Order.ascending(new Foo().bar));
All query methods:
-
first()
get the first match (if any) -
single()
get the single expected match (ornull
) -
count()
get count of matched Propoids -
max(Property<T>)
get the max value of a property -
min(Property<T>)
get the min value of a property -
sum(Property<T>)
get the sum of values of a property -
avg(Property<T>)
get the average value of a property -
list(Order...)
get all matches, optionally ordered by properties -
list(Range, Order...)
get a range of matches, optionally ordered by properties
The list()
methods are special in that their elements are backed by a life database cursor. To close its resources you have to cast its result to java.io.Closable
and call close()
on it. Even simpler you can just consume all its elements, e.g.:
for (Foo foo : foos) {
}
// the list is consumed now
Additionally you can perform bulk modifications on the matched Propoids:
-
set(Property<T>, T)
set a property -
delete()
delete all matched Propoids
A Reference is used to refer to a specific Propoid instance and can be persisted or passed between activities:
intent.setData(new Reference<Foo>(foo).toUri());
You can use references for intent filters too:
<intent-filter>
<action android:name="android.intent.action.EDIT"/>
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="propoid"
android:host="your.package.Foo"
/>
</intent-filter>
The propoid can then be looked up in your activitiy:
Foo foo = repository.lookup(Reference.fromUri(intent.getData()));
To increase performance of your application you might want to add indices to your repository. For example the following creates a non-unique index on Foo
by ascending bar
:
Foo foo = new Foo();
repository.index(foo, false, Order.ascending(foo.bar));
A Locator specifies how the SQLiteDatabase is openened:
- InMemoryLocator opens a database in memory
- FileLocator (the default) opens a database file, either speficied explicitely or relative to the application context
FileLocator allows initializing the database content on creation:
new FileLocator(context, "foos") {
protected void initial(File file) {
copy(context.getAssets().open("default-foos"), new FileOutputStream(file));
}
};
Each repository is highly configurable through a set of settings, e.g.:
Repository repository = new Repository(context, "foos", new FoosMapping(), new FoosFactory());
Mapping provides Mappers which are used to map properties to database columns. DefaultMapping handles most common types:
- primitives and their wrapper classes
- Strings
- byte arrays
java.util.Locale
java.util.Date
android.location.Location
- enums
- types (i.e. class instances)
- relations to propoids
Naming decides which table to be used for each Propoid and how to encode subclasses. DefaultNaming uses a single-table-mapping for inheritance.
All Propoids read from the database are created by a factory. DefaultFactory creates a new instance on each requested Propoid.
Before Propoids are inserted, updated or deleted, this strategy is allowed to cascade operations to referenced Propoids. DefaultCascading allows registering of properties, which should be cascaded.
On opening the database this strategy allows to perform required upgrades.
DefaultVersioning keeps a list of Upgrades, each being able to upgrade a database to the subsequent version:
DefaultVersioning versioning = new DefaultVersioning();
versioning.add(new SQLExec(new SQL("alter table foo drop column bar"))); // update to version 1
versioning.add(new SQLExec(new SQL("drop table foo"))); // update to version 2
The current database's version is tracked so that upgrades are applied automagically.
A repository can be derived with custom settings as needed.
In the following example we want the repository to keep object identify for already loaded objects, thus we derive with an IdentityFactory passing in the old objects:
Factory factory = new IdentityFactory(oldFoos, repository.factory);
List<Foo> newFoos = repository.derive(factory).query(new Foo()).list();
Note that all objects already present in oldFoos
are now reused for the returned match.
A repository can easily run as a service. Just register RepositoryService or a subclass in your application manifest and then bind to a RepositoryConnection in your activities or services:
new RepositoryConnection() {
public void onConnected(Repository repository) {
// ... work with repository
}
}.bind(context, RepositoryService.class);
A repository can be used for search suggestions too: Register a subclass of RepositorySuggest in your application manifest:
public class FooRepositorySuggest extends RespositorySuggest<Foo> {
protected Match<Foo> query(Repository repository, String query) {
return repository.query(Where.like(new Foo().bar, "%" + query + "%"));
}
// what text should be used for displaying search results
protected String getText1(Foo foo);
return foo.bar.get()
}
}
Then implement your search activity:
public void onCreate(Bundle state) {
super.onCreate(state);
if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) {
String query = RepositorySuggest.getQuery(getIntent());
} else if (Intent.ACTION_VIEW.equals(getIntent().getAction())) {
Reference<Foo> reference = RepositorySuggest.getReference(getIntent());
} else {
// other action
}
}
To keep things simple propoid-db is missing some features to be a full-blown OR mapper:
- query results can not be grouped
- only one aggregate function can be applied on each query
- no fetch joins
- one-to-many relations are not supported