Daten speichern

Dieses Dokument behandelt die vier Methoden zum Schreiben von Daten in Ihre Firebase Realtime Database: Set-, Update-, Push- und Transaktionsunterstützung.

Möglichkeiten zum Speichern von Daten

einstellen Schreib- oder ersetzen Sie die Daten auf einen definierten Pfad, wie messages/users/<username>
aktualisieren Aktualisieren Sie einige der Schlüssel für einen definierten Pfad, ohne alle Daten zu ersetzen
drücken In einer Liste von Daten in der Datenbank. Jedes Mal , wenn Sie einen neuen Knoten auf eine Liste drücken, erzeugt die Datenbank einen eindeutigen Schlüssel, wie messages/users/<unique-user-id>/<username>
Transaktion Verwenden Sie Transaktionen, wenn Sie mit komplexen Daten arbeiten, die durch gleichzeitige Aktualisierungen beschädigt werden könnten

Daten speichern

Der grundlegende Datenbankschreibvorgang ist ein Satz, der neue Daten in der angegebenen Datenbankreferenz speichert und alle vorhandenen Daten in diesem Pfad ersetzt. Um Set zu verstehen, erstellen wir eine einfache Blogging-App. Die Daten für Ihre App werden in dieser Datenbankreferenz gespeichert:

Java
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("server/saving-data/fireblog");
Node.js
// Import Admin SDK
const { getDatabase } = require('firebase-admin/database');

// Get a database reference to our blog
const db = getDatabase();
const ref = db.ref('server/saving-data/fireblog');
Python
# Import database module.
from firebase_admin import db

# Get a database reference to our blog.
ref = db.reference('server/saving-data/fireblog')
gehen
// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our blog.
ref := client.NewRef("server/saving-data/fireblog")

Beginnen wir mit dem Speichern einiger Benutzerdaten. Wir speichern jeden Benutzer unter einem eindeutigen Benutzernamen und speichern auch seinen vollständigen Namen und sein Geburtsdatum. Da jeder Benutzer einen eindeutigen Benutzernamen hat, ist es sinnvoll, hier die set-Methode anstelle der push-Methode zu verwenden, da Sie den Schlüssel bereits haben und keinen erstellen müssen.

Erstellen Sie zunächst eine Datenbankreferenz zu Ihren Benutzerdaten. Dann nutzen set() / setValue() mit dem Benutzernamen des Benutzers, den vollständigen Namen und Geburtstag ein Benutzerobjekt in die Datenbank zu speichern. Sie können eine Zeichenfolge, Zahl, boolean, Pass gesetzt null , ein Array oder ein JSON - Objekt. Passing null werden die Daten an der angegebenen Stelle entfernen. In diesem Fall übergeben Sie ihm ein Objekt:

Java
public static class User {

  public String date_of_birth;
  public String full_name;
  public String nickname;

  public User(String dateOfBirth, String fullName) {
    // ...
  }

  public User(String dateOfBirth, String fullName, String nickname) {
    // ...
  }

}

DatabaseReference usersRef = ref.child("users");

Map<String, User> users = new HashMap<>();
users.put("alanisawesome", new User("June 23, 1912", "Alan Turing"));
users.put("gracehop", new User("December 9, 1906", "Grace Hopper"));

usersRef.setValueAsync(users);
Node.js
const usersRef = ref.child('users');
usersRef.set({
  alanisawesome: {
    date_of_birth: 'June 23, 1912',
    full_name: 'Alan Turing'
  },
  gracehop: {
    date_of_birth: 'December 9, 1906',
    full_name: 'Grace Hopper'
  }
});
Python
users_ref = ref.child('users')
users_ref.set({
    'alanisawesome': {
        'date_of_birth': 'June 23, 1912',
        'full_name': 'Alan Turing'
    },
    'gracehop': {
        'date_of_birth': 'December 9, 1906',
        'full_name': 'Grace Hopper'
    }
})
gehen

// User is a json-serializable type.
type User struct {
	DateOfBirth string `json:"date_of_birth,omitempty"`
	FullName    string `json:"full_name,omitempty"`
	Nickname    string `json:"nickname,omitempty"`
}

usersRef := ref.Child("users")
err := usersRef.Set(ctx, map[string]*User{
	"alanisawesome": {
		DateOfBirth: "June 23, 1912",
		FullName:    "Alan Turing",
	},
	"gracehop": {
		DateOfBirth: "December 9, 1906",
		FullName:    "Grace Hopper",
	},
})
if err != nil {
	log.Fatalln("Error setting value:", err)
}

