Android - Complex Databases and Content Providers - android

I am designing an Android app with a fairly complex (normalized) database structure. My question is, what is the best design pattern for working with my data?
The database has a table Books, a table Authors, a table Collections (of books), and a junction table Books_Collections that relates the two based on an integer ID. I say that it is fairly complex because I want to display a list of books & authors in a specific a collection in a ListView in format "Book Title (Author Name)", so I need to be able to perform the following query (I will abbreviate the table names A, B, C, and B_C):
SELECT B.title, A.name
FROM B
JOIN A ON B.author_id = A.id
JOIN B_C ON B_C.book_id = B.id
WHERE B_C.collection_id = [variable]
I have seen some people advising developers to wrap their Sqlite databases in a Content Provider to easily take advantage of the Loaders introduced in Honeycomb. But is this really feasible for such complex queries? (If so, how?) or would it be better to just use DAO since I am not sharing my data with other apps? And if using DAO, should a custom AsyncTaskLoader be created to connect the data to a ListView?
EDIT
One more important detail: I want to change the background color of the list items based on a boolean variable in Books marking whether or not the book has been read, and a context menu will allow users to delete books from the list, so the list needs to adjust instantly to data changes.

Yes, is doable and easily done
I'm no expert but im followin the pattern used by google on his Google I/O app
https://code.google.com/p/iosched/
Check the provider package where u have the 3 classes needed for using the pattern
ScheduleDatabase.java
(definition of sqlite3 database)
ScheduleProvider.java
(Content provider atttached to this database)
ScheduleContract.java
(Contract defined to expose the provider)
Personally, in the case of join
in the Database File i define
interface Tables {
String TABLE1= "table1";
String TABLE2= "table2";
// JOINS
String TABLE1_JOIN_TABLE2 = "table1"
+ "LEFT OUTER JOIN table2 ON table1.table_id= table2.tableid";
}
and then in your provider map a provider entity to this join.

I ended up using database helpers/adapters and creating a custom AsyncTaskLoader. I wasn't able to find a ton of examples of custom ones, and there's not great documentation on it, but it's not too hard to figure out. Works like a charm.

Related

Android ROOM - How to Insert a Object inside an Object (i.e. Flash Card Deck has a List of Questions)

As per the title,
I have been confused on how I would implement the following:
A FlashCard deck has a title, due date (both have been implemented).
However, it also contains a List of Cards.
A Card is made up of a question and answer.
Like this, Deck(title, duedate, flashcards). Cards(question, answer)
I have been thinking about using a dedicated typeconverter for this. However,
it can also get messy.
For example,
I was thinking of having a type converter which collects all the existing questions and answers into a concatonated csv string. Then it will deconvert this when transitioning back into the object.
This method seems quite complicated. Therefore, I was wondering what is the best practice per se, for this sort of thing.
Thanks...
I do think your problem is a database schema problem. So basically there are 2 solutions depending on how you want to use the cards.
1. If the card does not repeat in any deck or if each and every card is unique:
Then you can use this schema:-
For finding all cards for a particular deck say suppose "Deck 1" whose id is 1. You can use the SQL query "SELECT * FROM Cards WHERE Deck_id=1;"
Pros:
You will have to maintain only two tables.
Probably easy to write SQL Queries.
Cons
You cannot reuse any cards. All cards will be unique.
May require more storage space.
2. If the card repeats, or if you want to reuse a card:
Then you can use this schema:-
For finding all cards for a particular deck say suppose "Deck 1" whose id is 1. You can use the SQL query "SELECT * FROM Cards WHERE id IN(SELECT Cards_id FROM Deck_has_Cards WHERE Deck_id=1);"
Pros:
Saves a lots of storage space(if cards are not unique).
Cons
Queries become very hard to write
I will recommend you to check for better SQL queries than those I have written.
If you are using Room Database use #Query(<SQL>)...(I am not expanding on this many tutorials are available).
I will not recommend the way you are trying to store the cards as it will be not working with the A.C.I.D. properties of the database.

Android Content Provider SQLite Junction Tables

