DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. Databases
  4. Real Android apps leveraging db4o persistence engine (Part 1)

Real Android apps leveraging db4o persistence engine (Part 1)

German Viscuso user avatar by
German Viscuso
·
Jul. 19, 10 · Interview
Like (0)
Save
Tweet
Share
11.38K Views

Join the DZone community and get the full member experience.

Join For Free

This the first delivery in a series of articles targeted at showing developers how db4o (an open source database that leverages today's object-oriented languages, systems, and mindset) is being used in several Android projects to avoid all the pitfalls and hassles of object-relational mapping while benefiting from an elegant and straight forward way to evolve a domain model which, in the end, translates into faster, easier upgrades for users.

There are many benefits to using an object database like db4o, including easier code maintenance, and the ability to create applications based on more complex data models. Unlike in rigid, predefined SQL tables, you can store dynamic, free-form data, which can be changed or amended any time. In addition, db4o allows for data replication, another missing element in Android's software stack.

Let's take a look at the code in these projects to learn how developers leverage object database technology on their apps and also use the opportunity to introduce key concepts about db4o. Let's start with project DyCaPo.

DyCaPo stands for “Dynamic Car Pooling”, a system that facilitates the ability of drivers and passengers to make one-time ride matches close to their departure time, with sufficient convenience and flexibility to be used on a daily basis. The project is the result of research activities on the adoption of a FREE/OPEN Dynamic Carpooling system in the province of Trento, Italy.

Riccardo Buttarelli, chose db4o as the persistence engine in the client application for the DyCaPo Service running on Android OS (aka dycadroid). If you check dycadroid's db4o configuration:

private static Configuration configure(){

	dbConfiguration = Db4o.newConfiguration();

	dbConfiguration.objectClass(Trip.class).objectField(Trip.ID).indexed(true);
	dbConfiguration.objectClass(Trip.class).cascadeOnUpdate(true);
	dbConfiguration.objectClass(Trip.class).cascadeOnDelete(true);
	dbConfiguration.objectClass(Location.class).objectField(Location.GEORSSPNT).indexed(true);
	dbConfiguration.objectClass(Location.class).cascadeOnDelete(true);
	dbConfiguration.objectClass(Location.class).cascadeOnUpdate(true);
	dbConfiguration.objectClass(Person.class).objectField(Person.USERNAME).indexed(true);
	dbConfiguration.objectClass(Person.class).cascadeOnUpdate(true);
	dbConfiguration.objectClass(Person.class).cascadeOnDelete(true);
	//[...]
	dbConfiguration.objectClass(ActiveTrip.class).objectField(ActiveTrip.ID).indexed(true);
	dbConfiguration.objectClass(ActiveTrip.class).cascadeOnUpdate(true);
	dbConfiguration.objectClass(ActiveTrip.class).cascadeOnDelete(true);
	dbConfiguration.objectClass(Route.class).cascadeOnUpdate(true);
	dbConfiguration.objectClass(Route.class).cascadeOnDelete(true);
	dbConfiguration.lockDatabaseFile(false);
	dbConfiguration.messageLevel(2);

	return dbConfiguration;
}
you'll see that he does a heavy use of cascading on updates and deletes in the db4o configuration for several classes. When you use cascading in db4o you can save a lot of time because you're basically telling the database to apply an operation on your object considering all the objects that are referenced from it (object tree). For example, when you delete an ActiveTrip object you'll probably want to also delete all the Route objects that are referenced from it. In order to achieve this functionality automatically you’ll just have to enable cascaded deletes for that class:
dbConfiguration.objectClass(ActiveTrip.class).cascadeOnDelete(true);
and forget about managing deletion of referenced objects!

Cascaded deletes can be applied to all members of an object or can be limited to specific fields.

On the other hand, if you changed an ActiveTrip object tree you also probably want to reflect the changes on the associated Routes automatically (that are referenced from the ActiveTrip object). It's quite easy to achieve that by enabling cascaded updates on the class

dbConfiguration.objectClass(ActiveTrip.class).cascadeOnUpdate(true);
this way changed Route objects will also be updated on the database when you update an ActiveTrip and you won't have to traverse all the referenced objects to look for updated data (let db4o handle that!)

As you might have guessed already the trade-off of a cascading-heavy configuration is database performance: if you're dealing with deep object structures you might want to exercise a more granular control during updates (ie. manually set the update depth). By default db4o uses an update depth of 1 meaning that only primitives values in the object will be updated (not the changes in referenced objects). You can set the update depth to a fixed value of your choice so stores() can go as deep as you want or you can just use cascaded updates all the object tree will be traversed looking for changes.

If you want full automation on database operation you can try the Transparent Persistance framework. Basically this is a way to make your object fully database-aware. You store the object once in the database. After that, all changes you make on the object are reflected in the database ‘by magic’. You can implement this manually or use byte-code-enhancers.

If you take a closer look at the configuration code above you’ll also notice that some fields in specific classes are configured as “indexed”:
dbConfiguration.objectClass(ActiveTrip.class).objectField(ActiveTrip.ID).indexed(true);
Indexing works like in the relational world and will give you a faster access when you're querying over that field. db4o automatically uses indexes for queries if they are present. Lots of complaints about slow queries have to do with the omission of indexes in the configuration.

We saw some tips related to updates and deletes but what about fetching objects? The most important concept of object retrieval is the concept of “activation” and has many similarities with updates and deletes in terms of cascading. Suppose I want to load an ActiveTrip from the database, shall I also get all the associated Routes in the same operation? What if I just need to know the value of an int field in the ActiveTrip but Routes are irrelevant to me for this operation?
db4o allows you to handle how deep in the object tree you want to go when loading an object (aka “activation depth”). Same as before, via the configuration you can opt to activate everything when you fetch the parent object via cascading:
dbConfiguration.objectClass(ActiveTrip.class).cascadeOnActivate(true);
or if you want more control you can go with a manual activation (ObjectContainer#activate(object, depth);) or let db4o handle everything via the transparent activation framework which activates object trees from the database on demand (ie. as object members are accessed).

Right in the end of the configuration code you’ll find the line:
dbConfiguration.lockDatabaseFile(false);
which is listed as our number one dangerous practice! During normal operation, and if you don't use the configuration option above, db4o locks the database file to prevent concurrent access which could leave you with a corrupted database. If you find yourself struggling with constant DatabaseFileLockedExceptions that's a clear sign that you need to change your database access pattern: if you need multiple concurrent transactions you can try db4o’s embedded client server mode of operation.

So, we've seen the db4o configuration options in dycadroid but how difficult are the standard upsert, delete, query operations? Actually extremely easy!

The beauty of db4o is that you don't have to do any kind of processing to your object in order to make it persistent (ie. no mapping). You just pass the full object as a parameter to the store() method of a db4o’s object container and voila it's saved (and also the sibling objects depending on your configuration). The store() operation acts as an insert if the passed object is new (ie. was never stored in the db during the current transaction) or as an update if the object was previously saved during the same session (hence we could call it an ”upsert” operation):
public static void saveActiveTrip (ActiveTrip trip){
	Log.d(TAG, "saving ActiveTrip from trip");
        ObjectContainer db = DBProvider.getDatabase();
        //[...]
        db.store(trip);
        db.commit();
}
Note the commit() in the last line above. As any decent transactional database db4o supports commit and rollback operations during transactions. Transaction semantics is implicit to match db4o's simplicity. In the code above a transaction is started when the object container is opened (eg. openFile() inside getDatabase()) and ends when the object container is closed (db.close()). Along the same lines of simplicity a close() operation will force a commit().

 

Important: after a close() operation in an object container all the objects that where saved to db4o will be seen as new objects when you open a new object container (for more information see db4o's unique identity concept).

 Similarly when you want to delete an object from the database you also interact with a db4o object container but this time it's delete() that you must use. Same rules apply with regards to object identity: in order for delete to work you must have stored or fetched the object passed as parameter during the same transaction otherwise db4o won’t be able to tell that the object belongs to the database and the operation will be ineffective. The delete operation applies to the passed object only and does not delete the siblings except if you configured cascaded deletes (see above).

How about querying? Querying with db4o can be as easy as issuing one line of code. You have the choice of 3 different query mechanisms:

  1. Native Queries: refactorable query interface that is very close to the language. The queries are optimized and converted to SODA queries behind the curtains. In db4o for .NET a LINQ provider is also provided and is the preferred method for querying. Since Dalvik (Android VM) uses it’s own binary format db4o’s native query optimizer won’t work during runtime. If you want to optimize NQs on Android you’ll have to use db4o’s build time native query enhancer.
  2. SODA Queries: low level and powerful graph based query system where you basically build a query tree and pass it to db4o for execution.
  3. Query by Example: useful for simple queries where you pass a prototype object that will serve as an example to find similar objects. Uses reflection to analyze the passed prototype object and build the query.

Here's an example (also from dycadroid) that shows a delete plus a simple query using “query by example”:

public static boolean deletePrefs(){
	try{
		ObjectContainer db = DBProvider.getDatabase();
		ObjectSet prefs = db.queryByExample(new Preferences());
		db.delete(prefs);
		db.commit();
		return true;
        }catch (Exception e){
		return false;
        }
}
Note that in the example query by example is used to retrieve all instances of class Preferences:
db.queryByExample(new Preferences());
but you could also pass a prototypical instance with some fields filled in to further constraint the query:
db.queryByExample(new Preferences("trip", null, null));
which will retrieve all objects of class Preferences where the type is ”trip” and won't pay attention to the remaining values in the constructor (will match anything there).

Well, I hope you have enjoyed db4o's simplicity by taking a look at parts of a real project. On the next article in the series we'll take a look at QuiteSleep another Android app that is available in the Android Market that leverages db4o for all persistence needs.
Db4o Threading Relational database Android (robot) app Persistence (computer science) Engine

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Implementing PEG in Java
  • How We Solved an OOM Issue in TiDB with GOMEMLIMIT
  • Getting a Private SSL Certificate Free of Cost
  • 19 Most Common OpenSSL Commands for 2023

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: