Thursday, 13 September 2012

Using database for objects (db4o) in android part-1

Mapping object paradigm to relational paradigm is very hectic job for programmer and intensive for processor. As mobile development move to object oriented paradigm, we need object based database where we can directly store/retrieve/update and delete objects without first converting them to relational table entities.

Fortunately we have db4o, which was released just after first release of android. db4o is database for objects. when we say "objects" this means objects are not converted to relational tables at any stage (neither by programmer nor by framework). db4o is fast, has small footprint and low processing overhead. You can read about performance comparison with other mobile databases  here.

In this post I'll explain:
  1. How to setup db4o to use in android project using eclipse. and
  2. How to perform CRUD operations on simple objects.

In order to setup db4o for your project, download latest version db4o library, extract the zip and put db4o-all.jar in your lib folder and update java build path.


Lets say we have custom class, Contact.java:

public class Contact {

    private String name;

    private String number;

    int age;

    boolean member;

    public Contact() {

        // default constructor

        name = null;

        number = null;

        age = 0;

        member = false;

    }

    public Contact(String name, String number, int age, boolean member) {

        this.name = name;

        this.number = number;

        this.age = age;

        this.member = member;

    }

    public String getNumber() {

        return number;

    }

    public void setNumber(String number) {

        this.number = number;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public boolean isMember() {

        return member;

    }

    public void setMember(boolean member) {

        this.member = member;

    }


    public void setName(String name) {

        this.name = name;

    }


    public String getName() {

        return name;

    }


    public String toString() {


        return name + "/" + number + "/" + Integer.toString(age) + "/"

                + Boolean.toString(member);

    }


}



Now lets start working with db40, first open the database:

ObjectContainer db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration(), DATABASE_PATH);



ObjectContainer is the db4o type which hold the database reference. We will use this reference to call all db4o methods for CRUD. There various options we can set using configuration, for the time being lets just use the default configuration Db4oEmbedded.newConfiguration().

Once database is open, we can store, retrieve update and delete objects:
  • Inserting objects:
    • store(Object o) method of ObjectContainer is used to insert new objects in database. e.g
           
 Contact con= new Contact("sohail", "111", 10, true);
 db.store(con);
 db.commit();
         

  • Querying objects:
    • There are two methods we can use to retrieve stored objects, these are: 
      • queryByExample(Object o) and
      • native query
In this tutorial, I'll use queryByExample which is simple and understand  for  beginners.

queryByExample takes and object and retrieve object/s similar to object supplied in arguments. In order to get all objects, we need to pass a dummy object whose all fields are set to null/0.

For example to get all objects where age=10,

 

Contact c=new Contact(null,null,10,true);

ObjectSet list=db.queryByExample(c);

this will return a list of all objects where age=10.

To fetch all objects of type Contact:

Contact t= new Contact();

ObjectSet list= db.queryByExample(t);



this will return all the objects of type Contact, Note that default constructor initializing all the fields to null and zeros. This is necessary to fetch all object. ObjectSet is db4o type which can be think of list of objects.


  • Updating objects:
    • In order to update an object, we need to first query that object and then we can update it using store method. e.g to update object where name= sohail
 

Object q=new Object();