I'm implementing a Content Provider, which is backed by a fairly complex SQLite DB schema. The database has a few junction tables and I'm unsure whether they should be visible to the user of the Content Provider or not.
The parent tables are exposed via the Contract, each one has its own content URI, etc. Now, when inserting data via ContentResolver#applyBatch() method, I create ContentProviderOperation per each table's content URI. So far everything is clear. But my problem is, how should the junction tables be populated, as they don't have their own content URIs?
To illustrate this, here's an example. I have 2 "parent" tables, Movies and Actors. The relationship between them is many-to-many and therefore I have a junction table called MoviesActors.
To insert at one batch I do the following:
List<ContentProviderOperation> operations = new ArrayList<>;
// movie
operations.add(ContentProviderOperation.newInsert(Contract.Movie.ContentUri).withValue("movie_id", "23asd2kwe0231123sa").build());
// actor
operations.add(ContentProviderOperation.newInsert(Contract.Actor.ContentUri).withValue("actor_id", "89asd02kjlwe081231a").build());
getContentResolver().applyBatch(authority, operations);
The junction table MoviesActors should be inserted with a row containing movie_id and actor_id. How do I take care of the junction table in this situation?
The only thing, which comes to my mind is extend the Contract to have content URI pointing to the junction tables and add another ContentProviderOperation, since otherwise, how do you communicate movie_id and actor_id to ContentProvider#applyBatch()?
I rather not expose the junction table to the user of the ContentProvider, but I might be wrong here... perhaps that's how it should be done on Android?
I've searched this topic for days already and haven't found an answer to that.
Any help would be greatly appreciated.
Bonus question:
Is it necessary to expose every single table via the Contract? For instance, when having child tables in one-to-many relationship. I'm specifically referring to Insert/Update/Delete since I know with Query I can simply do a join, but maybe I'm wrong also here.
Thanks a lot!
NOTE: I'm not interested in 3rd party library solutions.
I think you're tackling the problem from the wrong end. You're trying to design an interface to match your database structure, but the interface should come first.
In the first place, the interface should meet all the requirements of your ContentProvider client. If your ContentProvider client needs access to the junction table you'll have to expose it (in some way, see below), otherwise you don't have to. A good interface hides the actual implementation details, so the ContentProvider client doesn't need to care about whether the ContentProvider is backed by an SQLite database, by a bunch of in-memory maps or even a web-service.
Also, you should not think of a ContentProvider just as an interface to a database and the Contract as the database schema. A ContentProvider is much more versatile and powerful than that. The major difference is that ContentProviders are addressed by URIs whereas in SQL you just have table names. In contrast to a table name, a URI has a structure. URIs have a path that identifies the object (or directory of objects) that you want to operate on. Also you can add query parameters to a URI to modify the behavior of an operation. In this respect a ContentProvider can be designed much like a RESTful service.
See below for a concrete (but incomplete) example of a Contract of a simple movie database. This is basically how one would design a RESTful web-service, except for one thing: Just like in your code, movie-id and actor-id are provided by the caller. A real RESTful service would create and assign these automatically and return them to the caller. A ContentProvider can only return long IDs when inserting new objects.
Insert a new movie
insert on /movies/
Values: {"movie_id": <movie-id>, "title": <movie-title>, "year": ...}
Insert a new actor
insert on /actors/
Values: {"actor_id": <actor-id>, "name": <actor-name>, "gender": ...}
Add an existing actor to a movie
insert on /movies/movie-id/actors/
Values: {"actor_id": <actor-id>}
Add an existing movie to an actor:
insert on /actors/actor-id/movies/
Values: {"movie_id": <movie-id>}
Optional: add a new actor directly to a movie:
insert on /movies/movie-id/actors/
Values: {"actor_id": <actor-id>, "name": <actor-name>, "gender": ... }
If no actor with the given id exists, this operation will create the new actor and link it to the movie in a single step. If an actor with this ID already exists an exception would be thrown.
The same could be done the other way round, adding a new movie to an actor.
Delete an actor from a movie
delete on /movies/movie-id/actors/actor-id
or
delete on /actors/actors-id/movies/movie-id
Get all movies
query on /movies/
Get a specific movie
query on /movies/movie-id
Get all actors playing in a specific movie
query on /movies/movie-id/actors/
Get all movies a specific actor has played in
query on /actors/actor-id/movies/
The optional query selection statement can be used to filter the result. To get movies from the last 10 years a specific actor has played in, you would add the selection movies_year>=2005 to the last query.
By using a contract like this you wouldn't expose the junction table, instead you provide a REST-like interface to your database.
The job of the ContentProvider is to map these operations onto the database or any other back-end.

LoaderManager/CursorLoader for several cursors?

