Click here to Skip to main content
Click here to Skip to main content

Using SQLite Database with Android

By , 9 Oct 2012
 
  • Download a sample application on using database in Android from here.

Introduction

Android default Database engine is Lite. SQLite is a lightweight transactional database engine that occupies a small amount of disk storage and memory, so it's a perfect choice for creating databases on many mobile operating systems such as Android, iOS.

Things to consider when dealing with SQLite:

  1. Data type integrity is not maintained in SQLite, you can put a value of a certain data type in a column of another datatype (put string in an integer and vice versa).
  2. Referential integrity is not maintained in SQLite, there is no FOREIGN KEY constraints or JOIN statements.
  3. SQLite Full Unicode support is optional and not installed by default.

In this tutorial, we will create a simple database application to store employees data. the DB has:

Tables

  1. Employees
  2. Dept

Views

  1. ViewEmps: to display employees and their relative departments.

AndroidSQLite/sqlitedb1.jpg

Creating SQLite Database

By default, SQLite on Android does not have a management interface or an application to create and manage databases from, so we're going to create the database ourselves by code. First, we will create a class that handles all the operations required to deal with the database such as creating the database, creating tables, inserting and deleting records and so on. The first step is to create a class that inherits from SQLiteOpenHelper class. This class provides two methods to override to deal with the database:

  1. onCreate(SQLiteDatabase db): invoked when the database is created, this is where we can create tables and columns to them, create views or triggers.
  2. onUpgrade(SQLiteDatabse db, int oldVersion, int newVersion): invoked when we make a modification to the database such as altering, dropping , creating new tables.

Our class will have the following members:

public class DatabaseHelper extends SQLiteOpenHelper {

static final String dbName="demoDB";
static final String employeeTable="Employees";
static final String colID="EmployeeID";
static final String colName="EmployeeName";
static final String colAge="Age";
static final String colDept="Dept";

static final String deptTable="Dept";
static final String colDeptID="DeptID";
static final String colDeptName="DeptName";

static final String viewEmps="ViewEmps";

The Constructor

public DatabaseHelper(Context context) {
  super(context, dbName, null,33); 
  }

The constructor of the super class has the following parameters:

  • Context con: The context attached to the database
  • dataBaseName: The name of the database
  • CursorFactory: Sometimes, we may use a class that extends the Cursor class to implement some extra validations or operations on the queries run on the database. In this case, we pass an instance of CursorFactory to return a reference to our derived class to be used instead of the default cursor. In this example, we are going to use the standard Cursor Interface to retrieve results, so the CursorFactory parameter is going to be null.
  • Version: the version of the schema of the database. The constructor creates a new blank database with the specified name and version.

Creating the Database

The first superclass method to override is onCreate(SQLiteDatabase db):

public void onCreate(SQLiteDatabase db) {
  // TODO Auto-generated method stub
  
  db.execSQL("CREATE TABLE "+deptTable+" ("+colDeptID+ " INTEGER PRIMARY KEY , "+
    colDeptName+ " TEXT)");
  
  db.execSQL("CREATE TABLE "+employeeTable+" 
    ("+colID+" INTEGER PRIMARY KEY AUTOINCREMENT, "+
        colName+" TEXT, "+colAge+" Integer, "+colDept+" 
    INTEGER NOT NULL ,FOREIGN KEY ("+colDept+") REFERENCES 
    "+deptTable+" ("+colDeptID+"));");
  
  
  db.execSQL("CREATE TRIGGER fk_empdept_deptid " +
    " BEFORE INSERT "+
    " ON "+employeeTable+
    
    " FOR EACH ROW BEGIN"+
    " SELECT CASE WHEN ((SELECT "+colDeptID+" FROM "+deptTable+" 
    WHERE "+colDeptID+"=new."+colDept+" ) IS NULL)"+
    " THEN RAISE (ABORT,'Foreign Key Violation') END;"+
    "  END;");
  
  db.execSQL("CREATE VIEW "+viewEmps+
    " AS SELECT "+employeeTable+"."+colID+" AS _id,"+
    " "+employeeTable+"."+colName+","+
    " "+employeeTable+"."+colAge+","+
    " "+deptTable+"."+colDeptName+""+
    " FROM "+employeeTable+" JOIN "+deptTable+
    " ON "+employeeTable+"."+colDept+" ="+deptTable+"."+colDeptID
    );
  //Inserts pre-defined departments
  InsertDepts(db);  
 }

The method creates tables with columns, a view and a trigger. The method is invoked when the database is created. So we create our table and specify the columns. This method is invoked when the database does not exist on the disk, it’s executed only once on the same device the first time the application is run on the device.

