Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Daten speichern

Dieses Dokument behandelt die vier Methoden zum Schreiben von Daten in Ihre Firebase-Echtzeitdatenbank: Festlegen, Aktualisieren, Pushen und Transaktionsunterstützung.

Möglichkeiten zum Speichern von Daten

einstellen Schreiben oder ersetzen Sie Daten in einen definierten Pfad , z. B. messages/users/<username>
aktualisieren Aktualisieren Sie einige der Schlüssel für einen definierten Pfad, ohne alle Daten zu ersetzen
drücken Zu einer Liste von Daten in der Datenbank hinzufügen. Jedes Mal, wenn Sie einen neuen Knoten in eine Liste verschieben, generiert Ihre Datenbank einen eindeutigen Schlüssel, z. B. messages/users/<unique-user-id>/<username>
Transaktion Verwenden Sie Transaktionen, wenn Sie mit komplexen Daten arbeiten, die durch gleichzeitige Aktualisierungen beschädigt werden können

Daten speichern

Die grundlegende Datenbankschreiboperation 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
var admin = require("firebase-admin");

// Get a database reference to our blog
var db = admin.database();
var 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 anstelle der Push-Methode die set-Methode zu verwenden, da Sie bereits über den Schlüssel verfügen und keinen erstellen müssen.

Erstellen Sie zunächst einen Datenbankverweis auf Ihre Benutzerdaten. Verwenden Sie dann set() / setValue() , um ein Benutzerobjekt mit dem Benutzernamen, dem vollständigen Namen und dem Geburtstag des Benutzers in der Datenbank zu speichern. Sie können set einen String, eine Zahl, einen Booleschen Wert, einen null , ein Array oder ein beliebiges JSON-Objekt übergeben. Wenn Sie null werden die Daten am angegebenen Speicherort entfernt. 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
var 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. Wenn Sie nun zur URL https://docs-examples.firebaseio.com/server/saving-data/fireblog/users/alanisawesome/full_name navigieren, wird der Wert "Alan Turing" angezeigt. Sie können Daten auch direkt an einem untergeordneten Speicherort 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
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 oben genannten Beispiele - beide Werte gleichzeitig mit einem 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 überwachen, während das zweite Beispiel zwei auslöst. Es ist wichtig zu beachten, dass, wenn Daten bereits bei usersRef , der erste Ansatz diese überschreiben würde, die zweite Methode jedoch nur den Wert jedes einzelnen usersRef Knotens ändern würde, während andere usersRef von usersRef unverändert usersRef .

Gespeicherte Daten aktualisieren

Wenn Sie gleichzeitig in mehrere untergeordnete Elemente eines Datenbankspeicherorts schreiben möchten, ohne andere untergeordnete Knoten zu überschreiben, können Sie die folgende Aktualisierungsmethode verwenden:

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

hopperRef.updateChildrenAsync(hopperUpdates);
Node.js
var 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 die Daten von Grace aktualisiert und enthalten ihren Spitznamen. Wenn Sie hier set anstelle von update verwendet hätten, hätte es sowohl full_name als auch date_of_birth aus Ihrem hopperRef .

Die Firebase-Echtzeitdatenbank unterstützt auch Aktualisierungen mit mehreren Pfaden. Dies bedeutet, dass durch die Aktualisierung jetzt Werte an mehreren Stellen in Ihrer Datenbank gleichzeitig aktualisiert werden können. Diese leistungsstarke Funktion hilft Ihnen, Ihre Daten zu denormalisieren . Mithilfe von Multi-Path-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
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. Werfen wir einen Blick darauf, was passiert, wenn Sie stattdessen versuchen, Grace und Alan folgendermaßen 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
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)
}

Dies führt zu einem unterschiedlichen Verhalten, nämlich zum Überschreiben des gesamten Knotens /users :

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

Hinzufügen eines Abschlussrückrufs

Wenn Sie in Node.js- und Java Admin-SDKs wissen möchten, wann Ihre Daten festgeschrieben wurden, können Sie einen Abschlussrückruf hinzufügen. Sowohl Set- als auch Update-Methoden in diesen SDKs verwenden einen optionalen Abschlussrückruf, der aufgerufen wird, wenn der Schreibvorgang in die Datenbank übernommen wurde. Wenn der Anruf 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 werden alle Schreibmethoden blockiert. Das heißt, die Schreibmethoden werden erst zurückgegeben, wenn die Schreibvorgänge in die Datenbank übernommen 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", function(error) {
  if (error) {
    alert("Data could not be saved." + error);
  } else {
    alert("Data saved successfully.");
  }
});

Speichern von Datenlisten

