Suggestions to improve SQLite perfromance - android

I am saving data to an SQLite Database. It's taking a while for small amounts of data to be saved. I'm using: beginTransaction();
setTransactionSuccessful();, endTransaction(); etc but it doesn't improve performance. I'm considering switching to RealmDB if I can't improve this. Does anyone have any tips? Cheers
public enum DbSingleton {
INSTANCE;
private DatabaseHandler db;
public Context context;
private DatabaseHandler getDatabaseHandler(Context context) {
if (db != null) {
return db;
} else {
if (MainActivity.mainActivity == null) {
SQLiteDatabase.loadLibs(context);
return db = new DatabaseHandler(context); //make static context field in area this is used. e.g. main
} else {
return db = new DatabaseHandler(MainActivity.mainActivity);
}
}
}
//will provide one sample for reference now
public void insert(Context context, String table, ContentValues values) {
SQLiteDatabase.loadLibs(MainActivity.mainActivity);
//note this line
SQLiteDatabase sql = getDatabaseHandler(context).getWritableDatabase(DatabaseHandler.DB_PASSWD);
try {
sql.beginTransaction();
sql.insert(table, null, values);
// Log.i("Values being sent to db", values.toString());
sql.setTransactionSuccessful();
sql.endTransaction();
} catch (SQLiteException ex) {
Log.e("SQL EXCEPTION", ex.toString());
} finally {
sql.close();
}
}
public Cursor select(Context context, String statement, String[] selectArgs) {
SQLiteDatabase sql = getDatabaseHandler(context).getReadableDatabase(DatabaseHandler.DB_PASSWD);
if (selectArgs == null) {
return sql.rawQuery(statement, null);
} else {
return sql.rawQuery(statement, selectArgs);
}
}
public int Update(Context context, String table, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase sql = getDatabaseHandler(context).getWritableDatabase(DatabaseHandler.DB_PASSWD);
int count = -1;
try {
sql.beginTransaction();
count = sql.update(table, values, where, whereArgs);
sql.setTransactionSuccessful();
sql.endTransaction();
} catch (SQLiteException ex) {
Log.e("SQL EXCEPTION", ex.toString());
}
if (count == 0) count = -1;
return count;
}
public void Drop(Context context, String table) {
SQLiteDatabase sql = getDatabaseHandler(context).getWritableDatabase(DatabaseHandler.DB_PASSWD);
sql.execSQL("DROP TABLE IF EXISTS " + table);
}
public void Create(Context context, String table) {
SQLiteDatabase sql = getDatabaseHandler(context).getWritableDatabase(DatabaseHandler.DB_PASSWD);
sql.beginTransaction();
sql.execSQL(table);
sql.setTransactionSuccessful();
sql.endTransaction();
}

Wrapping your inserts in beginTransaction() and endTransaction() is only saving time when you do multiple inserts.
So always save your data to one table at once using the following format, this greatly improves performance:
ArrayList<String> itemsToInsert; //an array of strings you want to insert
db.beginTransaction();
ContentValues values = new ContentValues(1);
for (int i = 0; i < itemsToInsert.size(); i++) {
values.put('field', itemsToInsert.get(i));
db.insert(table, null, values);
}
db.setTransactionSuccessful();
db.endTransaction();
In addition, for selecting from a table, query() is performing slightly better than rawQuery(), but the difference is small.
Als check this article for more background information about SqlLite Performance:
sqlite-insertions

Android provides a new library as part of the architecture components called Room.
official doc says:
The Room persistence library provides an abstraction layer over SQLite
to allow for more robust database access while harnessing the full
power of SQLite.
Room Persistence Library
Save data in a local database using Room
More:
You can use the room with another awesome library (Paging Library) to handle paging and huge data sets
Paging library

Related

DatabaseLocked exception even after closing with db.close() and db.setTransactionSuccessful()

The application am working on will initially have one database with a table, say tbl_usr which will have only one record. Basically we are trying to keep one user per device. When the user logs in from the device with an auth code, his details will be fetched from server and stored in database. Next time if he tries to enter different auth code, which is valid but is not in table then he will not be allowed to proceed. Below is a common DBHelper class.
But whatever approach am trying, I am getting databaselocked exception, when tried for the 2nd time login. I've referred various links where in it was suggested to use different instance of database within method, but still it comes with error. Below is my Helper class
public class DBaseHelper extends SQLiteOpenHelper {
private static String CREATE_TABLE;
private static final String DATABASE_NAME="IPDB";
private static String UserMessage="";
private int tableType=0;
private ContentValues cValues;
private Cursor cursor;
public enum TableTypes{
Table1
};
public DBaseHelper(Context context){
super(context,context.getExternalFilesDir(null).getAbsolutePath()+"/"+DATABASE_NAME,null,1);
}
#Override
public void onCreate(SQLiteDatabase db){
TableTypes tableTypes=TableTypes.values()[tableType];
switch (tableTypes){
case Table1:
CREATE_TABLE="CREATE TABLE IF NOT EXISTS tbl_usr....";
break;
default:
break;
}
db.execSQL(CREATE_TABLE);
db.close();
System.out
.println("onCreate Method Done.");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
/*db.execSQL("DROP TABLE IF EXISTS "+LOGIN_TABLE);*/
onCreate(db);
}
/*this is the method which gets called from other class Like*/
/*helper.insertRecord(tableParams);*/
public HashMap<String,String> insertRecord(HashMap<String,String> dbaseParams){
HashMap<String,String> response=new HashMap<String,String>();
tableType=Integer.parseInt(dbaseParams.get("tableType"));
cValues = new ContentValues();
String TableName="";
TableTypes tableTypes=TableTypes.values()[tableType];
switch (tableTypes){
case Table1:
String AuthCode=dbParams.get("AuthCode");
/*if user exists then check if its the same user*/
if( CheckUserRecordExists(AuthCode) && empty(UserMessage) ){
response.put("isSuccess","true");
return response;
}
else {
if (!empty(UserMessage)) {
response.put("isSuccess", "false");
response.put("message",UserMessage);
return response;
}
/*add new user
Fill cValues declared above*/
TableName = "Table1";
}
break;
default:
break;
}
SQLiteDatabase dataBase = getWritableDatabase();
/*insert data into database*/
try {
dataBase.beginTransaction();
long rowID = dataBase.insertOrThrow(TableName, null, cValues);
dataBase.setTransactionSuccessful();
}
catch(Exception ex){
ex.printStackTrace();
}
finally {
dataBase.close();
}
response.put("isSuccess", "true");
return response;
}
private boolean CheckUserRecordExists(String authCode){
UserMessage="";
SQLiteDatabase dataBase=getReadableDatabase();
/*Exception here when comes for 2nd time after new installation*/
cursor = dataBase.query("Table1", new String[]{"COUNT(*)"}, null, null, null, null, null);
cursor.moveToFirst();
int iCount=cursor.getInt(0);
/*check if any record exist*/
if(iCount>0){
dataBase.close();
if(!cursor.isClosed()) cursor.close();
/*check if the code entered matches with the record existing*/
if(!CheckIsDataAlreadyInDBorNot("Table1","Auth_Code",authCode))
{
UserMessage="Invalid login!";
return false;
}
else return true;
}
else{
dataBase.close();
if(!cursor.isClosed()) cursor.close();
return false;
}
}
private boolean CheckIsDataAlreadyInDBorNot( String TableName,
String dbfield, String fieldValue) {
/*checking if user is same user*/
SQLiteDatabase dataBase=getReadableDatabase();
String[] columns = { dbfield };
String selection = dbfield + " =?";
String[] selectionArgs = { fieldValue };
String limit = "1";
Cursor cursor = dataBase.query(TableName, columns, selection, selectionArgs, null, null, null, limit);
boolean exists = (cursor.getCount() > 0);
cursor.close();
dataBase.close();
return exists;
}
public static boolean empty( final String s ) {
return s == null || s.trim().isEmpty();
}
}
I know its a huge code, but logic is simple. But the problem is database lock. Could someone let me know how I can make sure that database is always in valid state on each operation?
You have beginTransaction() but no matching calls to endTransaction(). An ongoing transaction keeps the database in a locked state and also keeps the internal reference count nonzero, so close() does not yet actually close the database.
The conventional pattern for transactional operations is
db.beginTransaction();
try {
// db operations that can throw
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Also in your onCreate() you should not be closing the database since you don't own it.

Android-Not able to write to database

I am having issue with android app connectivity to SQLite database. I have an instance of SQLite database in Assets folder. 'Select' operation returns the data that is manually inserted into database.But, Write operations (Insert,Update,Delete) are failing. I was using 'this.getReadableDatabase()' and replaced with 'this.getWritableDatabase', Similarly,
SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY) was replaced with SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE), but still of no use. I am testing on Samsung Galaxy S i-90003 with 2.3.6 version. Some one please help me
db = this.getWritableDatabase();
ContentValues values = new ContentValues();
// values.put(KEY_ID,contact.getId());//Contact Id
values.put(KEY_NAME, contact.getName()); // Contact Name
values.put(KEY_IMAGE, contact.getImage()); // Contact Phone
// Inserting Row
try {
if(isTableExists(TABLE_IMAGES,true)) {
db.insert(TABLE_IMAGES, null, values);
}
}catch (Exception e) {
System.out.println("Error inserting:"+e.getMessage());
}
db.close(); // Closing database connection
public boolean isTableExists(String tableName, boolean openDb) {
if(openDb) {
if(db == null || !db.isOpen()) {
db = getWritableDatabase();
}
if(!db.isReadOnly()) {
db.close();
db = getWritableDatabase();
}
}
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '"+tableName+"'", null);
if(cursor!=null) {
if(cursor.getCount()>0) {
cursor.close();
return true;
}
cursor.close();
}
return false;
}
The problem seems about write permission on the database.
Android give you this 2 method to manage the creation and upgrade of a new database
public class MyDBHandler extends SQLiteOpenHelper {
#Override
public void onCreate(SQLiteDatabase arg0) {
//add here the sql code to create the database
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// everytime you need to change the database place your sql code here
}
}
You should follow this tutorial to understand how to correct it
http://www.techotopia.com/index.php/An_Android_SQLite_Database_Tutorial