Upgrading the Database

Sometimes, we want to upgrade the database by changing the schema, add new tables or change column data types. This is done by overriding the onUpdate(SQLiteDatabase db,int old Version,int newVerison) method:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  // TODO Auto-generated method stub
  
  db.execSQL("DROP TABLE IF EXISTS "+employeeTable);
  db.execSQL("DROP TABLE IF EXISTS "+deptTable);
  
  db.execSQL("DROP TRIGGER IF EXISTS dept_id_trigger");
  db.execSQL("DROP TRIGGER IF EXISTS dept_id_trigger22");
  db.execSQL("DROP TRIGGER IF EXISTS fk_empdept_deptid");
  db.execSQL("DROP VIEW IF EXISTS "+viewEmps);
  onCreate(db);
 }

This method is invoked when the version number specified in the constructor of the class changes.

When you want to append a change to your database, you must change the version number in the constructor of the class.

So when you pass the constructor a version number of 2:

public DatabaseHelper(Context context) {
  super(context, dbName, null,2);
  
  // TODO Auto-generated constructor stub
 }

instead of 1:

super(context, dbName, null,2);

the application understands that you want to upgrade the database and onUpgrade method will be invoked. A typical implementation of this method is to drop the tables and create them again with the additional modifications.

Managing Foreign-Key Constraints

We mentioned before that SQLite 3 by default does not support foreign key constraint, however we can force such a constraint using TRIGGERS: we will create a trigger that ensures that when a new Employee is inserted, his/her Dept value is present in the original Dept table. The SQL statement to create such a trigger would be like this:

CREATE TRIGGER fk_empdept_deptid Before INSERT ON Employees 
FOR EACH ROW BEGIN
    SELECT CASE WHEN ((SELECT DeptID FROM Dept WHERE DeptID =new.Dept ) IS NULL)
    THEN RAISE (ABORT,'Foreign Key Violation') END;
    END

In onCreate method, we created this trigger like this:

db.execSQL("CREATE TRIGGER fk_empdept_deptid " +
    " BEFORE INSERT "+
    " ON "+employeeTable+
    
    " FOR EACH ROW BEGIN"+
    " SELECT CASE WHEN ((SELECT "+colDeptID+" FROM "+deptTable+" _
    WHERE "+colDeptID+"=new."+colDept+" ) IS NULL)"+
    " THEN RAISE (ABORT,'Foreign Key Violation') END;"+
    "  END;");

 UPDATE: 

you can force referential integrity by enabling foreign keys in your database by overriding onOpen method like this:

@Override
public void onOpen(SQLiteDatabase db) {
		super.onOpen(db);
		if (!db.isReadOnly()) {
// Enable foreign key constraints
db.execSQL("PRAGMA foreign_keys=ON;");
}
	} 

Executing SQL Statements

Now let's begin executing basic SQL statements. You can execute any SQL statement that is not a query whether it is insert, delete, update or anything using db.execSQL(String statement) method like when we did when creating the database tables:

db.execSQL("CREATE TABLE "+deptTable+" ("+colDeptID+ " INTEGER PRIMARY KEY , "+
    colDeptName+ " TEXT)");

Inserting Records

We insert records to the database using the following code for example to insert records in the Dept table:

SQLiteDatabase db=this.getWritableDatabase();
 ContentValues cv=new ContentValues();
   cv.put(colDeptID, 1);
   cv.put(colDeptName, "Sales");
   db.insert(deptTable, colDeptID, cv);

   cv.put(colDeptID, 2);
   cv.put(colDeptName, "IT");
   db.insert(deptTable, colDeptID, cv);
                     db.close();

Notice that we need to call this.getWritableDatabase() to open the connection with the database for reading/writing. The ContentValues.put has two parameters: Column Name and the value to be inserted. Also, it is a good practice to close the database after executing statements.

Updating Values

To execute an update statement, we have two ways:

  1. To execute db.execSQL
  2. To execute db.update method:
public int UpdateEmp(Employee emp)
  {
   SQLiteDatabase db=this.getWritableDatabase();
   ContentValues cv=new ContentValues();
   cv.put(colName, emp.getName());
   cv.put(colAge, emp.getAge());
   cv.put(colDept, emp.getDept());
   return db.update(employeeTable, cv, colID+"=?", 
    new String []{String.valueOf(emp.getID())});   
  }

