Changes

Writing Firebase Realtime Database Data

10,582 bytes added, 17:14, 15 August 2017
Created page with "Now that the basics of the Firebase Realtime Database have been covered, the next step is to explore the ways in which data can be written to a database tree from within an An..."
Now that the basics of the Firebase Realtime Database have been covered, the next step is to explore the ways in which data can be written to a database tree from within an Android app. This chapter will provide details on how to write and delete database tree nodes and also outline some techniques for handling database write errors.

== Obtaining the Database Reference ==

Before any tasks can be performed on a database, a reference to that database must first be obtained. Each Firebase project has its own dedicated realtime database, details of which can be reviewed by opening the project within the Firebase console and selecting the Database option. Within the console, panels can be selected to view the current data trees stored in the database, the rules configured for access, database usage statistics and database backups:


[[File:]]

Figure 20-1


In the above example, a basic data tree is shown containing nodes for address, name and phone values. At the top of the tree is an entry which reads peak-apparatus-599. This is the unique identifier assigned to the Firebase project and serves as the root element (/) for the database tree.

From the point of view of an Android app, a Firebase database is represented by an instance of the FirebaseDatabase class, a reference to which is obtained via a call to the getInstance() method of the class, for example:

<pre>
FirebaseDatabase database = FirebaseDatabase.getInstance();
</pre>

The FirebaseDatabase instance can then be used to get a database reference. This is a reference to a specific point within the database tree at which read and write operations may be performed. A database reference is represented by an instance of the DatabaseReference class and is obtained via a call to the getReference() method of the FirebaseDatabase instance:

<pre>
DatabaseReference dbRef = database.getReference();
</pre>

When the getReference() method is called without arguments, the path for the reference within the database is set to the root of the tree. Any write operations performed using this reference would, therefore, be performed relative to the tree root. In Figure 20 1 above, the address, name and phone tree nodes were all added as direct children of the root element.

If, on the other hand, a path is passed as an argument, all write operations will be performed relative to that path into the tree. In the following line of code, for example, the database reference is set to a specific path within the current database:

<pre>
dbRef = database.getReference("/test/data/message1");
</pre>

If the specified path does not already exist, it is created automatically within the tree as soon as data is written at that location.

== Performing a Basic Write Operation ==

The most basic of write operations involves the use of the setValue() method of the database reference object, passing through the data to be stored. The data must be of a supported type (as outlined in the previous chapter). Assuming the above configured database reference, a string value may be written to the database using the following line of code:

<pre>
dbRef.setValue("Hello");
</pre>

After execution, the data tree as represented within the Firebase console will appear as shown in Figure 20 2:

[[File:]]

Figure 20-2


If data already exists at the specified path, it will be overwritten by the write operation.

== Accessing Child Nodes ==

In addition to writing data using the path defined within the database reference, the child() method may be called on each node to navigate and write to a specific node within a tree. Assuming a database reference set to the root of the tree shown in Figure 20-2 above, the following code would change the string value assigned to the message1 key:

<pre>
dbRef = database.getReference();
dbRef.child("test").child("data").child("message1").setValue("Goodbye");
</pre>

This approach is ideal for situations where only a single value needs to be changed without rewriting large amounts of data to a tree. When writing to the database in this way it is important to understand how this interacts with the database reference.

As with all write operations, if the path specified by the child() method calls does not yet exist, it will be created at the point that the setValue() method is called. In the above code, the database reference was first configured to point to the root (/) of the database tree. The second line of code then assembles a path relative to the database reference, in other words /test/data/message1. When executed, the above code will result in the following tree where only the value assigned to the message1 key has changed:


[[File:]]

Figure 20-3


Had the database reference not been reset to the root of the tree (in other words the reference was still pointing to /test/data/message1), the setValue operation would have entirely different results. Consider, for example, the following variation to the previous code fragment:

<pre>
dbRef = database.getReference("/test/data/message1");
dbRef.child("test").child("data").child("message1").setValue("Goodbye");
</pre>

This time, the database reference is being set to /test/data/message1 before the setValue operation is performed. When executed, the code will result in the creation of the following tree structure:


[[File:]]

Figure 20-4