Wenn ein JSON-Objekt in der Datenbank gespeichert wird, werden die Objekteigenschaften automatisch verschachtelt den untergeordneten Datenbankspeicherorten zugeordnet. Nun , wenn Sie zu der URL navigieren https://docs-examples.firebaseio.com/server/saving-data/fireblog/users/alanisawesome/full_name , werden wir den Wert „Alan Turing“ sehen. Sie können Daten auch direkt an einem untergeordneten Standort speichern:

Java
usersRef.child("alanisawesome").setValueAsync(new User("June 23, 1912", "Alan Turing"));
usersRef.child("gracehop").setValueAsync(new User("December 9, 1906", "Grace Hopper"));
Node.js
const usersRef = ref.child('users');
usersRef.child('alanisawesome').set({
  date_of_birth: 'June 23, 1912',
  full_name: 'Alan Turing'
});
usersRef.child('gracehop').set({
  date_of_birth: 'December 9, 1906',
  full_name: 'Grace Hopper'
});
Python
users_ref.child('alanisawesome').set({
    'date_of_birth': 'June 23, 1912',
    'full_name': 'Alan Turing'
})
users_ref.child('gracehop').set({
    'date_of_birth': 'December 9, 1906',
    'full_name': 'Grace Hopper'
})
gehen
if err := usersRef.Child("alanisawesome").Set(ctx, &User{
	DateOfBirth: "June 23, 1912",
	FullName:    "Alan Turing",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

if err := usersRef.Child("gracehop").Set(ctx, &User{
	DateOfBirth: "December 9, 1906",
	FullName:    "Grace Hopper",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

Die beiden obigen Beispiele – beide Werte gleichzeitig als Objekt schreiben und separat an untergeordnete Speicherorte schreiben – führen dazu, dass dieselben Daten in Ihrer Datenbank gespeichert werden:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper"
    }
  }
}

Das erste Beispiel löst nur ein Ereignis auf Clients aus, die die Daten beobachten, während das zweite Beispiel zwei auslöst. Es ist wichtig zu beachten , dass , wenn die Daten bereits bei existieren usersRef , der erste Ansatz überschreiben würde, aber das zweite Verfahren würde nur den Wert jeden einzelnen untergeordneten Knoten ändern , während andere Kinder verlassen usersRef unverändert.

Gespeicherte Daten aktualisieren

Wenn Sie gleichzeitig in mehrere untergeordnete Knoten eines Datenbankspeicherorts schreiben möchten, ohne andere untergeordnete Knoten zu überschreiben, können Sie die unten gezeigte Update-Methode verwenden:

Java
DatabaseReference hopperRef = usersRef.child("gracehop");
Map<String, Object> hopperUpdates = new HashMap<>();
hopperUpdates.put("nickname", "Amazing Grace");

hopperRef.updateChildrenAsync(hopperUpdates);
Node.js
const usersRef = ref.child('users');
const hopperRef = usersRef.child('gracehop');
hopperRef.update({
  'nickname': 'Amazing Grace'
});
Python
hopper_ref = users_ref.child('gracehop')
hopper_ref.update({
    'nickname': 'Amazing Grace'
})
gehen
hopperRef := usersRef.Child("gracehop")
if err := hopperRef.Update(ctx, map[string]interface{}{
	"nickname": "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating child:", err)
}

Dadurch werden Graces Daten aktualisiert, um ihren Spitznamen aufzunehmen. Wenn Sie Satz hier statt Update verwendet hätten, wäre es sowohl gelöscht full_name und date_of_birth von Ihrem hopperRef .

Die Firebase Realtime Database unterstützt auch Multipath-Updates. Dies bedeutet , dass Update jetzt Werte an mehreren Standorten in Ihrer Datenbank zur gleichen Zeit aktualisiert werden können, eine leistungsstarke Funktion , die ermöglicht , hilft Ihnen , Ihre Daten denormalize . Mithilfe von Multipath-Updates können Sie Grace und Alan gleichzeitig Spitznamen hinzufügen:

Java
Map<String, Object> userUpdates = new HashMap<>();
userUpdates.put("alanisawesome/nickname", "Alan The Machine");
userUpdates.put("gracehop/nickname", "Amazing Grace");

usersRef.updateChildrenAsync(userUpdates);
Node.js
const usersRef = ref.child('users');
usersRef.update({
  'alanisawesome/nickname': 'Alan The Machine',
  'gracehop/nickname': 'Amazing Grace'
});
Python
users_ref.update({
    'alanisawesome/nickname': 'Alan The Machine',
    'gracehop/nickname': 'Amazing Grace'
})
gehen
if err := usersRef.Update(ctx, map[string]interface{}{
	"alanisawesome/nickname": "Alan The Machine",
	"gracehop/nickname":      "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating children:", err)
}

Nach diesem Update wurden sowohl Alan als auch Grace ihre Spitznamen hinzugefügt:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

Beachten Sie, dass der Versuch, Objekte durch Schreiben von Objekten mit den enthaltenen Pfaden zu aktualisieren, zu einem anderen Verhalten führt. Sehen wir uns an, was passiert, wenn Sie stattdessen versuchen, Grace und Alan auf diese Weise zu aktualisieren:

Java
Map<String, Object> userNicknameUpdates = new HashMap<>();
userNicknameUpdates.put("alanisawesome", new User(null, null, "Alan The Machine"));
userNicknameUpdates.put("gracehop", new User(null, null, "Amazing Grace"));

usersRef.updateChildrenAsync(userNicknameUpdates);
Node.js
const usersRef = ref.child('users');
usersRef.update({
  'alanisawesome': {
    'nickname': 'Alan The Machine'
  },
  'gracehop': {
    'nickname': 'Amazing Grace'
  }
});
Python
users_ref.update({
    'alanisawesome': {
        'nickname': 'Alan The Machine'
    },
    'gracehop': {
        'nickname': 'Amazing Grace'
    }
})
gehen
if err := usersRef.Update(ctx, map[string]interface{}{
	"alanisawesome": &User{Nickname: "Alan The Machine"},
	"gracehop":      &User{Nickname: "Amazing Grace"},
}); err != nil {
	log.Fatalln("Error updating children:", err)
}

Daraus ergibt sich ein anderes Verhalten, nämlich das Überschreiben des gesamten /users Knoten:

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

Hinzufügen eines Abschlussrückrufs

In Node.js und Java Admin SDKs können Sie einen Abschlussrückruf hinzufügen, wenn Sie wissen möchten, wann Ihre Daten festgeschrieben wurden. Sowohl set- als auch update-Methoden in diesen SDKs verwenden einen optionalen Abschlussrückruf, der aufgerufen wird, wenn der Schreibvorgang in der Datenbank festgeschrieben wurde. Wenn der Aufruf aus irgendeinem Grund nicht erfolgreich war, wird dem Rückruf ein Fehlerobjekt übergeben, das angibt, warum der Fehler aufgetreten ist. In Python- und Go Admin-SDKs blockieren alle Schreibmethoden. Das heißt, die Schreibmethoden kehren nicht zurück, bis die Schreibvorgänge in der Datenbank festgeschrieben wurden.

Java
DatabaseReference dataRef = ref.child("data");
dataRef.setValue("I'm writing data", new DatabaseReference.CompletionListener() {
  @Override
  public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
    if (databaseError != null) {
      System.out.println("Data could not be saved " + databaseError.getMessage());
    } else {
      System.out.println("Data saved successfully.");
    }
  }
});
Node.js
dataRef.set('I\'m writing data', (error) => {
  if (error) {
    console.log('Data could not be saved.' + error);
  } else {
    console.log('Data saved successfully.');
  }
});