q.setName("sohail);

ObjectSet result= db.queryByExample(q);



if(result.hasNext())

{

    Contact c= (Contact) result.next();

    //update age,number

    c.setAge(33);

   c.setNumber("444444444"); 

  db.store(c);



}

 

what we are doing here is to find the object where name=sohail, if found (result.hasNext) then update it.

  • Deleting Objects:
    • like updating, in order to delete objects we need to first query that object and bring it into memory, then we can use delete() method to delete.e.g deleting object where contact name=sohail
 

Contact n=new Contact();

n.setName("sohail");

ObjectSet result= db.queryByExample(n);



if(result.hasNext())

{

     Contact d=(Contact) result.next(); 

     db.delete(d);

}


Thats all. QueryByExample is simple however it has some limitations, we will discuss native queries in next tutorial which use the semantic of programming language, give more control and are recommended.


Full source:
package sohail.aziz.db4oexample;
import java.util.ArrayList;
import android.util.Log;
import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;

public class DbHelperQBE {

    String dbpath;
    ObjectContainer db;

    boolean OpenDb(String name) {

        if (name != null) {

            db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration(), name);

            return true;

        }

        return false;

    }


    void CloseDb() {

        db.close();

    }

    void emptyDb(){
   
        ObjectSet result= db.queryByExample(new Object());
    
        while(result.hasNext()){

               db.delete(result.next());   

        }
     

    }


    void StoreContact(Contact con) {

        db.store(con);

        db.commit();

        Log.d("sohail", "object stored");

    }

    Contact getContactByName(String name) {

       // define

        Contact obj = new Contact();

        obj.setName(name);

        ObjectSet result = db.queryByExample(obj);


        if (result.hasNext()) {

            return (Contact) result.next();

        }

        return null;

    }

    Contact getContact(Contact con) {

        ObjectSet result = db.queryByExample(con);


        if (result.hasNext()) {

            return (Contact) result.next();

        } else

            return null;

    }


    ArrayList<Contact> getAllContacts() {


        ArrayList<Contact> list = new ArrayList<Contact>();

        Contact proto = new Contact();

        ObjectSet<Contact> result = db.queryByExample(proto);


        while (result.hasNext()) {

            list.add(result.next());

        }

        return list;

    }

    boolean updateObject(Contact ObjTo, Contact ObjFrom) {



        Contact found = null;

        ObjectSet<Contact> result = db.queryByExample(ObjTo);

        if (result.hasNext()) { // if found

            found = result.next();

            found.setAge(ObjFrom.getAge()); // shallow copy just replay to, to From.

            found.setMember(ObjFrom.isMember());

            found.setName(ObjFrom.getName());

            found.setNumber(ObjFrom.getNumber());

            db.store(found);

            db.commit();

            return true;

        }

        return false;

    }

    boolean deleteObject(Contact p) {

        Contact found = null;
        ObjectSet<Contact> result = db.queryByExample(p);

        if (result.hasNext()) {

            found = result.next();
            db.delete(found);

            return true;

        } else {

            return false;

        }

    }

}


8 comments:

  1. Great tutorial.

    Question: In the source code you have openDB and closeDB. Where do you call these in context to the rest of the application? I read that those two methods are processor heavy and for an app on a tablet or phone we cannot afford to open and close for every operation we do. Thanks!

    ReplyDelete
    Replies
    1. It all depends on your application logic, if you app does not call db operations too frequently then there is no harm in opening and closing db in every insert/query operation. Otherwise you can open db once on application start and close it when app terminate.

      Delete
  2. Hey, Thanks for this.

    I'm getting to grips with android, where should the "name" when opening the db point to? is there a specific place this should be in the android framework?

    ReplyDelete
  3. This is database path, can be internal to package files e.g /data/data/packagename/db.db4o or at sd card. Look http://www.sohailaziz.com/2013/01/db4o-concurrent-access.html, db full path defined there.

    ReplyDelete
  4. can you please post the full source code? and In my app, i am getting data from JSON which includes images,paragraphs etc, then i want if user has opened my app once and loaded the text and images, then he/she does not have to use internet for loading the same images and text. Then i thought, i should use Db4o database (best among other dbs). please help me bro.

    ReplyDelete
  5. Please check source link above.

    ReplyDelete
  6. java.lang.RuntimeException: Unable to start activity ComponentInfo{com.db4o.db4oexample/com.db4o.db4oexample.MainActivity}: com.db4o.ext.Db4oIOException: /MyExampleDatabase: open failed: EROFS (Read-only file system)

    What does this mean friend, Now what should I do.

    ReplyDelete
    Replies
    1. Gunaseelan A,

      you cannot write to the root directory.
      This is how you define your database path
      private static final String DATABASE_NAME = "MYDB.db4o";
      private static final int DATABASE_MODE = 0;
      private String dbPath;
      then initialize it:
      dbPath = ctx.getDir("data", DATABASE_MODE) + "/" + DATABASE_NAME;
      db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration(), dbPath);
      That way the db4o file goes to your application's data folder

      Delete