Android - How can I pass data related to two tables to a the insert method of a Content Provider
Asked Answered
R

3

2

I need to insert data related to an Order and its corresponding Detail.

Without a ContentProvider I would do something like this:

public boolean insertOrder(Order order, ArrayList<OrderDetail> items) {
    boolean wasSuccessful = false;
    ContentValues cvOrder = new ContentValues();
    cvPedido.put(ORDER_CUSTOMER_ID, order.getCustomerId());
    cvPedido.put(ORDER_CUSTOMER_NAME, order.getCustomerName());     
    String insertQuery = "INSERT INTO " + ORDER_DETAIL_TABLE
            + " VALUES (?,?)";
    //...
    try {

        this.openWriteableDB();
        SQLiteStatement statement = db.compileStatement(insertQuery);
        db.beginTransaction();
        long idOrder = db.insertOrThrow(ORDER_TABLE, null, cvOrder);
        if (idOrder > 0) {


            for (int i = 0; i < items.size(); i++) {    
                OrderDetail detail=items.get(i);
                statement.clearBindings();
                statement.bindString(1, detail.getDescription);
                statement.bindDouble(2, detail.getPrice);
                //...
                statement.execute();
            }
            db.setTransactionSuccessful();
            wasSuccessful = true;               
        }

    } finally {
        db.endTransaction();
        this.closeDB();
    }
    return wasSuccessful;
}

The problem is that now I want to use a ContentProvider and I don't know what to do with this kind of cases where data about two or more tables must be passed to a single CRUD operation, knowing that a insert operation only accepts two parameters :

 @Override
    public Uri insert(Uri uri, ContentValues values) {

    }

What do you do in a ContentProvider when you have to insert relational data in a transaction?

Thanks in advance.

Reynalda answered 2/12, 2014 at 19:31 Comment(0)
A
3

You should use ContentProviderOperation. Since it's your ContentProvider you can assure that applyBatch() will execute all operations within a transaction. All standard content providers also ensure that that's the case.

See my blog post about ContentProviderOperation in general and my other post about how to use withBackReference() to access results of previous operations - which you need to access the orderId.

One important caveat: All ContentProviderOperations of one batch must use the same authority - but can use different URIs! In your case that should be no problem.

Abolition answered 8/12, 2014 at 19:54 Comment(3)
Sorry I didn't mark this as THE answer before, but as I had to get back to an IOS project and then other things came up, it wasn't until yesterday that I could returned to my android project and confirm that your solution was the correct answer.Reynalda
No problem. Thanks for keeping this in mind and upvoting me after so long a time! That should be worth a SO award on its own.Abolition
You really deserve it as you put me on the right track. Once again thank you very much and sorry if it took me so much time to mark this as the answer but since I'm learning Android in my free time I had to put off this personal project for a while. Finally in case someone find it useful here's an example of how to implement the applyBatch method in a Content Provider github.com/google/iosched/blob/master/android/src/main/java/com/…Reynalda
J
1

You can put all the data into a ContentValues and have a provider. You'll have to get a little creative with the order details.

Below psuedo code I create a key "DETAIL" on the fly with a integer then the item.

           ContentValues values = new ContentValues();
            values.put(ORDER_ID,orderid);

           for (int i = 0; i < items.size(); i++) {    
            values.put("DETAIL" + Integer.ToString(i),items.get(i));
            }

            Uri uri = context.getContentResolver().insert(
                    ORDER_URI, values);

Then in content provider you sort it out.

@Override
public Uri insert(Uri uri, ContentValues values) {

    int uriType = sURIMatcher.match(uri);
    SQLiteDatabase sqlDB = database.getWritableDatabase();
    long id = 0;
    switch (uriType) {
    case ORDER:
        // trim name and description
        trimNameDescriptions(values);
        try {
            id = sqlDB.insertOrThrow(ORDERS_TABLE,
                    null, values);
        Integer i =0;
        do (values.containsKey("DETAIL" + i.toString()){
        ContentValues v = new ContentValues();
        v.put("DETAIL",values.get("Detail" + i.toString()));
        v.put("ORDERID",id);
          //ACTUALLY CALL THE INSERT METHOD OF THE PROVIDER
        insert(DETAIL_URI,v);
         i+=1;
     }
Jonme answered 3/12, 2014 at 3:59 Comment(6)
So no transaction needed?Characteristically
Well you could tweak it some more and put a transaction in there. I was just showing how the ContentValues can be used to pass around information. If I was you I would go with the code in your question or modify the orders so it writes into a ContentValues.Jonme
Thank you @danny117. So ContentProviders are basically made to work with just one table at the time?Reynalda
Idk its worth a try. I have one view I query strictly for data in a provider the view makes it look like the code for a table query so it is more structured. If anything makes the code look prettty.Jonme
@eddy: No. You can work with multiple tables within one batch without problems. You just cannot cross authorities.Abolition
@WolframRittmeyer I like this apply batch method of the contentProviderOperation builder.Jonme
S
0

You can design content provider to mirror your SQLite tables, and use insertOrder code same as above.

Just use insert of content providers for each table(uri), to perform similar operations as in your insertOrder method

Another option is to define your content provider URI to take combination of your Order and items , and implement the parsing yourself in the content provider before committing to underlying data model.

Seamy answered 2/12, 2014 at 20:12 Comment(3)
Do you think you could give me an example?Reynalda
Sorry I don't think I get yout idea eitherCharacteristically
I think the idea is to use a special URI for that case like content://authority/orderwithdetails/orderId . Problem is: This wont work in this case since there are many details to insert and not just one.Abolition

© 2022 - 2024 — McMap. All rights reserved.