What are the semantics of withValueBackReference?
Asked Answered
E

1

42

I cannot figure out the exact semantics of withValueBackReference.

I've read the example code (for example the code which adds a new contact) using this method, providing a backReference value of 0. What does this mean?

The documentation says:

A column value from the back references takes precedence over a value specified in withValues(ContentValues)

Electrochemistry answered 11/1, 2011 at 7:44 Comment(1)
I found this discussion on SO - it talks about using the withValueBackReference method in a case where the objective is to save both the master and details records in a single operation. However, I still dont understand how the back reference value of 0 figures here! #3225357Electrochemistry
S
170

This question relates to batch operation on a content provider. The example is modified from this related question.

When creating a batch of operations first create a list of operations to perform using:

ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();

then apply them to the content provider using the applyBatch method.

ContentProviderResult[] results = this.getContentResolver().applyBatch(FooBar.AUTHORITY, operations);

That is the basic concept so let's apply it. Suppose we have a content provider which handles uris for Foo records and some child records called Bar.

content://com.stackoverflow.foobar/foo

content://com.stackoverflow.foobar/foo/#/bar

For now we'll just insert 2 new Foo recordscalled "Foo A" and "Foo B", here's the example.

ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();

//add a new ContentProviderOperation - inserting a FOO record with a name and a decscription
operations.add(ContentProviderOperation.newInsert(intent.getData())
    .withValue(FOO.NAME, "Foo A")
    .withValue(FOO.DESCRIPTION, "A foo of impeccable nature")
    .build());

//let's add another
operations.add(ContentProviderOperation.newInsert(intent.getData())
    .withValue(FOO.NAME, "Foo B")
    .withValue(FOO.DESCRIPTION, "A foo of despicable nature")
    .build());

ContentProviderResult[] results = this.getContentResolver().applyBatch(FooBar.AUTHORITY, operations);

Nothing special here, we are adding 2 ContentProviderOperation items to our list and then applying the list to our content provider. The results array filled with the id's of the new records that we have just inserted.

So say we wanted to do something similar but we also want to add some child records into our content provider in one batch operation. We want to attach the child records to the Foo records we just created. The problem is we don't know the id of the parent Foo records because the batch has not been run. This is where the withValueBackReference helps us. Let's see an example:

ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();

//add a new ContentProviderOperation - inserting a Foo record with a name and a decscription
operations.add(ContentProviderOperation.newInsert(intent.getData())
    .withValue(FOO.NAME, "Foo A")
    .withValue(FOO.DESCRIPTION, "Foo of impeccable nature")
    .build());

//let's add another
operations.add(ContentProviderOperation.newInsert(intent.getData())
    .withValue(FOO.NAME, "Foo B")
    .withValue(FOO.DESCRIPTION, "Foo of despicable nature")
    .build());

//now add a Bar record called [Barbarella] and relate it to [Foo A]
operations.add(ContentProviderOperation.newInsert(intent.getData()
    .buildUpon()
    .appendPath("#") /* We don't know this yet */
    .appendPath("bar")
    .build())
.withValueBackReference (BAR.FOO_ID, 0) /* Index is 0 because Foo A is the first operation in the array*/
.withValue(BAR.NAME, "Barbarella")
.withValue(BAR.GENDER, "female")
.build());

//add a Bar record called [Barbarian] and relate it to [Foo B]
operations.add(ContentProviderOperation.newInsert(intent.getData()
    .buildUpon()
    .appendPath("#") /* We don't know this yet */
    .appendPath("bar")
    .build())
.withValueBackReference (BAR.FOO_ID, 1) /* Index of parent Foo B is 1*/
.withValue(BAR.NAME, "Barbarian")
.withValue(BAR.GENDER, "male")
.build());

ContentProviderResult[] results = this.getContentResolver().applyBatch(FooBar.AUTHORITY, operations);

So the withValueBackReference() method allows us to insert the related records before we know the id of the parent we want to relate them to. The back reference index is simply the index of the operation that will return the id we want to look for. It is perhaps easier to think of it in terms of which result you would expect to contain the id. e.g results[1] would contain the id for "Foo B" so the index we use to back reference to "Foo B" is 1.

Stribling answered 19/7, 2011 at 0:30 Comment(8)
This is a great answer. I think it would make it even more clear to add a sentence or to explaining what "intent.getData()" actually points to in your example. If I am reading the code correctly this is the content URI (operation type) and in your example could be interchanged with BAR.FOO_ID. That way, the system allows you to specify the "Nth result to the operation of type BAR.FOO_ID". Am I right about that?Adventist
In this example intent.getData() is equivalent to Uri.parse("content://com.stackoverflow.foobar/foo").Stribling
Are you sure the replacement of the # sign in content://com.stackoverflow.foobar/foo/#/bar with the id of the master record actually works? I tried it and it seems that it does not. If I look into the source of ContentProviderOperation it seems that the backreferences are only applied to the ContentValues, not the URI.Hugo
Merlin, I have the same question as @aha. I haven't had the chance to take a very deep look at the code he pointed to, so I can't tell if that particular implementation has this issue or not. In any case, I'm more concerned about the contract. What is the contract here?Jadotville
This will be working only in case of master-details relation? I've tried to do the same but without master-detail structure, the problem is the withValueBackReference gives back the first table's name..Atman
@Atman that is the default use case, but you might be able to find more imaginative uses for it.Stribling
What about in situation with update where you are supposed to already have the RAW_CONTACT_ID, what does the second parameter relate to then?Eurythmic
If you already know the parent ID then you just use withValue(...) instead of withValueBackReference(..., index) so there is no second parameter.Stribling

© 2022 - 2024 — McMap. All rights reserved.