Keeping track of an object's location in Firebase

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?

1 answer

  • answered 2017-10-11 10:23 Frank van Puffelen

    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.