7

I want to implement a ContentProvider that manipulates multiple tables. Here is what I tried so far. I wrote a Java Interface that represents the CRUD operations that every table should implement in its CRUD class.

public interface CRUDHandler {
    //UPDATE
    int update(Uri uri, ContentValues values, String selection,String[] selectionArgs);
    //READ
    Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) ;
    //CREATE
    Uri insert(Uri uri, ContentValues values);
    //DELETE
    int delete(Uri uri, String selection, String[] selectionArgs);
    //get Mime type
    String getType(Uri uri);
}

Then I wrote an abstract class that defines a static UriMatcher for the ContentProvider so each class that extends this class should add its Uri that identifies it and provide an implementation for each method in interface.

The class looks like this:

public abstract class  ApplicationCRUD  implements CRUDHandler{

    public static final UriMatcher sUriMatcher;

    static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    }
}

Further, I created a class for each table that extends this class and adds its Uri's to the UriMatcher from the abstract class.

Here is an example:

public class Table1CRUD extends ApplicationCRUD {
    //Setup Projection Map for Table1 
        private static HashMap<String , String>sTable1ProjectionMap;
        static {
            sTable1ProjectionMap.put(ApplicationProviderMetaData.Table1MetaData._ID, ApplicationProviderMetaData.Table1MetaData.TABLE_NAME+"."+ApplicationProviderMetaData.Table1MetaData._ID);
            sTable1ProjectionMap.put(ApplicationProviderMetaData.Table1MetaData.COL1, ApplicationProviderMetaData.Table1MetaData.TABLE_NAME+"."+ApplicationProviderMetaData.Table1MetaData.COL1);
            sTable1ProjectionMap.put(ApplicationProviderMetaData.Table1MetaData.COL2, ApplicationProviderMetaData.Table1MetaData.TABLE_NAME+"."+ApplicationProviderMetaData.Table1MetaData.COL2);
        }

        public static final int INCOMING_SINGLE_URI_INDICATOR = 5;
        public static final int INCOMING_COLLECTION_URI_INIDICATOR = 6;
    static {
        //standard URI 
        sUriMatcher.addURI(ApplicationProviderMetaData.AUTHORITY, "t1", INCOMING_COLLECTION_URI_INIDICATOR);
        sUriMatcher.addURI(ApplicationProviderMetaData.AUTHORITY, "t1/#", INCOMING_SINGLE_URI_INDICATOR);
        //here add your custom URI 

    }
    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

}

Now my ContentProvider looks like this:

public class ApplicationProvider  extends ContentProvider{
    //Define TAG FOR EACH CLASS FOR DEBUGGING 
    private static final String TAG = "ApplicationProvider";
    // Application CRUD Handlers use to support multiple tables inside the content provider
    private static Table1CRUD table1CRUD;
    private static Table2CRUD table2CRUD;

    static {
        table1CRUD = new Table1CRUD();
        table2CRUD= new Table2CRUD();

    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int uriMatchResult = ApplicationCRUD.sUriMatcher.match(uri);
        //case Table1
        if(uriMatchResult == Table1CRUD.INCOMING_COLLECTION_URI_INIDICATOR ||uriMatchResult == Table1CRUD.INCOMING_SINGLE_URI_INDICATOR )
        {
            return table1CRUD.delete(uri, selection, selectionArgs);
        }
        case Table2
        else if(uriMatchResult == Table2.INCOMING_COLLECTION_URI_INDICATOR|| uriMatchResult ==Table2.INCOMING_SINGLE_URI_INDICATOR){
            return table2CRUD.delete(uri, selection, selectionArgs);
        }

        else{
            throw new IllegalArgumentException("Unknown Uri "+uri);
        }
    }

Now I am using the SQLiteOpenHelper as a private class in the ContentProvider and I define an instance of it in my ContentProvider. Should I modify the interface and supply each CRUD method that object Dependency Injection to let each function use it to access the database? I also want to know what you think about this approach: Is it good enough? Does it make the thing I am trying to do efficiently? What modification can I do to improve this design?

JJD
  • 50,076
  • 60
  • 203
  • 339
confucius
  • 13,127
  • 10
  • 47
  • 66
  • 4
    Do **NOT** spam unrelated questions with comments that are merely links here. Somebody with your experience on StackOverflow should know better. – CommonsWare Mar 27 '12 at 12:53

3 Answers3

10

The Google IO Schedule application is a good resource if you are need to design a ContentProvider with multiple tables. It sounds like you want to do something a bit more complicated, but maybe it will help.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • Sorry to bother you,but I've been trying to find any example on how to handle CRUD operations when you have master-datail tables, e.g., author-books, order-detail, but unfortunately I haven't been able to find anything. The ContentProvider's `insert` method is an example , it only takes two parameters : Uri and `ContentValues`. I don't know what to do in this case as I clearly need to pass two `URI`s and perhaps something more elaborate than a single `ContenteValues` object. Do you know that any resource I can use as a guide? – eddy Dec 07 '14 at 15:33
2

I have gone through the opensource project, in which they have used multiple tables. You can look at the various projects of OpenIntents and here is a similar question.
I hope, it can help you .

Community
  • 1
  • 1
dharmendra
  • 7,835
  • 5
  • 38
  • 71
2

Why don't you make multiple tables under a content provider this way.

Suppose you have two tables named A and B, their URI's would be AUTHORITY/A, AUTHORITY/B

private static final int TableOne = 1; 
private static final int TableTwo = 2;

private static final UriMatcher uriMatcher;
static{
       uriMatcher.addUri(AUTHORITY,"A",TableOne);
       uriMatcher.addUri(AUTHORITY,"B",TableTwo);
     }

Now you can implement the overridden methods using a standard switch case , and mapping correctly to the underlying SQLite Database class's methods.

jaymeht
  • 678
  • 1
  • 8
  • 14