Verileri Kaydetme

Bu dokümanda, Firebase Realtime Database'nize veri yazmayla ilgili dört yöntem ele alınmaktadır: ayarlama, güncelleme, itme ve işlem desteği.

Veri Tasarrufu Yöntemleri

set Verileri messages/users/<username> gibi tanımlı bir yola yazın veya değiştirin
update Verilerin tamamını değiştirmeden, tanımlanmış bir yolun anahtarlarından bazılarını güncelleme
push Veritabanındaki veriler listesine ekleme. Bir listeye her yeni düğüm eklediğinizde veritabanınız messages/users/<unique-user-id>/<username> gibi benzersiz bir anahtar oluşturur.
işlem 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 kaydeden ve bu yoldaki mevcut verileri değiştiren bir ayardır. Veri kümesini 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")

Bazı kullanıcı verilerini kaydederek başlayalım. Her kullanıcıyı benzersiz bir kullanıcı adıyla kaydederiz, ayrıca kullanıcının tam adı ve doğum tarihiyle ilgili bilgileri de kaydederiz. Her kullanıcının benzersiz bir kullanıcı adı olacağı için, zaten anahtara sahip olduğunuz ve bir tane oluşturmanız gerekmediği için push yöntemi yerine burada ayarlama yöntemini kullanmanız önerilir.

Öncelikle kullanıcı verileriniz için bir veritabanı referansı oluşturun. Ardından, kullanıcının kullanıcı adını, tam adını ve doğum tarihini içeren bir kullanıcı nesnesini veritabanına kaydetmek için set() / setValue() kullanın. Dize, sayı, doğru/yanlış, null, dizi veya herhangi bir JSON nesnesi gönderebilirsiniz. null iletilmesi, belirtilen konumdaki verileri kaldırır. Bu durumda, işleve bir nesne ileteceksiniz:

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öreceksiniz. 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 her iki değerin de bir nesne olarak aynı anda yazılması ve bunları alt konumlara ayrı ayrı yazılması, 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 etkinlik tetikler. İkinci örnek ise iki etkinlik tetikler. usersRef alanında halihazırda veriler varsa ilk yaklaşım verilerin üzerine yazar, ikinci yöntem ise yalnızca her bir ayrı alt düğümün değerini değiştirir ve usersRef öğesinin diğer alt öğelerini değiştirmeden bırakır.

Kayıtlı verileri güncelleme

Diğer alt düğümlerin üzerine yazmadan bir veritabanı konumunun birden fazla alt öğesine aynı anda 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 ayarla özelliğini kullansaydınız hopperRef cihazınızdan hem full_name hem de date_of_birth silinirdi.

Firebase Realtime Database, çok yollu güncellemeleri de destekler. Bu, güncellemenin artık veritabanınızdaki birden fazla konumdaki değerleri aynı anda güncelleyebileceği anlamına gelir. Bu güçlü özellik, verilerinizi normalleştirmenize yardımcı olur. Çok yollu güncellemeleri kullanarak hem Gizem 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 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"
    }
  }
}

Yolları dahil ederek nesneleri yazarak güncellemeye çalışmanın farklı bir davranışa neden olacağını unutmayın. Şimdi Gözde ve Alan'ı şu şekilde güncellemeyi denerseniz 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, farklı bir davranışa neden olur ve /users düğümünün tamamının üzerine yazılır.

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

Tamamlama Geri Çağırması Ekleme

Node.js ve Java Yönetici SDK'larında, verilerinizin ne zaman kayda alındığını bilmek isterseniz tamamlama geri çağırma işlevi ekleyebilirsiniz. Bu SDK'lardaki hem ayarlama hem de güncelleme yöntemleri, yazma işlemi veritabanına bağlandığında çağrılan isteğe bağlı bir tamamlama geri çağırma işlevi alır. Arama herhangi bir nedenle başarısız olursa geri çağırma işlevine, 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 bulundurmanız ve liste yapınızı buna göre düzenlemeniz önemlidir. Yukarıdaki örneği genişleterek uygulamanıza blog yayınları ekleyelim. İlk aklınıza gelen, alt öğeleri otomatik olarak artan tam sayı dizinleriyle depolamak için set kullanmak olabilir. Örneğin:

// 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 eklerse bu yayın /posts/2 olarak depolanır. Bu yöntem, yalnızca tek bir yazarın gönderi eklediği durumlarda işe yarar. Ancak ortak çalışma blog uygulamanızda birçok kullanıcı aynı anda gönderi ekleyebilir. İki yazar /posts/2'e aynı anda yazı 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 bir push() işlevi sağlar. Benzersiz alt anahtarlar kullanarak çeşitli istemciler, yazma çakışmaları konusunda endişelenmeden aynı konuma aynı anda alt öğe 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)
}

Tekil 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, birden fazla kullanıcı aynı anda yayın eklerse yazma çakışması yaşanmaz. Veritabanı verileriniz şu şekilde 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()'ü çağırdıktan sonra hemen set()'ü çağırma kalıbı o kadar yaygındır ki Firebase SDK'sı, ayarlanacak verileri doğrudan push()'e aşağıdaki gibi ileterek bunları 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ısı yapıldığında yeni veri yolu için bir referans döndürülür. Bu yolu, anahtarı almak veya ona veri ayarlamak için kullanabilirsiniz. Aşağıdaki kod, yukarıdaki örnekle aynı verileri döndürür ancak bu sefer 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 bir Firebase veritabanından nasıl okuyacağınızı öğreneceksiniz.

İşlem Verilerini Kaydetme

Artımlı sayaçlar gibi eşzamanlı değişiklikler nedeniyle bozulabilen karmaşık verilerle çalışırken SDK, işlem işlemi sağlar.

Java ve Node.js'de işlem işlemine iki geri çağırma işlevi gönderirsiniz: 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 engellenir ve bu nedenle yalnızca güncelleme işlevini kabul eder.

update 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ının aldığı up'ları 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ılabildiğinden sayıcı değerinin 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 aynı anda artırmak isterse her ikisi de yeni değer olarak 1 yazar. Bu da iki yerine bir artırımla sonuçlanır.

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

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

Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, veritabanına veri yazılmadan önce bile yerel etkinlikleri hemen tetikler. Yani Firebase'i kullanarak 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 kurulduktan sonra, istemcinin özel kod yazmak zorunda kalmadan mevcut sunucu durumuna "yaklaşması" için uygun etkinlik grubunu 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şimine sahip olacağını tanımlamanızı sağlayan bir güvenlik diline sahiptir. Bu konuyla ilgili daha fazla bilgiyi Verilerinizin Güvenliğini Sağlayın bölümünde bulabilirsiniz.