获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Menyimpan Data

Dokumen ini mencakup empat metode untuk menulis data ke Firebase Realtime Database: set, update, push, dan dukungan transaksi.

Cara Menghemat Data

mengatur Tulis atau ganti data ke jalur yang ditentukan , seperti messages/users/<username>
memperbarui Perbarui beberapa kunci untuk jalur yang ditentukan tanpa mengganti semua data
dorongan Tambahkan ke daftar data dalam database. Setiap kali Anda mendorong node baru ke dalam daftar, database Anda menghasilkan kunci unik, seperti messages/users/<unique-user-id>/<username>
transaksi Gunakan transaksi saat bekerja dengan data kompleks yang dapat dirusak oleh pembaruan bersamaan

Menyimpan Data

Operasi penulisan basis data dasar adalah satu set yang menyimpan data baru ke referensi basis data yang ditentukan, menggantikan data yang ada di jalur itu. Untuk memahami set, kami akan membuat aplikasi blog sederhana. Data untuk aplikasi Anda disimpan di referensi database ini:

Jawa
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')
Pergi
// 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")

Mari kita mulai dengan menyimpan beberapa data pengguna. Kami akan menyimpan setiap pengguna dengan nama pengguna yang unik, dan kami juga akan menyimpan nama lengkap dan tanggal lahir mereka. Karena setiap pengguna akan memiliki nama pengguna yang unik, masuk akal untuk menggunakan metode set di sini daripada metode push karena Anda sudah memiliki kunci dan tidak perlu membuatnya.

Pertama, buat referensi database ke data pengguna Anda. Kemudian gunakan set() / setValue() untuk menyimpan objek pengguna ke database dengan nama pengguna, nama lengkap, dan tanggal lahir pengguna. Anda dapat meneruskan set string, angka, boolean, null , array, atau objek JSON apa pun. Melewati null akan menghapus data di lokasi yang ditentukan. Dalam hal ini Anda akan memberikannya sebuah objek:

Jawa
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'
    }
})
Pergi

// 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)
}

Saat objek JSON disimpan ke database, properti objek secara otomatis dipetakan ke lokasi turunan database secara bertingkat. Sekarang jika Anda menavigasi ke URL https://docs-examples.firebaseio.com/server/saving-data/fireblog/users/alanisawesome/full_name , kita akan melihat nilai "Alan Turing". Anda juga dapat menyimpan data langsung ke lokasi anak:

Jawa
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'
})
Pergi
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)
}

Dua contoh di atas - menulis kedua nilai secara bersamaan sebagai objek dan menulisnya secara terpisah ke lokasi turunan - akan menghasilkan data yang sama yang disimpan ke database Anda:

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

Contoh pertama hanya akan memicu satu peristiwa pada klien yang menonton data, sedangkan contoh kedua akan memicu dua. Penting untuk dicatat bahwa jika data sudah ada di usersRef , pendekatan pertama akan menimpanya, tetapi metode kedua hanya akan mengubah nilai dari setiap simpul anak yang terpisah sambil membiarkan anak-anak lain dari usersRef tidak berubah.

Memperbarui Data Tersimpan

Jika Anda ingin menulis ke beberapa anak dari lokasi database secara bersamaan tanpa menimpa node anak lainnya, Anda dapat menggunakan metode pembaruan seperti yang ditunjukkan di bawah ini:

Jawa
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'
})
Pergi
hopperRef := usersRef.Child("gracehop")
if err := hopperRef.Update(ctx, map[string]interface{}{
	"nickname": "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating child:", err)
}

Ini akan memperbarui data Grace untuk memasukkan nama panggilannya. Jika Anda menggunakan set di sini alih-alih memperbarui, itu akan menghapus full_name dan date_of_birth dari hopperRef Anda.

Firebase Realtime Database juga mendukung pembaruan multi-jalur. Ini berarti bahwa pembaruan sekarang dapat memperbarui nilai di beberapa lokasi di basis data Anda secara bersamaan, fitur canggih yang memungkinkan Anda mendenormalisasi data Anda . Menggunakan pembaruan multi-jalur, Anda dapat menambahkan nama panggilan ke Grace dan Alan secara bersamaan:

Jawa
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'
})
Pergi
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)
}

Setelah pembaruan ini, nama panggilan Alan dan Grace ditambahkan:

{
  "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"
    }
  }
}

Perhatikan bahwa mencoba memperbarui objek dengan menulis objek dengan jalur yang disertakan akan menghasilkan perilaku yang berbeda. Mari kita lihat apa yang terjadi jika Anda mencoba memperbarui Grace dan Alan dengan cara ini:

Jawa
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'
    }
})
Pergi
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)
}

Ini menghasilkan perilaku yang berbeda, yaitu menimpa seluruh /users node:

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

Menambahkan Panggilan Balik Penyelesaian

Di Node.js dan Java Admin SDK, jika Anda ingin tahu kapan data Anda telah dikomit, Anda dapat menambahkan callback penyelesaian. Baik metode set maupun update dalam SDK ini mengambil callback penyelesaian opsional yang dipanggil saat penulisan telah dilakukan ke database. Jika panggilan tidak berhasil karena suatu alasan, panggilan balik dilewatkan objek kesalahan yang menunjukkan mengapa kegagalan terjadi. Di Python dan Go Admin SDK, semua metode penulisan diblokir. Artinya, metode penulisan tidak kembali sampai penulisan dilakukan ke database.

