Verileri Kaydetme

Bu belgede, Firebase Realtime Database cihazınıza veri yazmak için kullanılan dört yöntem (set, update, push ve işlemler desteği) ele alınmaktadır.

Veri tasarrufu yapmanın yolları

set Verileri tanımlanmış bir yola (ör. messages/users/<username>) yazma veya değiştirme
update Tüm verileri değiştirmeden tanımlı bir yolun bazı anahtarlarını güncelleme
push Veritabanındaki veri listesine ekleme Bir listeye her yeni düğüm gönderdiğinizde veritabanınız messages/users/<unique-user-id>/<username> gibi benzersiz bir anahtar oluşturur.
işlem Eşzamanlı güncellemeler nedeniyle bozulabilecek karmaşık verilerle çalışırken işlemleri kullanma

Verileri Kaydetme

Temel veritabanı yazma işlemi, belirtilen veritabanı referansına yeni verileri kaydeden ve bu yoldaki mevcut verileri değiştiren bir işlemdir. Set'i anlamak için basit bir blog uygulaması oluşturacağız. Uygulamanızın verileri şu veritabanı referansında depolanır:

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

Öncelikle bazı kullanıcı verilerini kaydederek başlayalım. Her kullanıcıyı benzersiz bir kullanıcı adıyla saklarız. Ayrıca kullanıcının tam adını ve doğum tarihini de saklarız. Her kullanıcının benzersiz bir kullanıcı adı olacağından, anahtara zaten sahip olduğunuz ve anahtar oluşturmanız gerekmediği için burada push yöntemi yerine set yöntemini kullanmak mantıklıdır.

Öncelikle kullanıcı verilerinize yönelik bir veritabanı referansı oluşturun. Ardından, kullanıcı nesnesini veritabanına kullanıcının kullanıcı adı, tam adı ve doğum tarihiyle kaydetmek için set() / setValue() kullanın. Dize, sayı, Boole, null, dizi veya herhangi bir JSON nesnesi iletebilirsiniz. null iletildiğinde belirtilen konumdaki veriler kaldırılır. Bu durumda, işleme bir nesne iletilir:

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

Bir JSON nesnesi veritabanına kaydedildiğinde, nesne özellikleri otomatik olarak iç içe yerleştirilmiş şekilde veritabanı alt konumlarıyla eşlenir. Şimdi https://docs-examples.firebaseio.com/server/saving-data/fireblog/users/alanisawesome/full_name URL'sine giderseniz "Alan Turing" değerini görürsünüz. Verileri doğrudan bir alt konuma da kaydedebilirsiniz:

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'
})
Go
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)
}

Yukarıdaki iki örnekte (değerlerin ikisini de aynı anda bir nesne olarak yazma ve bunları ayrı ayrı alt konumlara yazma) veritabanınıza aynı veriler kaydedilir:

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

İlk örnek, verileri izleyen istemcilerde yalnızca bir etkinliği tetiklerken ikinci örnek iki etkinliği tetikler. usersRef konumunda zaten veri varsa ilk yaklaşımın bu verilerin üzerine yazacağını, ikinci yöntemin ise usersRef konumundaki diğer alt öğeleri değiştirmeden yalnızca her bir ayrı alt düğümün değerini değiştireceğini unutmayın.

Kayıtlı Verileri Güncelleme

Diğer alt düğümleri üzerine yazmadan aynı anda bir veritabanı konumunun birden fazla çocuğuna yazmak istiyorsanız aşağıdaki gibi update yöntemini kullanabilirsiniz:

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

Bu işlem, Grace'in verilerini takma adını içerecek şekilde günceller. Güncelleme yerine burada ayarlama kullanılsaydı full_name ve date_of_birth, hopperRef öğesinden silinirdi.

Firebase Realtime Database, çok yollu güncellemeleri de destekler. Bu, güncellemenin artık veritabanınızdaki birden fazla konumda aynı anda değerleri güncelleyebileceği anlamına gelir. Bu güçlü özellik, verilerinizi normalleştirmemenize yardımcı olur. Çok yollu güncellemeleri kullanarak hem Grace hem de Alan'a aynı anda takma ad ekleyebilirsiniz:

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'
})
Go
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)
}

Bu güncellemeden sonra Alan ve Grace'in takma adları eklendi:

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

Nesneleri yollarla birlikte yazarak güncellemeye çalışmanın farklı bir davranışa yol açacağını unutmayın. Bunun yerine, Grace ve Alan'ı bu şekilde güncellemeye çalışırsanız ne olacağına bakalım:

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'
    }
})
Go
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)
}

Bu durum, farklı bir davranışa (yani /users düğümünün tamamının üzerine yazılmasına) neden olur:

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

Tamamlama geri araması ekleme

Node.js ve Java Admin SDK'larında, verilerinizin ne zaman işlendiğini öğrenmek istiyorsanız bir tamamlama geri çağırması ekleyebilirsiniz. Bu SDK'lardaki hem ayarlama hem de güncelleme yöntemleri, yazma işlemi veritabanına kaydedildiğinde çağrılan isteğe bağlı bir tamamlama geri çağırması alır. Arama herhangi bir nedenle başarısız olursa geri çağırmaya, hatanın nedenini belirten bir hata nesnesi iletilir. Python ve Go Admin SDK'larında tüm yazma yöntemleri engellenir. Yani, yazma yöntemleri, yazma işlemleri veritabanına kaydedilene kadar döndürülmez.

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

