1

When using Firebase to store and retrieve objects (POJOs) created by the user (for example: posts or comments), it becomes necessary to pass these objects around the application. But what is the suggested way to keep track of the associated DatabaseReference, location or unique key in the database for this object?

Example scenario

A simple to do list app allows the user to freely add, edit and remove items in their list. So when the user creates an item, something similar to the below would happen:

private Item storeItem(String title) {
    String key = mDatabase.child("items").push().getKey(); // Where do we keep this key?
    Item item = new Item(title);

    mDatabase.child("items").child(key).setValue(item);

    return item;
}

Where Item is this Java object:

public class Item {
    private String title;
    private String description;

    public Item() {}

    public Item(String title) {
        this.title = title;
    }

    // ...
}

Behind the scenes, this item is added to a RecyclerView, either by inserting the returned Item to the adapter or when a ChildEventListener attached to the "items" reference is fired.

The user then wishes to rename this new item or add text to the description field, so tapping on it in the RecyclerView starts a separate Activity which receives the passed Item and uses getters/setters to make changes.

Now, we'll need to save these changes to the database, which we can do by calling setValue() again, as above. However, we didn't store the key variable from storeItem() so we don't actually know where this item is currently stored in the database.

So, where can we keep track of the created item's key for later use to save changes back to the database?

Possible solutions

There are a number of different paths we could take here, but I'm looking for some guidance on the suggested method, as the Firebase documentation doesn't mention this hurdle. I've outlined some examples that I can think of:

  • Store the key inside the object. We could add another field to the Item object to store the database key. So within the previous storeItem() method, the key variable is added to the Item constructor and stored in the database as a field.
  • Create a wrapper object. We could wrap the Item object in a container that has methods such as getItem() and getKey() or getDatabaseReference() and then pass this around the app instead of the Item itself.
  • Use the DataSnapshot instead. Once the item is created, wait until an attached listener receives it, then use and pass around the retrieved DataSnapshot, which has methods for getKey() and getRef().
  • Retrieve the object every time it is needed. Instead of passing Item around the app, we could retrieve it from the database every time it is needed, by using the key or DatabaseReference.

Wrapping up

Looking back on this huge question, it seems I might have overcomplicated it a little, but I wanted to be thorough in my explanation. I'm also hoping that it's not purely opinion-based and there currently is some standard way to achieve this.

So I guess my question is: is there a standard method to handle and make changes to Java objects stored in Firebase?

Grimthorr
  • 6,856
  • 5
  • 41
  • 53
  • 1
    I always store the key inside the object, except I call it id instead of key. A unique id often comes in handy anyway! – Incinerator Oct 11 '17 at 14:05

1 Answers1

2

Most developers I see struggling with this end up storing the key inside the Java objects too. To prevent it being duplicated in the JSON, you can annotate it in the Java class:

public class Item {
  private String title;
  private String description;
  @Exclude
  public String key;

  public Item() {}

  public Item(String title) {
    this.title = title;
  }

  // ...
}

See: Is there a way to store Key in class which I cast from Firebase object?

My personal preference in such cases is to keep the DataSnapshot around. The main disadvantage I see in that is that the information on the object-type of the snapshot is spreading out over my code since this exists in multiple places:

snapshot.getValue(Item.class);

I've been lobbying to generify the DataSnapshot class so that it'd become DataSnapshot<Item>, which would solve that problem. I think that is currently being considered in the Firestore SDK for JavaScript/TypeScript.

But lacking such a solution for the Android SDK for the Realtime Database, you're probably better off with the first approach: storing the key inside the Java objects.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks @FrankvanPuffelen. I forgot about `@Exclude` but using it for this makes perfect sense. I've been using option 2 and wrapping the objects with a generic container, which also opens up the ability to add methods such as `save()` to directly save changes back to the database. Generifying `DataSnapshot` is a great idea and would be similar to using a container in some ways. – Grimthorr Oct 11 '17 at 15:05
  • Yeah, if I had to do bigger projects, I'd probably go for such a DTO too. And indeed: a generified `DataSnapshot` would mostly obsolete that, although your `save()` method would still fit there. – Frank van Puffelen Oct 11 '17 at 15:28
  • Ok great! So there's not currently a standard method but rather managing it is mostly down to developer preference and scalability. Storing the key inside the object is probably the easiest though. Thanks again for the answer, I'll accept this if there's nothing further. – Grimthorr Oct 11 '17 at 16:56