Jawa
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.');
  }
});

Menyimpan Daftar Data

Saat membuat daftar data, penting untuk mengingat sifat multi-pengguna dari sebagian besar aplikasi dan menyesuaikan struktur daftar Anda. Memperluas contoh di atas, mari tambahkan entri blog ke aplikasi Anda. Naluri pertama Anda mungkin menggunakan set untuk menyimpan anak-anak dengan indeks bilangan bulat yang bertambah secara otomatis, seperti berikut ini:

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

Jika pengguna menambahkan posting baru, itu akan disimpan sebagai /posts/2 . Ini akan bekerja jika hanya satu penulis yang menambahkan posting, tetapi dalam aplikasi blogging kolaboratif Anda, banyak pengguna dapat menambahkan posting pada waktu yang sama. Jika dua penulis menulis ke /posts/2 secara bersamaan, maka salah satu postingan akan dihapus oleh yang lain.

Untuk mengatasi ini, klien Firebase menyediakan fungsi push() yang menghasilkan kunci unik untuk setiap anak baru . Dengan menggunakan kunci anak yang unik, beberapa klien dapat menambahkan anak-anak ke lokasi yang sama pada waktu yang sama tanpa khawatir tentang konflik penulisan.

Jawa
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'
})
Pergi

// 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)
}

Kunci unik didasarkan pada stempel waktu, sehingga daftar item akan secara otomatis diurutkan secara kronologis. Karena Firebase menghasilkan kunci unik untuk setiap entri blog, konflik penulisan tidak akan terjadi jika beberapa pengguna menambahkan entri secara bersamaan. Data database Anda sekarang terlihat seperti ini:

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

Dalam JavaScript, Python, dan Go, pola pemanggilan push() dan kemudian segera memanggil set() sangat umum sehingga Firebase SDK memungkinkan Anda menggabungkannya dengan meneruskan data yang akan disetel langsung ke push() sebagai berikut:

Jawa
// 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'
})
Pergi
if _, err := postsRef.Push(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

Mendapatkan kunci unik yang dihasilkan oleh Push()

Memanggil push() akan mengembalikan referensi ke jalur data baru, yang dapat Anda gunakan untuk mendapatkan kunci atau menyetel data ke sana. Kode berikut akan menghasilkan data yang sama seperti contoh di atas, tetapi sekarang kita akan memiliki akses ke kunci unik yang dihasilkan:

Jawa
// 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
Pergi
// 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

Seperti yang Anda lihat, Anda bisa mendapatkan nilai kunci unik dari referensi push() Anda.

Di bagian selanjutnya tentang Mengambil Data , kita akan mempelajari cara membaca data ini dari database Firebase.

Menyimpan Data Transaksi

Saat bekerja dengan data kompleks yang dapat dirusak oleh modifikasi bersamaan, seperti penghitung tambahan, SDK menyediakan operasi transaksi .

Di Java dan Node.js, Anda memberi operasi transaksi dua callback: fungsi update dan callback penyelesaian opsional. Di Python dan Go, operasi transaksi diblokir dan karena itu hanya menerima fungsi pembaruan.

Fungsi pembaruan mengambil status data saat ini sebagai argumen dan harus mengembalikan status baru yang diinginkan yang ingin Anda tulis. Misalnya, jika Anda ingin menambah jumlah upvotes pada posting blog tertentu, Anda akan menulis transaksi seperti berikut:

Jawa
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')
Pergi
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)
}

Contoh di atas memeriksa untuk melihat apakah penghitung null atau belum bertambah, karena transaksi dapat dipanggil dengan null jika tidak ada nilai default yang ditulis.

Jika kode di atas dijalankan tanpa fungsi transaksi dan dua klien mencoba menaikkannya secara bersamaan, mereka berdua akan menulis 1 sebagai nilai baru, menghasilkan satu kenaikan, bukan dua.

Konektivitas Jaringan dan Penulisan Offline

Klien Firebase Node.js dan Java mempertahankan versi internal mereka sendiri dari setiap data aktif. Ketika data ditulis, itu ditulis ke versi lokal ini terlebih dahulu. Klien kemudian menyinkronkan data tersebut dengan database dan dengan klien lain berdasarkan 'upaya terbaik'.

Akibatnya, semua penulisan ke database akan segera memicu peristiwa lokal, bahkan sebelum data apa pun ditulis ke database. Artinya, saat Anda menulis aplikasi menggunakan Firebase, aplikasi Anda akan tetap responsif terlepas dari latensi jaringan atau konektivitas Internet.

Setelah konektivitas dibangun kembali, kami akan menerima rangkaian peristiwa yang sesuai sehingga klien "mengejar" status server saat ini, tanpa harus menulis kode khusus apa pun.

Mengamankan Data Anda

Firebase Realtime Database memiliki bahasa keamanan yang memungkinkan Anda menentukan pengguna mana yang memiliki akses baca dan tulis ke berbagai node data Anda. Anda dapat membaca lebih lanjut tentangnya di Amankan Data Anda .