Android Cursor retrieve belongsTo relationshoips - android

I have two data models Item and Category with the relationship "Item belongs to Category"
Naturally my database has an Item.category_id column.
When I retrieve data using SQLiteDatabase#query() it returns a cursor with the data for the passed table name. How can it also retrieve related data (in this case, the category)?
PS: I'm trying to avoid rawQuery() but if that's the only way then...

This can be achieved with a regular Query. I'm making the assumption you have the id of the item and want to fetch the Category.
I'm also making the assumption you know what I mean by/how to create your own Contract & Provider, if you don't, look up database providers on the official docs or let me know
String[] args = { String.valueOf(myItemId) };
getContentResolver().query(MyCategoryContact.CONTENT_URI, null, MyCategoryContact.ITEM_ID + " = ?", args, null);

Related

Sqlite: SqliteDatabase.delete() vs a raw query

Conclusion: Android's database APIs work but the documentation is horribly incomplete.
I have recently run into a brain wrecking situation due to the flexibility Sqlite provides by not forcing you to specify the data type when creating the table. The problem was my mindset that assumed that every data type would be a general character sequence if not specified and therefore the way to talk to database is through java.lang.String.
But you can't blame me either when you see methods like the below:
int delete (String table,
String whereClause,
String[] whereArgs)
in the SqlDatabase class from Android docs.
I have a table consisting of Phone No(that I stored as java.lang.String) and Timestamp as a long field. When I tried deleting a record using this method, it just never got deleted despite countless debugging.
I checked everything and query was alright and table is existent and all the checklist until by chance, I discovered that removing the '' around the timestamp while querying in a raw manner instead of using the above method yields a successful deletion, something like this:
DELETE FROM messages_records_table WHERE messageTimestamp = 1508494606000;
instead of the following:
DELETE FROM messages_records_table WHERE messageTimestamp = '1508494606000';
or,
DELETE FROM messages_records_table WHERE messageTimestamp = "1508494606000";
Phone No isn't a problem; it's the timestamp that was creating the problem in INSERTION/DELETION
So, I tried running a raw deletion query with quotes removed(that are required with a string/varchar type) and it yielded successful deletion. I used the following method for this:
db.execSQL(String sql, Object[] whereArgs)
The key thing to notice here is that Object[] is different from String[] when compared to delete(). I passed a Long to Object to make it work but passing a Long.toString() in delete() seems to be useless.
So my question is, Is my analysis correct and delete() API is basically useless or have I missed some bigger picture..after all, it's provided by Android team carefully?
SQLite supports multiple data types; and while column types are not strictly enforced, values might be automatically converted in some cases (this is called affinity).
When your values are stored as numbers, you should access them as numbers, not as strings.
The Android database API does not allow you to use parameter types other than strings in most functions. This is a horrible design bug.
To search for a number, either use execSQL(), which allows you to use number parameters, or convert the string value back into a number:
db.delete(..., "timestamp = CAST(? AS NUMBER)",
new String[]{ String.valueOf(ts) });
The problem was my mindset that assumed that every data type would be
a general character sequence if not specified and therefore the way to
talk to database is through java.lang.String.
I think that's the real issue.
If you specify no type e.g.
CREATE TABLE mytable (col1,col2,col3)
Then according to Determination of Column Affinity(3.1) rule 3:-
3) If the declared type for a column contains the string "BLOB" or if no
type is specified then the column has affinity BLOB.
And then according to Section 3
A column with affinity BLOB does not prefer one storage class over
another and no attempt is made to coerce data from one storage class
into another.
I've personally never had an issue with delete. However I do have a tendency to always delete according to rowid.
Here's a working example usage that shows that delete isn't useless and is deleting according to a long. However the columns are all of type INTEGER :-
int pudeletes;
int sldeletes;
int rdeletes;
int pdeletes;
if(doesProductExist(productid)) {
// if not in a transaction then begin a transaction
if(!intransaction) {
db.beginTransaction();
}
String whereargs[] = { Long.toString(productid)};
// Delete ProductUsage rows that use this product
pudeletes = db.delete(
DBProductusageTableConstants.PRODUCTUSAGE_TABLE,
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete ShopList rows that use this product
sldeletes = db.delete(
DBShopListTableConstants.SHOPLIST_TABLE,
DBShopListTableConstants.SHOPLIST_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete Rules rows that use this product
rdeletes = db.delete(
DBRulesTableConstants.RULES_TABLE,
DBRulesTableConstants.RULES_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete the Product
pdeletes = db.delete(
DBProductsTableConstants.PRODUCTS_TABLE,
DBProductsTableConstants.PRODUCTS_ID_COL +
" = ?",
whereargs
);
// if originally not in a transaction then as one was started
// complete and end the transaction
if(!intransaction) {
db.setTransactionSuccessful();
db.endTransaction();
}
}

Android SQLite Update stops working after calling it two times

I have problems in updating rows in SQLite database in my Android application. It works successfully only, if I update it two times. But when I try to do it on the third time, it doesn't update the same row anymore.
LogCat doesn't show any exceptions. db.update() returns '1'.
I've searched similar issues on StackOverflow and the web. People advic]sed to remove db.close(); from database-helper, because I call it several times, or to use db.update method instead of db.rawQuery() or db.execSQL().
I also tested my query in SQLite client, and it works as it's supposed to.
Here is code of simple database-helper method:
public int updateEventDoneMark(Event event)
{
ContentValues args = new ContentValues();
args.put("completed", event.getCompleted());
return db.update("Event", args, "id" + "='" +event.getId() + "'", null);
}
Is there some SQLite-related issue I should know while I update one database entry several times in a row?
What does your content provider update and URI match look like?
Typical Content providers have a URI for each Table/View for a single row where _id is passed as a where_argument and a URI for multiple rows which uses where and where_arguments to select the rows to be updated.
Also it looks like you update by id. Android really want the id column named "_id", although I don't think is currently your issue, but it really depends on the URI it's using. Content Providers are usually coded with the _id and select by the column for a single row based on _id. That's why I want to see content provider. Your also selecting by the id yourself, this doesn't seem normal, although it could be accomplished, but not the norm. Typically the where part is something like 'colunm name = ?" and the next parameter where_arguments is a string array containing the value to replace the '?'.
Hope this helps.

Should my function to get an Android Contact by name return a Cursor or something else?

I'm working on a class that is supposed to handle the user's Android contacts and interact with an SQL database where you can move your phone's contacts to (the information in the database will be displayed as a ListView). I've made a function that is supposed to retrieve an Android contact by name and return all of that contact's information. My function is as follows:
public Cursor getContactByName(String name)
{
Uri uri = ContactsContract.Data.CONTENT_URI;
String[] projection = null;
String selection = Data.DISPLAY_NAME + "=?";
String[] selectionArgs = new String[]{name};
String sortOrder = null;
return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
}
So the idea is that this would be called by another function, which would first check if the contact existed. This function would return a cursor containing only that contact's information. First I want to know if I'm right in returning a Cursor containing the contact's information or should I instead create a class that stores this information and return an object of the class? Or perhaps I should return a string? Keeping in mind that what I'm returning is the contact that I intend to move to my database, I feel like returning a Cursor isn't what I should be aiming for. And as a side-question, is there any reason to use ContentResolver.query instead of managedQuery for my purposes?
It depends on what you are going to be using the data for. If you're going to be adding it to another database (as your question suggests) you are probably best off leaving it as a cursor and simply entering that cursor in your second database. If you like mapping your database rows to objects, then do that. Keep in mind Object Relational Mapping takes more memory and cycles but usually results in clearer code. It's really up to you as the programmer (sorry I know that's not what you want to hear).
Personally i would prefer to create an object Contact. So you are more flexible and you can also implement functions related to your objects in this class (or the related DAO).
Passing the values as Cursor or even as String gives more trouble than it will help you. In an object you can see exactly the propertynames for everything, with a cursor you have to know the names or the positions of the corresponding fields, and in a string you even have to parse your results.
EDIT:
As #DavidCowden metioned, it depends strongly on what you are trying to achieve. So my guess was, that you want to load your data and display it somewhere.
And as already mentioned, in this case i would prefer a seperate class as it's much cleaner and maintainable.

Distinct and substri in android with sqlite

I want get the first letter for all contacts without repetition i can't use something like this:
Cursor flc = this.cr.query(ContactsContract.Contacts.CONTENT_URI,
Proyection2, null, null, "UPPER("+ContactsContract.Contacts.DISPLAY_NAME+")");
Because Content providers don't allow functions in proyection with code this is explanation that i want:
"Select distinct substr(" + ContactsContract.Contacts.DISPLAY_NAME+",
1, 1) from "+ContactsContract.Contacts.CONTENT_URI "
I search a method for do this with content providers or accesing directly to bd but i don't know how.
Content providers are not a generic SQL interface; they support only those accesses that they have implemented, and they might not be based on an SQL database in the first place.
If you want to do any filtering or grouping not directly supported by a content provider, you have to do it yourself.

How to extract records in a sequential manner, from a table in Sqlite?

I have a table in Sqlite which contains three components for each record (eg A, ​​B, C), and the primary key (id) is random. I would like a SQL query that extracts each record sequentially, as if you were applying a loop to the database.
I know there is a clause like LIMIT but it only returns the first element (LIMIT 1) instead, two elements (LIMIT 2) ... but I want to extract a record, and process it and move on to another. Recalling that the id is random.
It is not ideal to write the queries by hand as methods have been provided to generate safe queries.
database.query(DATABASE_TABLE, new String[] {ROW_ID, ROW_COLUMN_ONE, ROW_COLUMN_TWO, ... }, null, null, null, null, new String[] {ROW_ID});
This will return all rows in your database ordered by the ROW_ID column.
This site might be of further use as it covers some common use cases for SQLite implementation.
http://www.vogella.com/articles/AndroidSQLite/article.html
What kind of processing?
If you want to update or alter the data in some way, you can probably do it within SQL, but it sounds like you have something more complicated in mind that just "UPDATE table SET value_column = value WHERE key_column = key".
If that's the case you will need to extract the records and iterate through them in java. Take a look at android.database.*.

Categories

Resources