Android - Using ORMLite DAO as a ContentProvider
Asked Answered
C

1

7

I have seen pattern C in the Google IO presentation and I am very anxious to implement this pattern. However, I do really like the ORMLite library and would like to use this library in my application as well.

When I say Google IO presentation I mean this one: https://www.youtube.com/watch?v=xHXn3Kg2IQE by Virgil Dobjanschi.

Now I have been searching a lot for an implementation that shows me how to use ORMLite in conjunction with Contentproviders.

Now my problem here is that the ORMLite DAO is conflicting with the Contentprovider. They essentially do the same and are a pain to integrate into each other. (Using Ormlite in Conjunction with Android's Content Provider others discussing this and agreeing upon this claim.)

A few libraries have implemented ORMLite into the contentprovider API pattern, one example is: https://github.com/blandware/android-atleap

However, underwater they still revert the model to ContentValues (simple types).

Android - Using Dao Pattern with contentProvider This question is similair to my situation but 3 years ago and I'm suggesting an alternate solution below.

@jcwenger's answer is very useful, but I was wondering if anything has changed in the past 3 years. I'm facing the same issue and perhaps now since ORMLite has matured, it's more rewarding to use ORMLite?

My colleague next to me really, really wants to use ORMLite since he doesn't want to have to write any mapping himself. I know of the existance of the atleap and Android-OrmLiteContentProvider projects. These only provide a cursor to the activity and my colleague want to have lists of models or a single model. Can this be achieved?

My colleague suggests writing my own implementation of the Cursor, SyncAdapter? and Contentprovider (has to be done regardless) to work with models. However can the same functionality still be achieved with lists etc? Passing events to the activity to contentobservers etc?

Is this viable?

Edit We'll most likely use the contentproviders privately. We do not need to expose these contentproviders. However the advantages that contentproviders provide are great. How else could I notify my GUI to update when the data has changed?

I also have to display data from multiple tables (joins and other data, not contained in the same table) in one activity and download images etc.

Clint answered 30/5, 2014 at 9:26 Comment(6)
you can use a MatrixCursor if you don't want to create a Cursor from the scratchCasandra
Thanks for the advise. I've checked MatrixCursor but it's implementation is not quite what I am looking for. I've come to the conclusion that I'll have to write my own implementation of a Cursor and corresponding components.Clint
what's wrong with it? have you seen a Cursor interface? it has more or less 40 methods to implement... why to do that from the scratch?Casandra
Well we'll not use those 40 methods anyway. MatrixCursor also forces us to use an implementation that we can, but with a lot of effort, tailor to our requirements. However, MatrixCursor just doesn't fit in our solution.Clint
Cursor is an interface so you will have to implement all of them, unless you use some its concrete implementations like MatrixCursor for example :) i use it all the time i need a Cursor that doesn't come from sqlite queryCasandra
good question, we are also looking for better way to show data in UI, with new ORM technologiesOverton
C
3

So since I couldn't find a proper answer, this is how I solved it after a while of trying:

public class CardProvider extends ContentProvider {

private InternalDatabase dbhelper;
private RuntimeExceptionDao<Card, UUID> cardDao;

/**
 * Content authority for this provider.
 */
private static final String AUTHORITY = CardUris.CONTENT_AUTHORITY;

// The constants below represent individual URI routes, as IDs. Every URI pattern recognized by
// this ContentProvider is defined using sUriMatcher.addURI(), and associated with one of these
// IDs.
//
// When a incoming URI is run through sUriMatcher, it will be tested against the defined
// URI patterns, and the corresponding route ID will be returned.
/**
 * URI ID for route: /cards
 */
public static final int ROUTE_CARDS = 1;

/**
 * URI ID for route: /cards/{ID}
 */
public static final int ROUTE_CARDS_ID = 2;

/**
 * UriMatcher, used to decode incoming URIs.
 */
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
    sUriMatcher.addURI(AUTHORITY, "cards", ROUTE_CARDS);
    sUriMatcher.addURI(AUTHORITY, "cards/*", ROUTE_CARDS_ID);
}

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

@Override
public String getType(Uri uri) {
    final int match = sUriMatcher.match(uri);
    switch (match) {
        case ROUTE_CARDS:
            return CardUris.CONTENT_CARDS;
        case ROUTE_CARDS_ID:
            return CardUris.CONTENT_ITEM_CARD;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }
}

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

@Override
public boolean onCreate() {
    dbhelper = OpenHelperManager.getHelper(getContext(), InternalDatabase.class);
    cardDao = dbhelper.getRuntimeExceptionDao(Card.class);
    return true;
}

@Override
public Cursor query(Uri uri, String[] arg1, String arg2, String[] arg3,
        String arg4) {

    int uriMatch = sUriMatcher.match(uri);
    switch (uriMatch) {
        case ROUTE_CARDS_ID:
            /*String id = uri.getLastPathSegment();
            Card card = null;
            try {
                card = cardDao.queryBuilder().where().eq(Entry.ID_FIELD_NAME, id).queryForFirst();
            } catch (SQLException e) {
                e.printStackTrace();
            }*/
            //return null;
        case ROUTE_CARDS:
            // Return all known entries.
            // Note: Notification URI must be manually set here for loaders to correctly
            // register ContentObservers.
            // build your query
            QueryBuilder<Card, UUID> qb = cardDao.queryBuilder();

            // when you are done, prepare your query and build an iterator
            CloseableIterator<Card> iterator = null;
            Cursor cursor = null;
            try {
                //qb.query();
                iterator = cardDao.iterator(qb.where().eq("relevant", 1).and().eq("removed", false).prepare());
               // get the raw results which can be cast under Android
               AndroidDatabaseResults results =
                   (AndroidDatabaseResults)iterator.getRawResults();
               cursor = results.getRawCursor();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
               //iterator.closeQuietly();
            }
            cursor.setNotificationUri(this.getContext().getContentResolver(), uri);
            return cursor;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }

}

@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
    // TODO Auto-generated method stub
    return 0;
}

}

You could probably give purpose to the insert, update and delete methods, but the dao does this too and is what I am using.

Clint answered 21/7, 2014 at 14:39 Comment(5)
+1 currently integrating ormlite with ContentProviders as well - and your post is nice food for thought!Nicodemus
Glad it was able to help someone. Personally I would have abandoned ORMLite and would have written the queries myself, but a colleague likes ORM's for the ease of writing and reading from the database. I have to agree that it's easy to use when doing basic queries, but find that it's going against Android components such as the ContentProvider since it does what the ORMLite DAO does, and more. I would say I'm forced to use ORMLite, but in future endeavours, I'd stay away from it. Most of the time it's just too tempting to quickly use a DAO.Clint
I second what you said - its damn convenient when you dont need to use content providers. Unfortunately I need to use them and only them which makes ormlite seem kinda useless for this use-case but it still takes a lot of stuff off my mind like mapping data to their DAOs - and I dont really need additional queries, since my setup is quite simple. But I guess if it wasn't, I might not go for ormlite when forced to use content providers. Thanks again!Nicodemus
But, if you don't update the data through the ContentProvider (insert, update, delete), the cursor won't get updated, right?Registration
In my situation I update the data from more than one location, since I get data from multiple sources. All I have to do to have the ContentProvider update is call the method to notify the contentresolver and the ContentProvider will call the query method.Clint

© 2022 - 2024 — McMap. All rights reserved.