Is it a good practice to open and close the database for every database transaction operation? let me clear you more.
I have two methods like
public SQLiteDatabase getDatabase() {
if (database == null || !database.isOpen()) {
database = getWritableDatabase();
}
return database;
}
public void closeDatabase() {
if (database != null && database.isOpen()) {
database.close();
}
}
so every time, when I am updating/inserting or deleting, I am opening the database and closing it.
public void insert(...) {
getDatabase().insert(...);
closeDatabase();
}
public void update(...) {
getDatabase().update(...);
closeDatabase();
}
public void delete(...) {
getDatabase().delete(...);
closeDatabase();
}
remember that all these methods are inside a class DatabaseHelper which is extending SQLiteOpenHelper and there is a global variable private SQLiteDatabase database
and I will perform these operations(insert/update/delete) more frequently.
So my question is Is it a good practice to open and close database for every transaction? if not, what is the good way? Where and When I have to close my database?
Opening and closing the database every time may (un-intentionally) run into problem such as Trying to open an already closed database.
Hence, I would suggest is to have a Singleton for the creating the database object, so that every time you make a call to database = getWritableDatabase(); you refer to the same object.
Consider closing this in onDestroy() method, so that as and when the App closes database is closed too.
private static AllItemsDB db; //AllItemsDB is my database class
public static AllItemsDB getDb() {
if (db == null) {
Log.d("","Issue here");
db = new AllItemsDB(app);
Log.d("","Issue here not");
}
return db;
}
since this is a static method, I can do AllItemsDB.myCRUD_methods and it will return me the same oblect every time and easy to access as well. :)
Help.
Related
I have singleton database helper to access db. This part has no problem. However, I doubt that async threads of reading and writing/deleting ends up with problem.
If one thread is reading, and the other one is deleting; I am suspicious about reading one cannot read the value before deletion. Can anybody confirm this? And what should be the solution way for achieving this with singleton helper?
Any help is appreciated, thanks
public CategoryDatabaseConnection(Context context) {
mDbOpenHelper = CategoryDatabaseOpenHelper.getInstance(context, null, null, 0);
mOpenCounter = mDbOpenHelper.mOpenCounter;
}
public void open() throws SQLException {
// open database in reading/writing mode
int value = mOpenCounter.incrementAndGet();
if(value == 1 || mDatabase==null) {
// Opening new database
mDatabase = mDbOpenHelper.getWritableDatabase();
}
}
You database helper must be synchronized so that only one thread can access it at a time.
To implement synchronization just put keywod synchronized before your class.
Ex.
public static synchronized singletonDBHelper()
{
// your code
}
There are a few Android APIs (after donut and before honeycomb) if Im not mistaken, where Google have enabled the AsyncTasks to run paralelly aiming for faster execution. Then lots of devs made mistakes when reaching out to the same database using multiple AsyncTasks, and since Android 3.0 AsyncTasks are running serially by default.
I am suffering this problem now when testing my app on an Android 2.3.4 device with my SQLite
First, Im getting categories from the server, I open DB, insert them close DB.
Second I get the subcategories from the server, open DB, insert them into DB, close DB
Third I get user items from the server, open DB, insert items, then close DB
Im taking good care to ensure that one starts after another, but in every 8-10 iterations something somewhere slows down and overlaps with another procedure right in the moment where a task is opening the db, another task closes it right after, and the first task starts trying to write to a closed db....
What do I do? I want clean, reliable separation, sequential execution and I dont want to start the asynctasks from the previous asynctask's onPostExecute, because these three will not always run in a row
I read an article yesterday that you CANT do it on android 2.x
Shall I try to open the DB and DBHelper before ALL of the operations and close the DB afterwards?
EDIT: Usually I get the error here (at Begin transaction):
(The error says that the DB is closed)
#Override
protected Void doInBackground(Void... arg0) {
// dbTools.close();
try {
if (database == null) {
database = dbTools.getWritableDatabase();
}
} catch (Exception e) {
e.printStackTrace();
}
ContentValues values = new ContentValues();
database.beginTransaction();
try {
// Iterating all UserItem objects from the LinkedHashSet and getting their info
for (UserItem userItem : userItems) {
// Inserting values for the database to insert in a new record
values.put(C.DBColumns.ITEM_ID, userItem.getItemId());
values.put(C.DBColumns.ITEM_NAME, userItem.getItemName());
// database.insertWithOnConflict(C.DBTables.ITEMS, null, values, SQLiteDatabase.CONFLICT_REPLACE);
database.insert(C.DBTables.ITEMS, null, values);
} // End of For loop
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
// Closing all cursors, databases and database helpers properly because not closing them can spring lots of trouble.
if (database != null && database.isOpen()) {
try {
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
} // End of doInBackground
And this is my DBTOOLS CLASS:
public class DBTools extends SQLiteOpenHelper {
// Its a good practice for DBTools to be a singleton. Do not instantiate it with "new DBTools(context)" but with
// DBTools.getInstance(context) instead
private static DBTools sInstance;
public static DBTools getInstance(Context context) {
if (sInstance == null) {
sInstance = new DBTools(context);
}
return sInstance;
}
public DBTools(Context context) {
super(context, C.Preferences.LOCAL_SQLITE_DATABASE_NAME, null, 1);
}
public void onCreate(SQLiteDatabase database) {
database.execSQL(SQLQueries.tableCategoriesCreate);
database.execSQL(SQLQueries.tableSubcategoriesCreate);
database.execSQL(SQLQueries.tableItemsCreate);
}
public void onOpen(SQLiteDatabase database) {
database.execSQL(SQLQueries.tableCategoriesCreate);
database.execSQL(SQLQueries.tableSubcategoriesCreate);
database.execSQL(SQLQueries.tableItemsCreate);
}
public void onUpgrade(SQLiteDatabase database, int version_old, int current_version) {
database.execSQL(SQLQueries.tableCategoriesDrop);
database.execSQL(SQLQueries.tableSubcategoriesDrop);
database.execSQL(SQLQueries.tableItemsDrop);
onCreate(database);
}
} // End of Class
Since you can't call from onPostExecute, I would say you have two options, one would be to move your open close calls to the beginning and end of your activity or service.
Option two would be to setup a reference counter in your DB and DBHelper where you track the number of times open has been called, and then decrement that count when close is called. That way you can perform close only when the count is 0. One thing to remember when taking this approach is that you should probably have a method that will force the db to close that you call when you are sure your other connections are done. This shouldn't be necessary but will be a failsafe to ensure the db gets closed if something goes wrong.
Edit: You would have to make DBTools a singleton for it to work, but it's not equivalent. Here's a quick example.
public class DBTools {
private static DBTools instance;
private static int openCount;
public DBTools getInstance() {
if (instance == null) {
instance = new DBTools();
}
return instance;
}
private DBTools() {
openCount = 0;
}
public void open() {
openCount++;
//Do open
}
public close() {
openCount--;
if (openCount == 0) {
//Do close
}
public void forceDBClose() {
//Do close
}
}
I am also a newbee in android. I was having a problem like this too.
To overcome this, i used Singleton class.
I created one instance of the DBHelper class and used it in all my asynctasks.
So, until the DB is closed, all the asynctasks access the initialised DB object.
If there is no object in the memory, the async tasks, instantiates it and use it then.
I'm trying to implement android SQLite usage design pattern that ensures one opened SQLiteDatabase instance per application.
public class BaseDataSource {
private static final CustomSQLiteHelper dbHelper = CustomSQLiteHelper.getInstance();
protected static SQLiteDatabase database;
static {
//HERE RISES EXCEPTION
BaseDataSource.database = BaseDataSource.dbHelper.getWritableDatabase();
}
private void close() {
if(null != BaseDataSource.database && BaseDataSource.database.isOpen()) {
BaseDataSource.database.close();
if(null != BaseDataSource.dbHelper) {
BaseDataSource.dbHelper.close();
}
}
}
protected BaseDataSource() {}
protected void finalize () throws Throwable {
close();
super.finalize();
}
}
But while my applications starts I get this kind exception:
Caused by: java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.xxx/databases/xxx.db
How SQLiteDatabse database can be opened and closed before class was created?
UPDATED
I found my own bug. It was in CustomSQLiteHelper class. In onCreate method I closed database. I tried every soliution that I found in internet and due to that I made a bug.
if you going to event any thing then first need to open as write database. So for that use this method
and call like this
openAsWrite();
public void openAsWrite() throws SQLException {
db = DBHelper.getWritableDatabase();
}
// ---closes the database---
public void close() throws SQLException {
DBHelper.close();
}
Use following pattern when getting a database object:
try {
if (sDatabase != null) {
if (!sDatabase.isOpen()) {
sDatabase = sContext.openOrCreateDatabase(DATABASE_NAME, 0, null);
}
} else {
// open database here
sDatabase = sContext.openOrCreateDatabase(DATABASE_NAME, 0, null);
}
Log.d(TAG, "Database successfully opened.");
} catch (SQLException e) {
Log.e(TAG, "" + e);
}
Using database object as static makes your class behave like this.
Creates database instance when the class is loaded.
closes the database connection when finalize method is called.
your class will not going to acquire database connection anymore, and it will have the database instance which is closed already.
eventually when try to access the instance which is closed, it pops the error to you
I think you should use a different approach will be better.
I have an app that use a sqlite database. For every query (10 query selects in 10 differents methods) i open the database. It is a good choice to open db when the application is started and close it when application close? For example, creating an static reference to my DB object in MyApplication class (extends Application).
Thanks for your advices.
Create a static singleton which keeps references to the db and other oft-used resources, such as this:
class Global {
private static SQLiteDatabase _db = null;
public static SQLiteDatabase getDb() {
if( _db == null ) {
... assign & open _db
}
return _db;
}
}
Elsewhere, just reference Global.getDb()
I'm getting two contradicting Exceptions when creating and populating my new SQLiteDatabase in Android. In short my code:
SQLiteOpenHelper extending class:
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_TABLE_CREATE);
loadLevelData(db); //puts data in the database
//db.close(); <<< ?
}
In my activity class I instantiate this class (in onCreate()), and call getWritableDatabase():
dbHelper = new DbOpenHelper(getApplicationContext());
database = dbHelper.getWritableDatabase();
Now if I don't call db.close() after populating the database, like above, I get
android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
However if I DO close it, I get the following exception:
java.lang.IllegalStateException: database not open
on getWritableDatabase().
This really confuses me, so could anyone help me with what's wrong?
You are not expected to close the database in the DatabaseHelper class. However you need to close it every time you open it calling getWritableDatabase:
dbHelper = new DbOpenHelper(getApplicationContext());
database = dbHelper.getWritableDatabase();
//... do something with database
database.close();
You are closing your database at the wrong time.
I typically keep the database around like this:
public class MyActivity extends Activity {
SQLiteDatabase writeableDb;
// ...
// Code
// ...
public void onStart(){
super.onCreate(savedState);
// Do stuff, get your helper, etc
writeableDb = helper.getWriteableDatabase();
}
public void onStop(){
writeableDb.close();
super.onStop();
}
}
Alternatively, wrap all your code working with that db connection in a try/finally block
db = helper.getWriteableDatabase();
try { // ... do stuff ... }
finally { db.close(); }
Note: All of the opening/closing should be done in the Activity working with the database, not the open helper.