Veri Listelerini Kaydetme

Veri listeleri oluştururken çoğu uygulamanın çok kullanıcılı yapısını göz önünde bulundurmak ve liste yapınızı buna göre ayarlamak önemlidir. Yukarıdaki örneği genişleterek uygulamanıza blog yayınları ekleyelim. İlk içgüdünüz, aşağıdaki gibi otomatik olarak artan tam sayı dizinlerine sahip alt öğeleri depolamak için set kullanmak olabilir:

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

Kullanıcı yeni bir yayın eklediğinde bu yayın /posts/2 olarak saklanır. Bu yöntem yalnızca tek bir yazar gönderi ekliyorsa işe yarar. Ancak ortak çalışma blogu uygulamanızda birçok kullanıcı aynı anda gönderi ekleyebilir. İki yazar aynı anda /posts/2 adresine yazarsa gönderilerden biri diğeri tarafından silinir.

Bu sorunu çözmek için Firebase istemcileri, her yeni alt öğe için benzersiz bir push() işlevi sağlar. Benzersiz alt anahtarlar kullanarak, birden fazla istemci yazma çakışmaları konusunda endişelenmeden aynı anda aynı konuma çocuk ekleyebilir.

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

Benzersiz anahtar, zaman damgasına dayalı olduğundan liste öğeleri otomatik olarak kronolojik sıraya göre sıralanır. Firebase her blog yayını için benzersiz bir anahtar oluşturduğundan birden fazla kullanıcı aynı anda yayın eklerse yazma çakışması oluşmaz. Veritabanı verileriniz artık şu şekilde görünüyor:

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

JavaScript, Python ve Go'da push() işlevini çağırma ve ardından hemen set() işlevini çağırma kalıbı o kadar yaygındır ki Firebase SDK, ayarlanacak verileri doğrudan push() işlevine ileterek bu iki işlevi birleştirmenize olanak tanır:

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

push() tarafından oluşturulan benzersiz anahtarı alma

push() işlevini çağırdığınızda, anahtarı almak veya verileri ayarlamak için kullanabileceğiniz yeni veri yoluna bir referans döndürülür. Aşağıdaki kod, yukarıdaki örnekle aynı verileri oluşturur ancak artık oluşturulan benzersiz anahtara erişebiliriz:

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
Go
// 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

Gördüğünüz gibi, benzersiz anahtarın değerini push() referansınızdan alabilirsiniz.

Verileri Alma başlıklı sonraki bölümde, bu verileri Firebase veritabanından nasıl okuyacağımızı öğreneceğiz.

İşlem Verilerini Kaydetme

Eşzamanlı değişiklikler nedeniyle bozulabilecek karmaşık verilerle (ör. artımlı sayaçlar) çalışırken SDK, işlem işlemi sağlar.

Java ve Node.js'de, işlem işlemine iki geri çağırma işlevi sağlarsınız: bir güncelleme işlevi ve isteğe bağlı bir tamamlama geri çağırma işlevi. Python ve Go'da işlem işlemi engelleyici olduğundan yalnızca güncelleme işlevini kabul eder.

Güncelleme işlevi, verilerin mevcut durumunu bağımsız değişken olarak alır ve yazmak istediğiniz yeni durumu döndürmelidir. Örneğin, belirli bir blog yayınındaki yukarı oy sayısını artırmak istiyorsanız aşağıdaki gibi bir işlem yazarsınız:

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')
Go
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)
}

İşlemler varsayılan değer yazılmamışsa null ile çağrılabileceğinden, yukarıdaki örnek sayaç değerinin null olup olmadığını veya henüz artırılmadığını kontrol eder.

Yukarıdaki kod bir işlem işlevi olmadan çalıştırılsaydı ve iki istemci aynı anda kodu artırmaya çalışsaydı her ikisi de yeni değer olarak 1 yazardı. Bu da iki artış yerine tek artışla sonuçlanırdı.

Ağ Bağlantısı ve Çevrimdışı Yazma İşlemleri

Firebase Node.js ve Java istemcileri, etkin verilerin kendi dahili sürümlerini tutar. Veriler yazıldığında önce bu yerel sürüme yazılır. Ardından istemci, bu verileri veritabanı ve diğer istemcilerle "en iyi çaba" ilkesine göre senkronize eder.

Sonuç olarak, veritabanına yazma işlemleri, veritabanına herhangi bir veri yazılmadan önce yerel etkinlikleri hemen tetikler. Bu, Firebase kullanarak bir uygulama yazdığınızda uygulamanızın ağ gecikmesi veya internet bağlantısından bağımsız olarak yanıt vermeye devam edeceği anlamına gelir.

Bağlantı yeniden kurulduğunda, istemcinin özel kod yazmaya gerek kalmadan mevcut sunucu durumuna "yetişmesi" için uygun etkinlik grubu alınır.

Verilerinizi Güvenli Hale Getirme

Firebase Realtime Database, verilerinizin farklı düğümlerine hangi kullanıcıların okuma ve yazma erişimine sahip olduğunu tanımlamanıza olanak tanıyan bir güvenlik diline sahiptir. Bu konu hakkında daha fazla bilgiyi Verilerinizi Güvenceye Alma başlıklı makalede bulabilirsiniz.