The update method has the following parameters:

  1. String Table: The table to update a value in
  2. ContentValues cv: The content values object that has the new values
  3. String where clause: The WHERE clause to specify which record to update
  4. String[] args: The arguments of the WHERE clause

Deleting Rows

As in update to execute a delete statement, we have two ways:

  1. To execute db.execSQL
  2. To execute db.delete method:
public void DeleteEmp(Employee emp)
  {
   SQLiteDatabase db=this.getWritableDatabase();
   db.delete(employeeTable,colID+"=?", new String [] {String.valueOf(emp.getID())});
   db.close();
  }

The delete method has the same parameters as the update method.

Executing Queries

To execute queries, there are two methods:

  1. Execute db.rawQuery method
  2. Execute db.query method

To execute a raw query to retrieve all departments:

Cursor getAllDepts()
  {
   SQLiteDatabase db=this.getReadableDatabase();
   Cursor cur=db.rawQuery("SELECT "+colDeptID+" as _id, 
	"+colDeptName+" from "+deptTable,new String [] {});
   
   return cur;
  }

The rawQuery method has two parameters:

  1. String query: The select statement
  2. String[] selection args: The arguments if a WHERE clause is included in the select statement

Notes

  1. The result of a query is returned in Cursor object.
  2. In a select statement if the primary key column (the id column) of the table has a name other than _id, then you have to use an alias in the form SELECT [Column Name] as _id cause the Cursor object always expects that the primary key column has the name _id or it will throw an exception .

Another way to perform a query is to use a db.query method. A query to select all employees in a certain department from a view would be like this:

public Cursor getEmpByDept(String Dept)
  {
   SQLiteDatabase db=this.getReadableDatabase();
   String [] columns=new String[]{"_id",colName,colAge,colDeptName};
   Cursor c=db.query(viewEmps, columns, colDeptName+"=?", 
	new String[]{Dept}, null, null, null);
   return c;
  }

The db.query has the following parameters:

  1. String Table Name: The name of the table to run the query against
  2. String [ ] columns: The projection of the query, i.e., the columns to retrieve
  3. String WHERE clause: where clause, if none pass null
  4. String [ ] selection args: The parameters of the WHERE clause
  5. String Group by: A string specifying group by clause
  6. String Having: A string specifying HAVING clause
  7. String Order By by: A string Order By by clause

Managing Cursors

Result sets of queries are returned in Cursor objects. There are some common methods that you will use with cursors:

  1. boolean moveToNext(): moves the cursor by one record in the result set, returns false if moved past the last row in the result set.
  2. boolean moveToFirst(): moves the cursor to the first row in the result set, returns false if the result set is empty.
  3. boolean moveToPosition(int position): moves the cursor to a certain row index within the boolean result set, returns false if the position is un-reachable
  4. boolean moveToPrevious(): moves the cursor to the previous row in the result set, returns false if the cursor is past the first row.
  5. boolean moveToLast(): moves the cursor to the lase row in the result set, returns false if the result set is empty.

There are also some useful methods to check the position of a cursor: boolean isAfterLast(), isBeforeFirst, isFirst, isLast and isNull(columnIndex). Also if you have a result set of only one row and you need to retrieve values of certain columns, you can do it like this:

public int GetDeptID(String Dept)
  {
   SQLiteDatabase db=this.getReadableDatabase();
   Cursor c=db.query(deptTable, new String[]{colDeptID+" as _id",colDeptName},
	colDeptName+"=?", new String[]{Dept}, null, null, null);
   //Cursor c=db.rawQuery("SELECT "+colDeptID+" as _id FROM "+deptTable+" 
   //WHERE "+colDeptName+"=?", new String []{Dept});
   c.moveToFirst();
   return c.getInt(c.getColumnIndex("_id"));  
  }

We have Cursor.getColumnIndex(String ColumnName) to get the index of a column. Then to get the value of a certain column, we have Cursor.getInt(int ColumnIndex) method.

Also there are getShort, getString, getDouble, getBlob to return the value as a byte array. It's a good practice to close() the cursor after using it.

Download a sample application on using database in Android from here.

More tutorials can be found on my blog at http://android-pro.blogspot.com.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mina Samy
Software Developer Symbyo Technologies
Egypt Egypt
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questioninsert values in database through sqlite (what wrong in it???)membersushant singh19 Apr '13 - 9:37 
package com.example.logindb;
 
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import static com.example.logindb.DBase.*;
 
public class MainActivity extends Activity {
	SQLiteDatabase mydb;
	EditText edtName,edtPassword;
	Button btnSubmit,btnSignup;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mydb=new DBase(getBaseContext(), null, null, 0).getWritableDatabase();
		initializer();
	}
 
	private void initializer() {
		edtName=(EditText)findViewById(R.id.editText1);
		edtPassword=(EditText)findViewById(R.id.editText2);
		btnSubmit=(Button)findViewById(R.id.button1);
		btnSignup=(Button)findViewById(R.id.button2);
		
		btnSignup.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				ContentValues cv=new ContentValues();
				
				cv.put(USERNAME, edtName.getText().toString());
				cv.put(PASSWORD, edtPassword.getText().toString());
				mydb.insert(USER_TABLE, null, cv);
				mydb.close();
			}
		});
	}
 
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}
 
}
<pre lang="java">
 