Beim Erstellen von Datenlisten ist es wichtig, den Mehrbenutzercharakter der meisten Anwendungen zu berücksichtigen und die Listenstruktur entsprechend anzupassen. Erweitern Sie das obige Beispiel und fügen Sie Ihrer App Blog-Beiträge hinzu. Ihr erster Instinkt könnte darin bestehen, set zum Speichern von untergeordneten Elementen mit automatisch inkrementierenden Ganzzahlindizes wie den folgenden zu verwenden:

// 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, wird dieser als /posts/2 gespeichert. Dies würde funktionieren, wenn nur ein einziger Autor Beiträge hinzufügen würde, aber in Ihrer kollaborativen Blogging-Anwendung können viele Benutzer gleichzeitig Beiträge hinzufügen. Wenn zwei Autoren gleichzeitig an /posts/2 schreiben, wird einer der Beiträge vom anderen gelöscht.

Um dies zu lösen, bieten die Firebase-Clients eine push() Funktion, die für jedes neue Kind einen eindeutigen Schlüssel generiert . Durch die Verwendung eindeutiger untergeordneter Schlüssel können mehrere Clients gleichzeitig untergeordnete Elemente am selben Speicherort hinzufügen, ohne sich um Schreibkonflikte sorgen 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
var postsRef = ref.child("posts");

var 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 Blog-Beitrag einen eindeutigen Schlüssel generiert, treten keine Schreibkonflikte auf, wenn mehrere Benutzer gleichzeitig einen Beitrag hinzufügen. Ihre Datenbankdaten sehen jetzt folgendermaßen 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 ist das Muster des Aufrufs von push() und des sofortigen Aufrufs von set() so häufig, dass Sie sie im Firebase SDK kombinieren können, indem Sie die zu setzenden Daten wie folgt direkt an push() :

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 durch push () generierten eindeutigen Schlüssels

Wenn Sie push() aufrufen push() wird ein Verweis auf den neuen Datenpfad zurückgegeben, über den Sie den Schlüssel abrufen oder Daten dafür festlegen können. Der folgende Code führt zu denselben Daten wie im obigen 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()
var newPostRef = postsRef.push();

// Get the unique key generated by push()
var 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üssels aus Ihrer push() Referenz abrufen.

Im nächsten Abschnitt zum Abrufen von Daten erfahren Sie, wie Sie diese Daten aus einer Firebase-Datenbank lesen.

Speichern von Transaktionsdaten

Bei der Arbeit mit komplexen Daten, die durch gleichzeitige Änderungen wie inkrementelle Zähler beschädigt werden könnten, bietet das SDK eine Transaktionsoperation .

In Java und Node.js geben Sie der Transaktionsoperation zwei Rückrufe: eine Aktualisierungsfunktion und einen optionalen Abschlussrückruf. In Python und Go blockiert die Transaktionsoperation und akzeptiert daher nur die Aktualisierungsfunktion.

Die Aktualisierungsfunktion verwendet den aktuellen Status der Daten als Argument und sollte den neuen gewünschten Status zurückgeben, den Sie schreiben möchten. Wenn Sie beispielsweise die Anzahl der Upvotes in einem bestimmten Blog-Beitrag 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
var upvotesRef = db.ref("server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes");
upvotesRef.transaction(function (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 prüft, ob der Zähler null oder noch nicht inkrementiert wurde, da Transaktionen mit null aufgerufen werden können, wenn kein Standardwert geschrieben wurde.

Wenn der obige Code ohne eine Transaktionsfunktion ausgeführt worden wäre und zwei Clients versucht hätten, ihn gleichzeitig zu erhöhen, würden beide 1 als neuen Wert schreiben, was zu einem Inkrement anstelle von zwei führen würde.

Netzwerkkonnektivität und Offline-Schreibvorgänge

Firebase Node.js- und Java-Clients verwalten ihre eigene interne Version aller aktiven Daten. Wenn Daten geschrieben werden, werden sie zuerst in diese lokale Version geschrieben. Der Client synchronisiert diese Daten dann nach bestem Wissen mit der Datenbank und mit anderen Clients.

Infolgedessen lösen alle Schreibvorgänge in die Datenbank lokale Ereignisse sofort aus, bevor überhaupt Daten in die Datenbank geschrieben wurden. Dies bedeutet, dass beim Schreiben einer Anwendung mit Firebase Ihre App unabhängig von der Netzwerklatenz oder der Internetverbindung reagiert.

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

Sichern Sie Ihre Daten

Die Firebase-Echtzeitdatenbank verfügt über eine Sicherheitssprache, mit der Sie definieren können, welche Benutzer Lese- und Schreibzugriff auf verschiedene Knoten Ihrer Daten haben. Weitere Informationen finden Sie unter Sichern Sie Ihre Daten .