In this case, the write operation has been performed relative to the /test/data/message1 path, resulting in an entirely new path being appended to the existing path. Since the combined path did not previously exist, Firebase has created it for us before setting the “Goodbye” value.

When working with paths, therefore, it is important to be aware of where the database reference is pointing at all times.

== Writing to Multiple Nodes ==

The setValue() method is useful for writing values to the database one at a time. Another common scenario is likely to involve writing values to multiple nodes simultaneously. This type of update operation can be performed using the updateChildren() method of the database reference. In the following code fragment, this method is used to update title and content nodes for both message1 and message2:

<pre>
dbRef = database.getReference("/test/data");

Map<String, Object> childUpdates = new HashMap<>();

childUpdates.put("message1/title", "Title 1");
childUpdates.put("message1/content", "Content string 1");
childUpdates.put("message2/title", "Title 2");
childUpdates.put("message2/content", "Content string 2");

dbRef.updateChildren(childUpdates);
</pre>

== Storing a Java Object ==

The previous chapter (An Introduction to the Firebase Realtime Database) outlined the requirements that must be met in order to be able to store the content of a Java object in a Firebase database. Assuming these requirements have been met, the data within a Java object can be stored using the setValue() method. The previous chapter identified the following example Java class as being suitable for storage in a realtime database:

<pre>
import com.google.firebase.database.IgnoreExtraProperties;

@IgnoreExtraProperties
public class Profile {
public String name;
public String address;
public String phone;

public Profile() {
}

public Profile(String name, String address, String phone) {
this.name = name;
this.address = address;
this.phone = phone;
}
}
</pre>

Code to store an instance of this class would read as follows:

<pre>
Profile profile = new Profile("Tim Cook", "Infinite Loop", "555-123-1234");
dbRef = database.getReference();
dbRef.setValue(profile);
</pre>

As will be outlined in the next chapter, this data can subsequently be read back from the database in the form of a Java object.

== Deleting Data ==

In addition to reading, writing and updating the data in a realtime database, data may also be deleted using a variety of options. Data can be deleted, for example, by passing a null value through to a setValue() method call:

<pre>
dbRef.child("message1").child("content").setValue(null);
Similarly, data may be deleted using the removeValue() method:
dbRef.child("message1").child("content").removeValue();
</pre>

Alternatively, data can be deleted from multiple nodes in a single operation using null values in conjunction with the updateChildren() method:

<pre>
Map<String, Object> childUpdates = new HashMap<>();

childUpdates.put("message1/title", null);
childUpdates.put("message1/content", null);
childUpdates.put("message2/title", null);
childUpdates.put("message2/content", null);

dbRef.updateChildren(childUpdates);
</pre>

== Handling Write Errors ==

As with any operation, it is important for an app to notify the user in the event that a realtime database write operation failed. The success or failure of a write operation can be tracked by the app through the use of a completion handler. Completion handlers are created using the CompletionListener interface of the DatabaseReference class as follows:

<pre>
DatabaseReference.CompletionListener completionListener = new
DatabaseReference.CompletionListener() {

@Override
public void onComplete(DatabaseError databaseError,
DatabaseReference databaseReference) {

if (databaseError != null) {
// Notify user of failure
}
}
};
</pre>

In the event of a failure, the getMessage() method of the DatabaseError object may be used to obtain the description of the error that occurred to display the user.

Once the completion listener has been added, it needs to be referenced when data is being written to the database using the setValue() or updateChildren() methods, for example:

<pre>
dbRef.child("message1").child("content").setValue("hello",
completionListener);

dbRef.updateChildren(childUpdates, completionListener);
</pre>

== Summary ==

The first step in writing to a database involves obtaining a reference to the FirebaseDatabase instance. This instance is then used to generate a DatabaseReference object which points to a specific location within the tree. Write operations may then be performed by making calls to the setValue() method of the DatabaseReference object. The database tree hierarchy can be traversed during a write operation by making calls to the child() method. To write to multiple nodes simultaneously, simply specify the data to be updated within a Map object and pass it through to the updateChildren() method of the database reference.
Database write failures can be detected by implementing a completion listener and referencing it during write operations.