//ANOTHER CLASS FILE IS FOLLOWING
 

package com.example.logindb;
 
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
 
public class DBase extends SQLiteOpenHelper{
public static String USERNAME;
public static String PASSWORD;
public static int Id=1;
public static String USER_TABLE;
SQLiteDatabase db;
private final static String DB_NAME="User.sqlite";
private final static int DB_VERSION=1;
 
public DBase(Context context, String name, CursorFactory factory,
int version) {
super(context, DB_NAME, factory, DB_VERSION);

}
 
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+USER_TABLE+"("+Id+" integer primary key autoincrement,"+USERNAME+" text,"+PASSWORD+" text"+");");
}
 
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub

}
 
}

Question[My vote of 1] It would have been an excellente article, if you didn't throw the source code into sh*t.memberLuca Crisi, MCP23 Mar '13 - 2:10 
You ruined it letting the source code be undownloadable. I'm not going to download any sh***y exes from any site like the one you used to store your... RAR!! couldn't you use a more standard ZIP, instead? Pity, because the article was really well presented. Just a VERY BAD CHOICE on storing the sources. It seems you don't want to share it. That's the only reason I gave rated your article only 1 instead of 5. Why didn't you store your source on CodeProject, in a ZIPped format, instead?
Luca Crisi, MCP

QuestionAnother simple tutorial with source codememberArise Rayamangalam20 Mar '13 - 7:25 
a problem with this type of method is if you want to add data say 100 nos then your code look shabby and it will be very difficult tot enter the data. You can use another method by which we can enter the data using a Database Manager and later attach it to the Application. A simple tutorial with source code is given in the following link
http://android-helper4u.blogspot.com/2013/03/d-databse-and-spinner-tutorial.html

Questionspenner popup too small ?memberJomateix18 Feb '13 - 23:43 
Hi,
 
Thank you very much for this tutorial this has 2 tables wich is rare in a SQL database tutorial,
 
First of all its a great tutorial,i have a little comment about it. I am testing it in a 4.04 Tablet and a 4.04 phone, on any spinner to select the Department it popups a small table with 3 fields (Sales,IT,HR) wich I cant select any value (its always sales). I am quite new at android so I havent yet found a way to fix it.
 
kin regards,
GeneralMy source code samplememberthanhnb110118 Feb '13 - 16:58 
Thanks for your tuitorial artical. I code by my way. And this is the source code
http://www.dl.9android.net/index.php?act=dl&id=1360026346[^]
I refered this article and Here to code this sample program.
Thanks author one more time!
QuestionAndroid SQLite tutorialmemberboss prabu29 Jan '13 - 7:33 
Excellent tutorial!! Thanks... Even this site http://androidtechstuffs.blogspot.in/2013/01/android-sqlite-database-tutorial_29.html[^] is also quite helpful.. have a look Smile | :) Smile | :)
QuestionField Order On Insertionmemberbehzad a12 Jan '13 - 7:37 
Hi,
 
Is field order important on insertion in to a table with Content Provider?
GeneralMy vote of 4memberِAymHenry15 Nov '12 - 7:16 
Very good, very simple.
More info. how to pass the Context object. Note that most reader of the article is beginners.
Suggestion[My vote of 1] DownloadmemberEarl Allen15 Oct '12 - 22:25 
Your documentation may be useful; but, there is no way I will download an exe file from some strange web site and possible get a virus. USE THE FILE DOWNLOAD FEATURE OF THE CODE PROJECT.
Question[My vote of 2] 5 for the article and 1 for the downloadmembersnoopy00111 Oct '12 - 0:41 
Thanks for the article. It points out an important part of an android application and it is written very clear.
But the download from ilivid.com is nothing I will do. I don't get the rar file. This site tries to install a dowload manager on my system.
Why do you not use the file function of Code Project?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 10 Oct 2012
Article Copyright 2010 by Mina Samy
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid