I know you can use python and other scripting languages in android. But I haven't seen weather or not it was possible to use python as an interface to sqlite in android. Is this possible? This is the first android app where I've needed sqlite, and using the java api's is retarded.
If this isn't possible, can someone point me to a good tutorial on sqlite in android? I've found a bunch, but all of them are entirely different and I'm totally lost on which is the best way to do it.
It's just hard to picture how google expects you to use the sqlite database. It seems like you need like 10 different classes just to query a database.
Actually you just need 3 classes:
A ContentProvider, as found here: http://developer.android.com/guide/topics/providers/content-providers.html
Second you need is a SQLiteOpenHelper and last but not least a Cursor
Edit: Just noticed it's not obvious from the snippets what the db variable is. It's the SQLiteOpenHelper or better my extension of it (where I've only overridden the onCreate, onUpgrade and constructor. See below ^^
The ContentProvider is the one which will be communicating with the database and do the inserts, updates, deletes. The content provider will also allow other parts of your code (even other Apps, if you allow it) to access the data stored in the sqlite.
You can then override the insert/delete/query/update functions and add your functionality to it, for example perform different actions depending on the URI of the intent.
public int delete(Uri uri, String whereClause, String[] whereArgs) {
int count = 0;
switch(URI_MATCHER.match(uri)){
case ITEMS:
// uri = content://com.yourname.yourapp.Items/item
// delete all rows
count = db.delete(TABLE_ITEMS, whereClause, whereArgs);
break;
case ITEMS_ID:
// uri = content://com.yourname.yourapp.Items/item/2
// delete the row with the id 2
String segment = uri.getPathSegments().get(1);
count = db.delete(TABLE_ITEMS,
Item.KEY_ITEM_ID +"="+segment
+(!TextUtils.isEmpty(whereClause)?" AND ("+whereClause+")":""),
whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown Uri: "+uri);
}
return count;
}
The UriMatcher is defined as
private static final int ITEMS = 1;
private static final int ITEMS_ID = 2;
private static final String AUTHORITY_ITEMS ="com.yourname.yourapp.Items";
private static final UriMatcher URI_MATCHER;
static {
URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
URI_MATCHER.addURI(AUTHORITY_ITEMS, "item", ITEMS);
URI_MATCHER.addURI(AUTHORITY_ITEMS, "item/#", ITEMS_ID);
}
This way you can decide if only 1 result shall be returned or updated or if all should be queried or not.
The SQLiteOpenHelper will actually perform the insert and also take care of upgrades if the structure of your SQLite database changes, you can perform it there by overriding
class ItemDatabaseHelper extends SQLiteOpenHelper {
public ItemDatabaseHelper(Context context){
super(context, "myDatabase.db", null, ITEMDATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String createItemsTable = "create table " + TABLE_ITEMS + " (" +
...
");";
// Begin Transaction
db.beginTransaction();
try{
// Create Items table
db.execSQL(createItemsTable);
// Transaction was successful
db.setTransactionSuccessful();
} catch(Exception ex) {
Log.e(this.getClass().getName(), ex.getMessage(), ex);
} finally {
// End transaction
db.endTransaction();
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String dropItemsTable = "DROP TABLE IF EXISTS " + TABLE_ITEMS;
// Begin transaction
db.beginTransaction();
try {
if(oldVersion<2){
// Upgrade from version 1 to version 2: DROP the whole table
db.execSQL(dropItemsTable);
onCreate(db);
Log.i(this.getClass().toString(),"Successfully upgraded to Version 2");
}
if(oldVersion<3) {
// minor change, perform an ALTER query
db.execSQL("ALTER ...");
}
db.setTransactionSuccessful();
} catch(Exception ex){
Log.e(this.getClass().getName(), ex.getMessage(), ex);
} finally {
// Ends transaction
// If there was an error, the database won't be altered
db.endTransaction();
}
}
}
and then the easiest part of all: Perform a query:
String[] rows = new String[] {"_ID", "_name", "_email" };
Uri uri = Uri.parse("content://com.yourname.yourapp.Items/item/2";
// Alternatively you can also use getContentResolver().insert/update/query/delete methods
Cursor c = managedQuery(uri, rows, "someRow=1", null, null);
That's basically all and the most elegant way to do it as far as I know.
Related
For example code below. Do we need to close cursor? Do we better use try/catch/finally instead of using if()?
public int getCount() {
final Cursor countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
if (countCursor == null) {
return 0;
}
countCursor.moveToFirst();
final int count = countCursor.getInt(0);
return count;
}
The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
The answer I believe is primarily opinion-based. It depends I guess on coder's preference and the circumstances.
I have always preferred the if (cursor != null) or vice versa approach. Unless something truly spectacular has happened; which will be handled by throws Exception, I'd use if-else checks wherever I want the reader/reviewer to see which parts are really and truly exceptions and which are occurrences of different possible/valid scenarios.
This brings us to the current problem of Curosr and applying null checks.
AFAIK (since mostly a Cursor is related with a SQLiteDatabase) a ContentResolver.query() should never return a null Cursor if the query itself is valid unless in case of an invalid query which is a real exception and you should instead get an Exception.
So in my opinion the best approach would be using your example either
public int getCount() throws Exception {
Cursor countCursor;
try {
countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
countCursor.moveToFirst();
return countCursor.getInt(0);
}
finally {
cursor.close();
}
}
Or a variation where Exception is caught and handled within the method itself.
Now to answer your second question whether or not you should close() a Cursor: you should always close a Cursor. Whenever you don't have need for it. If you delve deeper into any of the Cursor.close() method-implementations. Since Curosr is an interface which in case of SQLite is implemented by SQLiteCursor you will notice that this method releases any and all allocations held by it.
I prefer to make a database helper class and through that database access becomes much much easier. Sample of a database helper Class -
public class DatabaseHelperClass extends SQLiteOpenHelper {
public DatabaseHelperClass(Context context)
{
super(context,"hakeem.db",null,1);
}
//Tables
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("Your SQL Query to create a table");
}
//Delete Tables
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table table_name if exists");
onCreate(db);
}
//Insertion of data into tables
long insertData(Various Parameters you like to pass for insertion)
{
SQLiteDatabase db=getWritableDatabase();
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return db.insert("signup_details",null,values);
}
//Delete record
public int deleteData(int id)
{
SQLiteDatabase sb=getWritableDatabase();
return sb.delete("hospital_details","id="+id,null);
}
//Update data in table
int updateData(Parameters you want to pass for update. Make sure you include a primary key)
{
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return getWritableDatabase().update("signup_details",values,"id="+id,null);
}
//Read data from tables
Cursor getSigninDetails() { return getWritableDatabase().rawQuery("select * from table_name",null); }
}
and to access results from the database-
private void getDataFromDatabase() {
Cursor cursor = db.getUserData();
if (cursor.moveToFirst()) {
do {
var_name1= cursor.getString(0);
var_name2= cursor.getString(1);
var_name3= cursor.getString(2);
var_name4= cursor.getString(3);
} while (cursor.moveToNext());
}
cursor.close();
}
I have some trouble with a SQLite database with 1 table and 2 columns, column_id and word. I extended SQLiteAssetHelper as MyDatabase and made a constructor:
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
I need to check whether some string is in the database (in column word). I tried to modify the code from answer provided by Benjamin and dipali, but I used SQLiteAssetHelper and I can't get it to work. The method that I have in mind receives the string to search for as a parameter and returns a boolean if string is in the database.
public boolean someMethod(String s)
In addition, I tried to put the check on a background thread with AsyncTask because I have 60 strings to check.
TABLE_NAME and COLUMN_WORD should be self-explanatory.
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {COLUMN_WORD};
String where = COLUMN_WORD + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
Cursor cursor = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1");
if (cursor.moveToFirst()) {
return true; // a row was found
}
return false; // no row was found
}
You can do this in the background, but I don't think for a query like this it's even necessary.
EDIT
There are some improvements that should be made to the above for the sake of correctness. For one thing, the Cursor should be closed since it is no longer being used. A try-finally block will ensure this:
Cursor cursor = db.query(...);
try {
return cursor.moveToFirst();
} finally {
cursor.close();
}
However, this method doesn't need to obtain a whole `Cursor. You can write it as follows and it should be more performant:
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String sql = "select count(*) from " + TABLE_NAME + " where "
+ COLUMN_WORD + " = " + DatabaseUtils.sqlEscapeString(s);
SQLiteStatement statement = db.compileStatement(sql);
try {
return statement.simpleQueryForLong() > 0;
} finally {
statement.close();
}
}
You could add a catch block and return false if you think it's possible (and valid) to encounter certain exceptions like SQLiteDoneException. Also note the use of DatabaseUtils.sqlEscapeString() because s is now concatenated directly into the query string and thus we should be wary of SQL injection. (If you can guarantee that s is not malicious by the time it gets passed in as the method argument, then you could theoretically skip this, but I wouldn't.)
because of possible data leaks best solution via cursor:
Cursor cursor = null;
try {
cursor = .... some query (raw or not your choice)
return cursor.moveToNext();
} finally {
if (cursor != null) {
cursor.close();
}
}
1) From API KITKAT u can use resources try()
try (cursor = ...some query)
2) if u query against VARCHAR TYPE use '...' eg. COLUMN_NAME='string_to_search'
3) dont use moveToFirst() is used when you need to start iterating from beggining
4) avoid getCount() is expensive - it iterates over many records to count them. It doesn't return a stored variable. There may be some caching on a second call, but the first call doesn't know the answer until it is counted.
I am having an existing sqlite database. I am developing an android app and I want to connect it with this existing sqlite DataBase.
Problem 1:
I have already included the sqlite database in my project via "DDMS push database function" as per my instructor's advise. Now I want to fetch the data from database, do I need to use SQLiteOpenHelper. If yes, how to use it and what will be coded in onCreate(SQLiteDatabase db) function and onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) function as we already have the Database with us, we don't really need to create it.
Problem 2:
What should be done to simply fetch the required data from the existing database.
Being a newbie, I am quite confused, can someone please explain these concepts and guide me to overcome this issue. Any help would be appreciated.
I have seen a tutorial also for this purpose as sugggested by #TronicZomB, but according to this tutorial (http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/), I must be having all the tables with primary key field as _id.
I have 7 tables namely destinations, events, tour, tour_cat, tour_dest, android_metadata and sqlite_sequence. Out of all, only tour_dest is not fulfilling the conditions of having a primary key named as _id. How to figure out this one?
Following is the screenshot of table which is lacking the primary key field necessary for binding id fields of database tables.
The onCreate and onUpgrade methods will be empty since you already have the database. There is a great tutorial on how to achieve this here.
You could then access the database like such (example):
public ArrayList<String> getValues(String table) {
ArrayList<String> values = new ArrayList<String>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT value FROM " + table, null);
if(cursor.moveToFirst()) {
do {
values.add(cursor.getString(cursor.getColumnIndex("value")));
}while(cursor.moveToNext());
}
cursor.close();
db.close();
return values;
}
Unless you are very comfortable with queries, databases, etc. I highly recommend you use http://satyan.github.io/sugar/ , it will also remove a lot of the boiler plate code required to do sqlite in Android
1. If DB already exists, onCreate will not invoke. onUpgrade will be invoked only if you will change DB version. onUpgrade you should to use if there some changes in your APP's database, and you have to make migration on new structure of data smoothly.
public class DbInit extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "name";
private static final int DATABASE_VERSION = 3;
private static final String DATABASE_CREATE = "create table connections . .. . ...
public DbInit(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (isChangeFromToVersion(1, 2, oldVersion, newVersion)) {
//Execute UPDATE here
}
}
private boolean isChangeFromToVersion(int from, int to, int oldVersion, int newVersion ) {
return (from == oldVersion && to == newVersion);
}
....
2. Simple example how to open connection to DB and get cursor object.
public class DAO {
private SQLiteDatabase database;
private DbInit dbHelper;
public ConnectionDAO(Context context) {
dbHelper = new DbInit(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public Connection getConnectionById(long id) {
Cursor cursor = null;
try {
open();
cursor = database.query(DbInit.TABLE_CONNECTIONS, allColumns, DbInit.COLUMN_ID + " = '" + id + "'", null, null, null, null);
if (!cursor.moveToFirst())
return null;
return cursorToConnection(cursor);
} finally {
if (cursor != null)
cursor.close();
close();
}
}
private Connection cursorToConnection(Cursor cursor) {
Connection connection = new Connection();
connection.setId(cursor.isNull(0) ? null : cursor.getInt(0));
connection.setName(cursor.isNull(1) ? null : cursor.getString(1));
.....
.....
return connection;
}
In my application I have a content provider which uses a database for the content provided. When the database is created the first time it needs to be filled from the raw content of a Json file. My idea was that i trigger this filling of the database at onCreate of my SQLiteOpenHelper subclass. This works fine yet I am not sure how to handle the the communication between application and content provider when the app is running the first time. Basically i would like to show some sort of a splash screen while the database is filled. Yet how does the application get informed that
the content provider is busy filling the database when running the first time
the content provider is ready to go
Surely I could fill the database from the application by calling the content provider with each dataset yet I would prefer doing it within the sphere of the content provider so that the application does not have to handle the reading of the json file etc. Besides design preferences it would also enable the content provider to fill the database more efficiently because it would have the whole dataset at once. I have a feeling this is not possible yet I hope I miss some simple point.
Any suggestions how to achieve this would be highly appreciated.
Thanks
martin
When using a content Provider i would presume that your using a DBHelper class to manage the creation of the database. Below is the code from the android notes example project.
This shows how the DBHelper constructor is intelligent enough to determine if the database has been created before. In the createDatabase method i would subsequently call a method to pre-populate the database, from as you say a json file.
The problem is that this doesn't really allow you to communicate to the Activity that your database hasn't been initialised.
One thought could be that you use SharedPreferences to store the fact you've populated the database. You could then check the sharedPreference in the activity on startup, Call the content provider to populate the database and then store in the shared preference that you've done this task already.
Just be aware that i'm not sure if the sharedPreferences maintain the same state as the database if you for example erase the data from the android settings menu. You'd need to check that.
http://code.google.com/p/android-notes/source/browse/trunk/src/com/bitsetters/android/notes/DBHelper.java?r=10
public class DBHelper {
private static final String DATABASE_NAME = "notes";
private static final String TABLE_DBVERSION = "dbversion";
private static final String TABLE_NOTES = "notes";
private static final int DATABASE_VERSION = 1;
private static String TAG = "DBHelper";
Context myCtx;
private static final String DBVERSION_CREATE =
"create table " + TABLE_DBVERSION + " ("
+ "version integer not null);";
private static final String NOTES_CREATE =
"create table " + TABLE_NOTES + " ("
+ "id integer primary key autoincrement, "
+ "note text, "
+ "lastedit text);";
private static final String NOTES_DROP =
"drop table " + TABLE_NOTES + ";";
private SQLiteDatabase db;
/**
*
* #param ctx
*/
public DBHelper(Context ctx) {
myCtx = ctx;
try {
db = myCtx.openOrCreateDatabase(DATABASE_NAME, 0,null);
// Check for the existence of the DBVERSION table
// If it doesn't exist than create the overall data,
// otherwise double check the version
Cursor c =
db.query("sqlite_master", new String[] { "name" },
"type='table' and name='"+TABLE_DBVERSION+"'", null, null, null, null);
int numRows = c.getCount();
if (numRows < 1) {
CreateDatabase(db);
} else {
int version=0;
Cursor vc = db.query(true, TABLE_DBVERSION, new String[] {"version"},
null, null, null, null, null,null);
if(vc.getCount() > 0) {
vc.moveToFirst();
version=vc.getInt(0);
}
vc.close();
if (version!=DATABASE_VERSION) {
Log.e(TAG,"database version mismatch");
}
}
c.close();
} catch (SQLException e) {
Log.d(TAG,"SQLite exception: " + e.getLocalizedMessage());
} finally {
db.close();
}
}
private void CreateDatabase(SQLiteDatabase db)
{
try {
db.execSQL(DBVERSION_CREATE);
ContentValues args = new ContentValues();
args.put("version", DATABASE_VERSION);
db.insert(TABLE_DBVERSION, null, args);
db.execSQL(NOTES_CREATE);
// Populate with data
populateDataBaseFromFile();// There are probably better ways to do this.
setSharedPreferenceYouPopulatedDB();
} catch (SQLException e) {
Log.d(TAG,"SQLite exception: " + e.getLocalizedMessage());
}
}
Personally I wouldn't bother with the splash screen, unless you really needed to.
Another thought might be to:
Write in the db helper a method to determin if your tables exist. Return false if not.
In startup activity call ContentProvider with a request that calls the DBHelper test method.
If false then display splash screen and then call Content Provider to populate DB.
If true, then carry on as normal.
I am doing my first app with a database and I am having a little trouble understanding the onUpgrade function. My database has a table with an items and a favorite column so that the user can favorite an item. Most implementations I see simply drop the table and reconstruct it but I don't want to do this. I want to be able to add more items to the table.
When the app is upgraded through the android marketplace does the database know its version number? So could I increment the version number in the code and then export it to the marketplace and when the user boots up the upgraded version for the first time then onUpgrade will be called?
If this is the case my onUpgrade would simply pull from a file and add the database items in. Is this a standard way of doing things or is there a better way of handling this in Android. I am trying to stay as standard as possible.
Thanks
Ok, before you run into bigger problems you should know that SQLite is limited on the ALTER TABLE command, it allows add and rename only no remove/drop which is done with recreation of the table.
You should always have the new table creation query at hand, and use that for upgrade and transfer any existing data. Note: that the onUpgrade methods runs one for your sqlite helper object and you need to handle all the tables in it.
So what is recommended onUpgrade:
beginTransaction
run a table creation with if not exists (we are doing an upgrade, so the table might not exists yet, it will fail alter and drop)
put in a list the existing columns List<String> columns = DBUtils.GetColumns(db, TableName);
backup table (ALTER table " + TableName + " RENAME TO 'temp_" + TableName)
create new table (the newest table creation schema)
get the intersection with the new columns, this time columns taken from the upgraded table (columns.retainAll(DBUtils.GetColumns(db, TableName));)
restore data (String cols = StringUtils.join(columns, ",");
db.execSQL(String.format(
"INSERT INTO %s (%s) SELECT %s from temp_%s",
TableName, cols, cols, TableName));
)
remove backup table (DROP table 'temp_" + TableName)
setTransactionSuccessful
(This doesn't handle table downgrade, if you rename a column, you don't get the existing data transfered as the column names do not match).
.
public static List<String> GetColumns(SQLiteDatabase db, String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("select * from " + tableName + " limit 1", null);
if (c != null) {
ar = new ArrayList<String>(Arrays.asList(c.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (c != null)
c.close();
}
return ar;
}
public static String join(List<String> list, String delim) {
StringBuilder buf = new StringBuilder();
int num = list.size();
for (int i = 0; i < num; i++) {
if (i != 0)
buf.append(delim);
buf.append((String) list.get(i));
}
return buf.toString();
}
Next to Pentium10's excellent answer, here are some good examples from living code:
Android AOSP: com.android.providers.calendar.CalendarDatabaseHelper.java
Android AOSP: com.android.browser.BrowserProvider.java
OpenIntents Notepad: org.openintents.notepad.NotePadProvider.java
Thank you for clarifying that onUpgrade() will not support Remove/Drop statements #Pentium 10
For those of you who would like to know the exact moment when onUpgrade() gets called, it is during a call to either getReadableDatabase() or getWriteableDatabase().
To those who are not clear how it ensure it gets triggered...the answer is: It is triggered when the database version provided to the constructor of SqLiteOpenHelper is updated. Here is a example
public class dbSchemaHelper extends SQLiteOpenHelper {
private String sql;
private final String D_TAG = "FundExpense";
//update this to get onUpgrade() method of sqliteopenhelper class called
static final int DB_VERSION = 2;
static final String DB_NAME = "fundExpenseManager";
public dbSchemaHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
// TODO Auto-generated constructor stub
}
now to...onUpgrade()
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
sql = "ALTER TABLE " + fundExpenseSchema.Expense.TABLE_NAME + " ADD COLUMN " + fundExpenseSchema.Expense.FUNDID + " INTEGER";
arg0.execSQL(sql);
}
I've been using the solution proposed by #Pentium10 for a long time but today i had a problem, after doing alter table, getColumns from the original table still returns the same columns (in the new version of the db the table suffer mayor structure changes, some columns added some others), really i don't know why select statement does not reflect the structure changes, more over before creating my table again, select statement still returns the columns! When the table is not re-created yet!
So i manage solving this issue updating getColumns method using pragma table_info, like this:
/**
* Get a list of column base_dictionary for the selected table
*
* #param db
* Database that contains the table
* #param tableName
* Table name to be used
* #return A List of column name
*/
public static List<String> getColumns(SQLiteDatabase db, String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("pragma table_info(" + tableName + ")", null);
ar = new ArrayList<String>();
if (c != null && c.moveToFirst()) {
do {
ar.add(c.getString(c.getColumnIndexOrThrow("name")));
} while (c.moveToNext());
c.close();
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (c != null) c.close();
}
return ar;
}