Is this the appropriate way to do database access in an Android app? Should I be opening and closing database connections like this, or should I have one SQLiteDatabase object that I continually run queries against? Is my method for get specific column data from the cursor appropriate?
public List<Object> getObjects() {
SQLiteDatabase db = SQLiteDatabase.openDatabase(this.path, null, SQLiteDatabase.OPEN_READONLY);
List<Object> ret = new ArrayList<Object>();
Cursor cursor = db.rawQuery("select * from objects", null);
while(cursor.moveToNext())
{
Object obj = new Object();
obj.setId(cursor.getInt(cursor.getColumnIndex("ID")));
obj.setTitle(cursor.getString(cursor.getColumnIndex("Title")));
ret.add(obj);
}
db.close();
return ret;
}
The Singleton Pattern where you have on final static object to acces a database is the most common for databases
Related
I have made Singelton object to make queries to SQLiteOpenHelper in which I have saved instance of getWriteableDatabase(). Throughout the application lifecycle, for all select and insert/update queries I am using that instance from multiple IntentServices. I have read Write-ahead Logging (WAL) which supports concurrent execution of queries. I am using the above instance with WAL disabled. Actually at a point the database does not return data, I was wondering if SQLite file can get corrupted because I am using getWritableabledatabse for reading/writing from multiple intent services.
Can a deadlock occur with this approach?
As per my findings, WAL should be enabled if you are accessing database from multiple threads.
EDIT
DatabaseAdapter.java
public class DatabaseAdapter {
private Context mContext;
private SQLiteDatabase mSqLiteDatabase;
private DatabaseHelper mDbHelper;
private static DatabaseAdapter adapter;
public static DatabaseAdapter getInstance() {
if(adapter == null) {
synchronized (DatabaseAdapter.class) {
if(adapter == null)
adapter = new DatabaseAdapter(MyApp.getInstance());
}
}
return adapter;
}
public DatabaseHelper getDatabaseHelper() {
return mDbHelper;
}
private DatabaseAdapter(Context c) {
mContext = c;
mDbHelper = new DatabaseHelper(mContext);
mSqLiteDatabase = mDbHelper.getWritableDatabase();
}
private long insert(String tableName, ContentValues contentValues) throws Exception {
this.open();
long id = mSqLiteDatabase.insert(tableName, null, contentValues);
this.close();
return id;
}
private int update(String tableName, ContentValues contentValues, int pk_id) throws Exception {
this.open();
String whereClause = mDbHelper.pk_id + " = " + pk_id;
int n = mSqLiteDatabase.update(tableName, contentValues, whereClause, null);
this.close();
return n;
}
private ArrayList<MyObject> selectChallans(String whereClause, String orderby) throws Exception {
try {
ArrayList<MyObject> arrayListObjects = new ArrayList<MyObject>();
Cursor queryCursor = mSqLiteDatabase.query(tableName, null, whereClause, null, null, null, orderby, null);
if (queryCursor == null) {
return null;
}
while (queryCursor.moveToNext()) {
MyObject myobject = getMyObject(queryCursor);
if(myobject != null)
arrayListObjects.add(myobject);
}
queryCursor.close();
return arrayListObjects;
} catch (Exception e) {
e.printStackTrace();
this.forceClose();
throw e;
}
}
}
I am using this Adapter singleton instance through the application for insert/update and select queries. I was concerned about mSqLiteDatabase instance. These functions are being called from multiple IntentServices.
AFAIK, the best practice is calling getWritableabledatabse only once with one SQLiteOpenHelper. After that, you can use the returned database for all thread without any issue. You have to make sure that you are using one database connection. You can check this Good Answer for more detail.
The SqliteOpenHelper object holds on to one database connection. It appears to offer you a read and write connection, but it really doesn't. Call the read-only, and you'll get the write database connection regardless.
So, one helper instance, one db connection. Even if you use it from multiple threads, one connection at a time. The SqliteDatabase object uses java locks to keep access serialized. So, if 100 threads have one db instance, calls to the actual on-disk database are serialized.
So, one helper, one db connection, which is serialized in java code. One thread, 1000 threads, if you use one helper instance shared between them, all of your db access code is serial. And life is good (ish).
For me, I usually create and open the SQLiteOpenHelper in Application class, then I can use it everywhere in any thread in my app.
ok I just followed an instruction that I should do this to retrieve sql data from database but it just cuts to there so far I have this inside my databasehelper class.
public void getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
}
while (cursor.moveToNext());
}
db.close();
}
So somehow this does is it get all the values of my tables 4th column which contains an int... how do I retrieve the value in my MainActivity and save it in an array of integers?
just add everything in a ArrayList and return the arraylist
simply call the method in your main activty
public ArrayList<Integer> getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
ArrayList data= new ArrayList<>();
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
data.add(resource);
}
while (cursor.moveToNext());
}
db.close();
}
return data;
}
Well, as you have it, the variable resource is scoped only to the while loop. Even if it wasn't it would constantly get overwritten on each loop iteration.
Instead, you should declare a collection higher up and Add each value to it during your while loop. You could also redefine your function to return the collection if integers.
public List<int> getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
List<int> myVals = new List<int>();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource, null); //null for conditions
if (cursor.moveToFirst())
{
do
{
myVals.Add(cursor.getInt(3));
}
while (cursor.moveToNext());
}
db.close();
return myVals;
}
Also, as a note... string concatenation of a SQL query is a recipe for disaster. Look up SQL Injection and best practices to avoid it before continuing further. It is worth the time to get into good habits early on.
EDIT / ADDENDUM
Unless you also limit your result set returned from your table query, you will be getting every record. The function you have here really has no practical use and would likely cause more problems than any benefits it may have. I would suggest, as an example of a more usable function that returns a specific IconResource based on the IconId:
public int getIconResource(int iconId)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "select IconResource from IconTable where IconId = ?";
PreparedStatement pstmnt = db.prepareStatement(getresource);
pstrmnt.setString(1, iconId);
ResultSet rset = db.executeQuery();
int iconResource;
if (rset.next())
iconResource = rset.getInt("IconResource");
db.close();
return iconResource;
}
Of course, the above is making assumptions of your table structure.
Using the above, in your code elsewhere, you would simply call this function with the IconId and use the output however needed:
int iconResource = getIconResource(5); // returns the IconResource for IconId = 5
The above prevents any possible SQL Injection attacks by using a parameterized query and avoiding the use of dynamic concatenated strings sent to your SQL server.
You may try out the following code:
public List<Integer> getIconResource(String tblName)
{
List<Integer> list = new ArrayList<Integer>();
list.clear();
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
list.add(resource);
}
while (cursor.moveToNext());
}
db.close();
return list;
}
Then call this method in MainActivity and store the List in another Integer type list.
databasehelper dbhelper;
List<Integer> newList = dbhelper.getIconResource("Your tablename");
fot(int i = 0 ; i< newList.size() ; i++){
int yourValue = newList(i);
}
Hi i have my own sqlite file and put in inside the assets folder it has multiple table inside. I want to query different table and display it in a listview. I've search different tutorial but it makes me confuse how will i call the different table? do i need to create a data model for each table and do i need to declare each table in my databasehelper? i always see's tutorial that you will create a database instead of using your own database. thank you
To use an existing database file(pre-populated with data), you can try Android SQLiteAssetHelper on github.
To prepare the database files, I recommand sqliteman
Create a database file in sqliteman, say "mydatabase", import the data. you can create multiple tables in a database file.
Put this file in assets/databases/ folder in your Android project.
Use this file with SQLiteAssetHelper
how will i call the different table?
You specify the table name in your SQL query, here is a very simple code, just for your references.
public class MyDatabase extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "mydatabase"; // This is the database file under assets/databases/ folder
private SQLiteDatabase readableDB;
private SQLiteDatabase writeableDB;
private static MyDatabase instance;
public static synchronized MyDatabase getInstance(Context context) {
if (instance == null) {
instance = new MyDatabase(context);
}
return instance;
}
private MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
readableDB = getReadableDatabase();
writeableDB = getWritableDatabase();
}
// Suppose tableA has two columns, name and id.
public ArrayList<String> selectNameById(String Id) {
ArrayList<String> result = new ArrayList<String>();
String query = "SELECT name FROM tableA WHERE id=?";
String[] whereArgs = new String[] { Id };
Cursor cursor = readableDB.rawQuery(query, whereArgs);
if (cursor.moveToFirst()) {
do {
Integer nameIndex = cursor.getColumnIndex("name");
String name = cursor.getString(nameIndex);
result.add(name);
} while (cursor.moveToNext());
}
return result;
}
}
In an Android 'simple' database scenario, is there any benefit or reason to use database.close() and Not databaseHelper.close() ? Is there any benefit or reason to use databaseHelper.close() and Not database.close() ?
Is there a technical reason why both these close methods (shown below) exist?
Thanks,
James
MyDatabaseHelper databaseHelper = new MyDatabaseHelper(this);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues valuesToInsert = new ContentValues();
int id = 0;
valuesToInsert.put("_id", id);
valuesToInsert.put("name", "test");
database.insert("MyRecordsTable", null, valuesToInsert);
database.close();
OR
MyDatabaseHelper databaseHelper = new MyDatabaseHelper(this);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues valuesToInsert = new ContentValues();
int id = 0;
valuesToInsert.put("_id", id);
valuesToInsert.put("name", "test");
database.insert("MyRecordsTable", null, valuesToInsert);
databaseHelper.close();
There isn't really a huge difference. This is the whole definition of close() within SQLiteOpenHelper:
/**
* Close any open database object.
*/
public synchronized void close() {
if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
if (mDatabase != null && mDatabase.isOpen()) {
mDatabase.close();
mDatabase = null;
}
}
The reason both exist, is that there may be instances where developers only use SQLiteOpenHelper for interfacing with their database and want the close() method as a convenience to directly access the DB, or vice versa if developers don't choose to use the OpenHelper at all.
I have created a database. And opened it :
public SQLiteDatabase open() {
String path = "/data/data/com.develop.assetcapture/databases/Asset_Directory";
db = SQLiteDatabase.openOrCreateDatabase(path, null);
return db;
}
I'm writing a user registration and what to enable them to reset their passwords.
I have no problem inserting new records or querying the database. However on my resetPassword(username,password) method I get an error message:
android.database.sqlite.DatabaseObjectNotClosedException:
Application did not close the cursor or database object that was opened here
Please help, I've been stuck on this for too long now.
When you query the database you usually receive a Cursor object. Here is an example:
public Cursor querySomething() {
final String whereClause = ...
final String[] params = ...
final Cursor c = this.getReadableDatabase().query(
Table.TABLE_NAME,
Table.allColumnNames(),
whereClause,
params, // parameters for where clause
null, // group
null, // having
null); // order
return c;
}
Using the cursor c you access the rows in the answer. When you have finished processing the answer you have to close the cursor object:
c.close();