I have two SQLite tables:
Service: sid (prim.key), workDesc, title
ServiceInstance: ssid (prim.key), sid(foreign key), date, workComment, odometer
Today I'm using a CustomAdapter extending SimpleCursorAdapter to feed a ListView with data from these tables. But since this method is deprecated, I want refactor the code using LoaderManager/CursorLoader instead.
Now, my confusion is that in the ListView, I want to show the date from the ServiceInstance table and the title from the Service table. Like:
2013-06-05 Regular service
How can I do that? Since now when I'm using my extended SimpleCursorAdapter-class, I'm sending a cursor to the constructor. But in this case when I have changed the database structure a bit, I need to show data from two different cursors (Service and ServiceInstance).
I have googled and read a couple of tutorials but not found any similar case. But here, http://www.mysamplecode.com/2012/11/android-database-content-provider.html
Where they declare the string array columns (step 6, MainActivity.java:58), can I just add the column names (date and title) even though they exist in different tables?
Another question, I don't plan to provide these data to other apps, so is it a meaning to create a ContentProvider anyway?
it shouldn't be necessary to bind to two different cursors in your case (and i wouldn't recommend it, since you would have to write your own adapter in that case).
Try to query across your tables instead, check this for a starting point:
SQLite query from multiple tables using SQLiteDatabase
how to use join query in CursorLoader when its constructor does not support it
For your second question, if you don't want to expose your ContentProvider to other applications set:
<provider
....
android:exported="false">
</provider>
in your manifest

Sqlite advanced search

I'm developing an android application for a store, which provides many functions. One of these is a function that allows the customer to search a product with some criteria (price,size,type... like in the picture ).
I guess I should work with SqliteDatabase , but I have no idea how I can make this multi-criteria search interface , so the user can query the database.
It's simple. after setup your database, you can use SQL queries and JOIN types with WHERE critaria and finally achive proper data, use Cursor class to iterate through results.
see:
http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
http://www.w3schools.com/sql/sql_where.asp
http://www.w3schools.com/sql/sql_and_or.asp
http://www.w3schools.com/sql/sql_join_inner.asp
Before handling it, you should design your database properly.
Suppose,
List of tables -
1) product_type(_id<should be primary key to link another table>,
type_id<integer>, type_name<text>
2) product_price(_id<should be primary key to link another table>,
type_id<integer>, price<double>
3) product_type_size(_id<should be primary key to link another table>, type_id<integer>, size<text>
And create views as your requirement -
product_search(join your table properly)
And run your queries as requirement and match with the value of the view.
Go ahead--

How to organize sqlite database

this is more of a question of theory than anything else. I am writing an android app that uses a pre-packaged database. The purpose of the app is solely to search through this database and return values. Ill provide some abstract examples to illustrate my implementation and quandary. The user can search by: "Thing Name," and what I want returned to the user is values a, b, and c. I initially designed the database to have it all contained on a single sheet, and have column 1 be key_index, column 2 be name, column 3 be a, etc etc. When the user searches, the cursor will return the key_index, and then use that to pull values a b and c.
However, in my database "Thing alpha" can have a value a = 4 or a = 6. I do not want to repeat data in the database, i.e. have multiple rows with the same thing alpha, only separate "a" values. So what is the best way to organize the data given this situation? Do I keep all the "Thing Names" in a single sheet, and all the data separately. This is really a question of proper database design, which is definitely something foreign to me. Thanks for your help!
There's a thing called database normalization http://en.wikipedia.org/wiki/Database_normalization. You usually want to avoid redundancy and dependency in the DB entities using a corresponding design with surrogate keys and foreign keys and so on. Your "thing aplpha" looks like you want to have a many-to-many table like e.g. one or many songs belong/s to the same or different genres. You may want to create dictionary tables to hold your id,name pairs and have foreign keys referencing these tables. In your case it will be mostly a read-only DB so you might want to consider creating indexes with high FILLFACTOR percentage don't think sqlite allows it to do though. There're many ways to design the database. Everything depends on the purpose of DB. You can start with a design of your hardware like raids/file systems/db block sizes to match the F-System's block sizes in order to keep the I/O optimal and where to put your tablespaces/filegroups/indexes to balance the i/o load. The whole DB design theory/task is really a deep subject which is not to be underestimated nor is a matter of few sentences in the answer of stackoverflow. :)
without understanding your data better here is my guess at what you are looking for.
table: product
- _id
- name
table: attribute
- product_id
- a

Categories

Resources