Verileri Kaydetme

Bu dokümanda, Firebase Realtime Database'inize veri yazmak için kullanılan dört yöntem ele alınmaktadır: ayarlama, güncelleme, aktarma ve işlem desteği.

Veri Tasarrufu Yolları

set messages/users/<username> gibi tanımlı bir yola veri yazma veya veri değiştirme
update Tüm verileri değiştirmeden tanımlanmış bir yol için anahtarlardan bazılarını güncelleme
push Veritabanındaki veri listesine ekleme. Bir listeye her yeni düğüm aktardığınızda veritabanınız messages/users/<unique-user-id>/<username> gibi benzersiz bir anahtar oluşturur.
transaction Eşzamanlı güncellemelerle bozulabilecek karmaşık verilerle çalışırken işlemleri kullanın

Verileri Kaydetme

Temel veritabanı yazma işlemi, belirtilen veritabanı referansına yeni veriler kaydederek söz konusu yoldaki mevcut tüm verileri değiştiren bir kümedir. Grubu anlamak için basit bir blog uygulaması oluşturacağız. Uygulamanızla ilgili veriler ş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")

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

Öncelikle, kullanıcı verileriniz için bir veritabanı referansı oluşturun. Ardından, kullanıcının kullanıcı adı, tam adı ve doğum günüyle birlikte veritabanına bir kullanıcı nesnesi kaydetmek için set() / setValue() kodunu kullanın. Bir dize, sayı, boole, null, dizi veya herhangi bir JSON nesnesini geçirebilirsiniz. null geçirildiğinde belirtilen konumdaki veriler kaldırılır. Bu durumda, ona bir nesne eklersiniz:

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

Veritabanına bir JSON nesnesi kaydedildiğinde nesne özellikleri, veritabanı alt konumlarına otomatik olarak iç içe yerleştirilmiş bir şekilde 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ürüz. Verileri doğrudan bir alt çocuğun konumuna 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 örnek (her iki değeri bir nesneyle aynı anda yazma ve bunları alt konumlara ayrı olarak yazma), aynı verilerin veritabanınıza kaydedilmesiyle sonuçlanır:

{
  "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. Veriler zaten usersRef konumunda mevcutsa ilk yaklaşım bu verilerin üzerine yazılır, ancak ikinci yöntem yalnızca ayrı alt düğümlerin değerini değiştirirken usersRef öğesinin diğer alt öğelerini değiştirmeden bırakır.

Kaydedilen Veriler Güncelleniyor

Bir veritabanı konumunun birden fazla alt öğesine aynı anda, diğer alt düğümlerin üzerine yazmadan yazmak istiyorsanız aşağıda gösterilen güncelleme 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, Gözde'nin verilerini takma adını içerecek şekilde günceller. Güncelleme yerine burada set kullansaydınız, hem full_name hem de date_of_birth hopperRef cihazınızdan silinirdi.

Firebase Realtime Database, çok yollu güncellemeleri de destekler. Diğer bir deyişle, güncelleme artık veritabanınızdaki birden fazla konumdaki değerleri aynı anda güncelleyebilir. Bu güçlü özellik, verilerinizi normalleştirmenize olanak tanır. Çok yollu güncellemeleri kullanarak hem Gözde 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 hem Alan hem de Gözde'nin 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, yolların bulunduğu nesneler yazarak güncellemeye çalışmanın farklı davranışla sonuçlanacağını unutmayın. Şimdi, Grace ve Alan'ı şu şekilde güncellemeyi denerseniz neler olduğuna bir göz atalı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, farklı 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 Çağırma İşlemi Ekleme

Node.js ve Java Yönetici 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. Çağrı herhangi bir nedenden dolayı başarısız olduysa geri çağırmaya hatanın nedenini belirten bir hata nesnesi iletilir. Python ve Go Admin SDK'larında, tüm yazma yöntemleri engellemektedir. Yani yazma işlemleri veritabanına kaydedilene kadar yazma yöntemleri 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ğıdakiler gibi otomatik olarak artan tam sayı dizinlerine sahip çocukları 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 depolanır. Bu, yalnızca tek bir yazar yayın ekleyecekse işe yarar; ancak ortak çalışmaya dayalı blog uygulamanızda birçok kullanıcı aynı anda yayın ekleyebilir. İki yazar /posts/2 alanına aynı anda yazarsa yayınlardan biri diğeri tarafından silinir.

Bu sorunu çözmek için Firebase istemcileri, her yeni alt öğe için benzersiz bir anahtar oluşturan push() işlevi sunar. Birçok istemci, benzersiz alt anahtarlar kullanarak yazma çakışmaları konusunda endişelenmeden alt öğeleri aynı anda aynı konuma 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 bir zaman damgasına dayanır, bu nedenle liste öğeleri otomatik olarak kronolojik olarak sıralanır. Firebase her blog yayını için benzersiz bir anahtar oluşturduğundan aynı anda birden fazla kullanıcı bir yayın eklerse yazma çakışması olmaz. Veritabanı verileriniz artık aşağıdaki gibi görünür:

{
  "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() ve ardından hemen set() çağrısı yapılırken kullanılan kalıp o kadar yaygındır ki Firebase SDK'sı, verileri doğrudan push() öğesine ileterek bunları aşağıdaki gibi 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() çağrıldığında, 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 sağlar, 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 öğrenebilirsiniz.

Veri Alma konulu bir sonraki bölümde, bu verilerin Firebase veritabanından nasıl okunacağını öğreneceğiz.

İşlem Verilerini Kaydetme

Eş zamanlı sayaçlar gibi eş zamanlı değişiklikler tarafından bozulabilecek karmaşık verilerle çalışırken SDK bir işlem işlemi sağlar.

Java ve Node.js'de, işlem işlemine bir güncelleme işlevi ve bir isteğe bağlı tamamlama geri çağırması olmak üzere iki geri çağırma sağlarsınız. Python ve Go'da, işlem işlemi engellediğinden 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ürmesi gerekir. Örneğin, belirli bir blog yayınındaki olumlu 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)
}

Yukarıdaki örnekte, varsayılan değer yazılmamışsa işlemler null ile çağrılabileceği için sayacın null olup olmadığı veya henüz artırılıp artırılmadığı kontrol edilir.

Yukarıdaki kod bir işlem işlevi olmadan çalıştırılmışsa ve iki istemci bunu aynı anda artırmayı denemişse her ikisi de yeni değer olarak 1 ifadesini yazar ve sonuçta iki yerine bir artış elde edilir.

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

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

Sonuç olarak veritabanına yapılan tüm yazma işlemleri, daha veritabanına herhangi bir veri yazılmadan önce yerel etkinlikleri hemen tetikler. Bu sayede, Firebase kullanarak bir uygulama yazdığınızda uygulamanız ağ gecikmesinden veya internet bağlantısından bağımsız olarak yanıt vermeye devam eder.

Bağlantı yeniden sağlandıktan sonra istemcinin özel kod yazmak zorunda kalmadan geçerli sunucu durumunu "yakalaması" için uygun etkinlik kümesini alırız.

Verilerinizin Güvenliğini Sağlama

Firebase Realtime Database, verilerinizin farklı düğümlerine hangi kullanıcıların okuma ve yazma erişimi olduğunu tanımlamanızı sağlayan bir güvenlik diline sahiptir. Verilerinizin Güvenliğini Sağlayın bölümünden daha fazla bilgi edinebilirsiniz.