Speichern von Datenlisten

Beim Erstellen von Datenlisten ist es wichtig, den Mehrbenutzercharakter der meisten Anwendungen zu berücksichtigen und Ihre Listenstruktur entsprechend anzupassen. Als Erweiterung des obigen Beispiels fügen wir Ihrer App Blog-Posts hinzu. Ihr erster Instinkt könnte sein, set zu verwenden, um untergeordnete Elemente mit automatisch inkrementierenden Integer-Indizes wie den folgenden zu speichern:

// NOT RECOMMENDED - use push() instead!
{
  "posts": {
    "0": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
    },
    "1": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

Wenn ein Benutzer einen neuen Beitrag hinzufügt würde es als gespeichert werden /posts/2 . Dies würde funktionieren, wenn nur ein einzelner Autor Beiträge hinzufügt, aber in Ihrer kollaborativen Blogging-Anwendung können viele Benutzer gleichzeitig Beiträge hinzufügen. Wenn zwei Autoren zu schreiben /posts/2 gleichzeitig, dann einer des Pfostens würde durch die andere gelöscht werden.

Um dies zu lösen, bieten die Firebase Kunden eine push() Funktion , die einen eindeutigen Schlüssel für jedes neues Kind erzeugt. Durch die Verwendung eindeutiger untergeordneter Schlüssel können mehrere Clients gleichzeitig untergeordnete Elemente zum selben Speicherort hinzufügen, ohne sich über Schreibkonflikte Gedanken machen zu müssen.

Java
public static class Post {

  public String author;
  public String title;

  public Post(String author, String title) {
    // ...
  }

}

DatabaseReference postsRef = ref.child("posts");

DatabaseReference newPostRef = postsRef.push();
newPostRef.setValueAsync(new Post("gracehop", "Announcing COBOL, a New Programming Language"));

// We can also chain the two calls together
postsRef.push().setValueAsync(new Post("alanisawesome", "The Turing Machine"));
Node.js
const newPostRef = postsRef.push();
newPostRef.set({
  author: 'gracehop',
  title: 'Announcing COBOL, a New Programming Language'
});

// we can also chain the two calls together
postsRef.push().set({
  author: 'alanisawesome',
  title: 'The Turing Machine'
});
Python
posts_ref = ref.child('posts')

new_post_ref = posts_ref.push()
new_post_ref.set({
    'author': 'gracehop',
    'title': 'Announcing COBOL, a New Programming Language'
})

# We can also chain the two calls together
posts_ref.push().set({
    'author': 'alanisawesome',
    'title': 'The Turing Machine'
})
gehen

// Post is a json-serializable type.
type Post struct {
	Author string `json:"author,omitempty"`
	Title  string `json:"title,omitempty"`
}

postsRef := ref.Child("posts")

newPostRef, err := postsRef.Push(ctx, nil)
if err != nil {
	log.Fatalln("Error pushing child node:", err)
}

if err := newPostRef.Set(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

// We can also chain the two calls together
if _, err := postsRef.Push(ctx, &Post{
	Author: "alanisawesome",
	Title:  "The Turing Machine",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

Der eindeutige Schlüssel basiert auf einem Zeitstempel, sodass Listenelemente automatisch chronologisch sortiert werden. Da Firebase für jeden Blogbeitrag einen eindeutigen Schlüssel generiert, treten keine Schreibkonflikte auf, wenn mehrere Benutzer gleichzeitig einen Beitrag hinzufügen. Ihre Datenbankdaten sehen jetzt so aus:

{
  "posts": {
    "-JRHTHaIs-jNPLXOQivY": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
    },
    "-JRHTHaKuITFIhnj02kE": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

In JavaScript, Python und Go, das Muster des Aufrufs push() und dann sofort anrufen set() ist so verbreitet , dass die Firebase SDK können Sie sie kombinieren , indem die Daten vorbei direkt zu setzen push() wie folgt:

Java
// No Java equivalent
Node.js
// This is equivalent to the calls to push().set(...) above
postsRef.push({
  author: 'gracehop',
  title: 'Announcing COBOL, a New Programming Language'
});;
Python
# This is equivalent to the calls to push().set(...) above
posts_ref.push({
    'author': 'gracehop',
    'title': 'Announcing COBOL, a New Programming Language'
})
gehen
if _, err := postsRef.Push(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

Abrufen des von push() generierten eindeutigen Schlüssels

Der Aufruf push() wird ein Verweis auf den neuen Datenpfad zurückkehren, in dem Sie die Taste oder eingestellten Daten zu bekommen , um sie verwenden können. Der folgende Code führt zu denselben Daten wie das obige Beispiel, aber jetzt haben wir Zugriff auf den eindeutigen Schlüssel, der generiert wurde:

Java
// Generate a reference to a new location and add some data using push()
DatabaseReference pushedPostRef = postsRef.push();

// Get the unique ID generated by a push()
String postId = pushedPostRef.getKey();
Node.js
// Generate a reference to a new location and add some data using push()
const newPostRef = postsRef.push();

// Get the unique key generated by push()
const postId = newPostRef.key;
Python
# Generate a reference to a new location and add some data using push()
new_post_ref = posts_ref.push()

# Get the unique key generated by push()
post_id = new_post_ref.key
gehen
// Generate a reference to a new location and add some data using Push()
newPostRef, err := postsRef.Push(ctx, nil)
if err != nil {
	log.Fatalln("Error pushing child node:", err)
}

// Get the unique key generated by Push()
postID := newPostRef.Key

Wie Sie sehen können, können Sie den Wert des eindeutigen Schlüssel von Ihrem erhalten push() Bezug genommen wird .

Im nächsten Abschnitt auf Abrufen von Daten , werden wir lernen , wie man diese Daten aus einer Datenbank Firebase zu lesen.

Transaktionsdaten speichern

Wenn es mit komplexen Daten arbeiten , die durch die gleichzeitigen Modifikationen beschädigt werden könnte, wie beispielsweise inkrementelle Zähler stellt die SDK einen Transaktionsvorgang .

In Java und Node.js geben Sie der Transaktionsoperation zwei Callbacks: eine Update-Funktion und einen optionalen Abschluss-Callback. In Python und Go blockiert die Transaktionsoperation und akzeptiert daher nur die Update-Funktion.

Die Update-Funktion nimmt den aktuellen Zustand der Daten als Argument und sollte den neuen gewünschten Zustand zurückgeben, den Sie schreiben möchten. Wenn Sie beispielsweise die Anzahl der Upvotes für einen bestimmten Blogbeitrag erhöhen möchten, schreiben Sie eine Transaktion wie die folgende:

Java
DatabaseReference upvotesRef = ref.child("server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes");
upvotesRef.runTransaction(new Transaction.Handler() {
  @Override
  public Transaction.Result doTransaction(MutableData mutableData) {
    Integer currentValue = mutableData.getValue(Integer.class);
    if (currentValue == null) {
      mutableData.setValue(1);
    } else {
      mutableData.setValue(currentValue + 1);
    }

    return Transaction.success(mutableData);
  }

  @Override
  public void onComplete(
      DatabaseError databaseError, boolean committed, DataSnapshot dataSnapshot) {
    System.out.println("Transaction completed");
  }
});
Node.js
const upvotesRef = db.ref('server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction((current_value) => {
  return (current_value || 0) + 1;
});
Python
def increment_votes(current_value):
    return current_value + 1 if current_value else 1

upvotes_ref = db.reference('server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes')
try:
    new_vote_count = upvotes_ref.transaction(increment_votes)
    print('Transaction completed')
except db.TransactionAbortedError:
    print('Transaction failed to commit')
gehen
fn := func(t db.TransactionNode) (interface{}, error) {
	var currentValue int
	if err := t.Unmarshal(&currentValue); err != nil {
		return nil, err
	}
	return currentValue + 1, nil
}

ref := client.NewRef("server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes")
if err := ref.Transaction(ctx, fn); err != nil {
	log.Fatalln("Transaction failed to commit:", err)
}

Das obige Beispiel überprüft, ob der Zähler null oder wurde noch nicht erhöht wird , da Transaktionen mit aufgerufen werden null , wenn kein Standardwert geschrieben wurde.

Wenn der obige Code hatte , ohne eine Transaktion Funktion ausgeführt wurde und zwei Clients versucht es gleichzeitig zu erhöhen, würden sie beide Schreib 1 als neuer Wert, was zu einer Erhöhung statt zwei.

Netzwerkkonnektivität und Offline-Schreibvorgänge

Firebase Node.js- und Java-Clients verwalten ihre eigene interne Version aller aktiven Daten. Beim Schreiben von Daten werden diese zuerst in diese lokale Version geschrieben. Der Client synchronisiert dann diese Daten mit der Datenbank und mit anderen Clients auf Best-Effort-Basis.

Als Ergebnis werden alle Schreibvorgänge in die Datenbank sofort lokale Ereignisse auslösen, bevor überhaupt Daten in die Datenbank geschrieben wurden. Dies bedeutet , dass , wenn Sie eine Anwendung mit Firebase zu schreiben, Ihre Anwendung reagiert , bleibt unabhängig von der Netzwerk - Latenz oder Internet - Konnektivität.

Sobald die Konnektivität wiederhergestellt ist, erhalten wir die entsprechenden Ereignisse, damit der Client den aktuellen Serverstatus "aufholt", ohne benutzerdefinierten Code schreiben zu müssen.

Sicherung Ihrer Daten

Die Firebase Realtime Database verfügt über eine Sicherheitssprache, mit der Sie definieren können, welche Benutzer Lese- und Schreibzugriff auf verschiedene Knoten Ihrer Daten haben. Sie können mehr über sie lesen Sichern Sie Ihre Daten .