Android List to Database

I have a custom List which holds information stored from a online mysql database. I now want to put this List into a sqlite internal database. The table has already been created in the database. I also have a databasehelper class which is working fine.
All the list information is stored in FoodInfoModel class which is made of get and set properties.
Do I create a method in the databasehelper class to insert the whole list at once? not sure how to go about it.
Current Method in databasehelper
public void addDiet(FoodInfoModel foodinfomodel) {
SQLiteDatabase db = getReadableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_DIET_ID, foodinfomodel.getDietID());
values.put(KEY_DAY, foodinfomodel.getDay());
values.put(KEY_QTY, foodinfomodel.getQty());
values.put(KEY_TIME_FOOD, foodinfomodel.getTime());
values.put(KEY_ITEM_FOOD, foodinfomodel.getItem());
values.put(KEY_MEASURE, foodinfomodel.getMeasure());
// Inserting Row
db.insert("my_diet", null, values);
db.close(); //
}
Function to set List and Adapter
public void onFetchComplete(List<FoodInfoModel> data) {
this.data = data;
System.out.println("data is " + data);
if(dialog != null) dialog.dismiss();
// create new adapter
adapter = new DietAdapterNew(this, data);
// set the adapter to list
setListViewHeightBasedOnChildren(listview);
listview.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
How do i add that data list to the internal sqlite db?
Thanks!
You basically need one more method.
public void addDiet(List<FoodInfoModel> foodinfomodels) {
SQLiteDatabase db = getReadableDatabase();
for( FoodInfoModel foodinfomodel : foodinfomodels ){
ContentValues values = new ContentValues();
values.put(KEY_DIET_ID, foodinfomodel.getDietID());
values.put(KEY_DAY, foodinfomodel.getDay());
values.put(KEY_QTY, foodinfomodel.getQty());
values.put(KEY_TIME_FOOD, foodinfomodel.getTime());
values.put(KEY_ITEM_FOOD, foodinfomodel.getItem());
values.put(KEY_MEASURE, foodinfomodel.getMeasure());
// Inserting Row
db.insert("my_diet", null, values);
}
db.close(); //
}
Yes, you have to create this kind of methods in DatabaseHelper class
public class DatabaseHandler extends SQLiteOpenHelper {
public void insertFoodInfo(ChatBase chat) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_DIET_ID, foodinfomodel.getDietID());
values.put(KEY_DAY, foodinfomodel.getDay());
values.put(KEY_QTY, foodinfomodel.getQty());
values.put(KEY_TIME_FOOD, foodinfomodel.getTime());
values.put(KEY_ITEM_FOOD, foodinfomodel.getItem());
values.put(KEY_MEASURE, foodinfomodel.getMeasure());
db.insert("my_diet", null, values);
db.close();
}
public void updateFoodInfo(FoodInfoModel model) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_DIET_ID, foodinfomodel.getDietID());
values.put(KEY_DAY, foodinfomodel.getDay());
values.put(KEY_QTY, foodinfomodel.getQty());
values.put(KEY_TIME_FOOD, foodinfomodel.getTime());
values.put(KEY_ITEM_FOOD, foodinfomodel.getItem());
values.put(KEY_MEASURE, foodinfomodel.getMeasure());
db.update("my_diet", values, KEY_DIET_ID + "=" + model.getId(),null);
db.close();
}
}
and then update or insert each FoodInfoModel inside the loop
And for bulk insert can use this code
db.beginTransaction();
try {
...
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
What you should create is a separate class with public static functions that process the CRUD (Create, Read, Update and Delete) functions associated with the SQL transactions.
Example (SQLcrud.java):
public static boolean insertObject(SQLdatabase localDB, Object insertObject)
{
// Test if things are not null and that the DB is open and writable.
// Insert the object
// If insert successful, return TRUE.
// If anything wrong or insert not successful return FALSE (or int indicating what went wrong.
}
Actual Example:
public static boolean insertLocalAdr(SQLiteDatabase db, PersonAddress adr, boolean deleteCurrent, boolean transaction)
throws SQLFunctionFailed {
if(db != null && adr != null)
{
try
{
// If the connection is open and writable.
SQLiteGeneral.openAndWritable(db);
if(deleteCurrent)
{
deleteLocalAdr(db, transaction);
}
String sqlStmt = GeneralSQLfunctions.getUserAdrInsert(
adr,
PersonAddress.ADR_TABLE_NAME,
GeneralSQLfunctions.databaseType.SQLITE);
return StmtExecution(db, sqlStmt, transaction);
}
catch (SQLException e)
{
e.printStackTrace();
throw new SQLFunctionFailed(e.getMessage());
}
}
else
{
throw new SQLFunctionFailed("Given DB or Adr was NULL! FATAL ERROR!");
}
}
Note: GeneralSQLfunctions.getUserAdrInsert just gets a simple formatted INSERT statement and StmtExecution simply executes the statement on the SQL DB. They are there for simplification. SQLiteGeneral.openAndWritable(db) throws a (custom) SQLFunctionFailed exception so the function fails and does not proceed.
While iterating over each list items, you can start a new AsyncTask or Thread to to make it faster.

SQLite queries to create a defined Java Object

I have a SQLite table of this tipe
Table Vehicles:
CATEGORY COUNTRY ID NAME EMAIL
A GE 1 BMW sample1#salple.it
A GE 2 Lamborghini sample2#salple.it
B GE 3 BMW sample3#salple.it
I want to select all the entries that have a specified name or a specified category and pass all the parameters how each row in a constructor
Vehicle(String category, String country, int id, String name, String email)
I have implemented this adapter using some tutorials:
public class TestAdapter
{
protected static final String TAG = "DataAdapter";
private final Context mContext;
private SQLiteDatabase mDb;
private DataBaseHelper mDbHelper;
public TestAdapter(Context context)
{
this.mContext = context;
mDbHelper = new DataBaseHelper(mContext);
}
public TestAdapter createDatabase() throws SQLException
{
try
{
mDbHelper.createDataBase();
}
catch (IOException mIOException)
{
Log.e(TAG, mIOException.toString() + " UnableToCreateDatabase");
throw new Error("UnableToCreateDatabase");
}
return this;
}
public TestAdapter open() throws SQLException
{
try
{
mDbHelper.openDataBase();
mDbHelper.close();
mDb = mDbHelper.getReadableDatabase();
}
catch (SQLException mSQLException)
{
Log.e(TAG, "open >>"+ mSQLException.toString());
throw mSQLException;
}
return this;
}
public void close()
{
mDbHelper.close();
}
public boolean SaveVehicles(String category , String country, String id, String name, String email)
{
try
{
ContentValues cv = new ContentValues();
cv.put("Category", category);
cv.put("Country", country);
cv.put("id", id);
cv.put("Name", name);
cv.put("Email", email);
mDb.insert("Vehicles", null, cv);
Log.d("SaveVehicles", "informationsaved");
return true;
}
catch(Exception ex)
{
Log.d("SaveVehicles", ex.toString());
return false;
}
}
}
But I don't know how I could implement the various get methods that I need, to meet a solution to my problem.
Creating an object from a SQL query would look something like this
/**
* #return Returns a list of all objects.
*/
public ArrayList<Object> getAllObjects()
{
// Select All Query
String selectQuery = "SELECT * FROM SOME_TABLE";
// Get the isntance of the database
SQLiteDatabase db = this.getWritableDatabase();
//get the cursor you're going to use
Cursor cursor = db.rawQuery(selectQuery, null);
//this is optional - if you want to return one object
//you don't need a list
ArrayList<Object> objectList = new ArrayList<Object>();
//you should always use the try catch statement incase
//something goes wrong when trying to read the data
try
{
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
//the .getString(int x) method of the cursor returns the column
//of the table your query returned
Object object= new Object(Integer.parseInt(cursor.getString(0)),
Integer.parseInt(cursor.getString(1)),
Integer.parseInt(cursor.getString(2)),
cursor.getString(3),
cursor.getString(4),
cursor.getString(5),
Boolean.parseBoolean(cursor.getString(6))
);
// Adding contact to list
objectList.add(object);
} while (cursor.moveToNext());
}
}
catch (SQLiteException e)
{
Log.d("SQL Error", e.getMessage());
return null;
}
finally
{
//release all your resources
cursor.close();
db.close();
}
return objectList;
}
The code above assumes you have some table in your database named "SOME_TABLE" and that you have an object that takes 7 parameters but you should be able to alter the snippet to make it work for you.
You need to query your database for the data, and then iterate through the returned cursor to pull out the data you need and put it into strings to feed into your constructor.
The query would look something like this (using the info you provided and the query method):
public Cursor fetchList(String category) {
return mDb.query("Vehicles", new String[] { "CATEGORY", "COUNTRY", "ID", "NAME", "EMAIL" }, "Category =" + category,
null, null, null, null);
}
Note that this is a basic query and subject to SQL injection attacks, It should be parameterized to make it less vulnerable, unless you are not going to allow the user to type in the category and rather have them pick from a list you provide.
Anyway, that would return your data in a cursor, with one row for each record that matched the search parameters. From there, you would need to iterate through the returned cursor and pull the data out of it and into strings you can use.

I want to return only and integer from an Android Database

I have already built a working database and want to return the max(_id) . I do not want a cursor though, I know it is going to be and int I only want to return the number. Is there a way to query this?
I think simpleQueryForLong is what you are looking for.
SQLiteDatabase db;
// initialize the db
SQLiteStatement statement = null;
try {
db.compileStatement("select max(_id) from table");
int max = statement.simpleQueryForLong();
} catch (SQLException e) {
// handle
} finally {
if (statement != null) {
statement.close();
}
}

Categories

Resources