ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

حفظ البيانات

يغطي هذا المستند الطرق الأربعة لكتابة البيانات في Firebase Realtime Database: دعم المجموعة والتحديث والدفع والمعاملات.

طرق حفظ البيانات

جلس اكتب أو استبدل البيانات إلى مسار محدد ، مثل messages/users/<username>
تحديث قم بتحديث بعض المفاتيح لمسار محدد دون استبدال جميع البيانات
إدفع إضافة إلى قائمة البيانات في قاعدة البيانات. في كل مرة تدفع فيها عقدة جديدة إلى قائمة ، تقوم قاعدة البيانات الخاصة بك بإنشاء مفتاح فريد ، مثل messages/users/<unique-user-id>/<username>
عملية تجارية استخدم المعاملات عند العمل مع البيانات المعقدة التي قد تتلف من خلال التحديثات المتزامنة

حفظ البيانات

عملية كتابة قاعدة البيانات الأساسية هي مجموعة تحفظ البيانات الجديدة في مرجع قاعدة البيانات المحدد ، لتحل محل أي بيانات موجودة في هذا المسار. لفهم المجموعة ، سننشئ تطبيقًا بسيطًا للتدوين. يتم تخزين بيانات تطبيقك في مرجع قاعدة البيانات هذه:

جافا
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");
بيثون
# Import database module.
from firebase_admin import db

# Get a database reference to our blog.
ref = db.reference('server/saving-data/fireblog')
اذهب
// 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")

لنبدأ بحفظ بعض بيانات المستخدم. سنقوم بتخزين كل مستخدم باسم مستخدم فريد ، وسنقوم أيضًا بتخزين الاسم الكامل وتاريخ الميلاد. نظرًا لأن كل مستخدم سيكون له اسم مستخدم فريد ، فمن المنطقي استخدام الطريقة المحددة هنا بدلاً من طريقة الدفع نظرًا لأن لديك بالفعل المفتاح ولا تحتاج إلى إنشاء واحد.

أولاً ، قم بإنشاء مرجع قاعدة بيانات لبيانات المستخدم الخاصة بك. ثم استخدم set() / setValue() لحفظ كائن مستخدم في قاعدة البيانات باسم المستخدم للمستخدم والاسم الكامل setValue() الميلاد. يمكنك تمرير تعيين سلسلة أو رقم أو قيمة منطقية أو null أو صفيف أو أي كائن JSON. سيؤدي تمرير القيمة null إلى إزالة البيانات في الموقع المحدد. في هذه الحالة ستمرره كائنًا:

جافا
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"
  }
});
بيثون
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'
    }
})
اذهب

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

عند حفظ كائن JSON في قاعدة البيانات ، يتم تعيين خصائص الكائن تلقائيًا إلى مواقع تابعة لقاعدة البيانات بطريقة متداخلة. الآن إذا انتقلت إلى عنوان URL https://docs-examples.firebaseio.com/server/saving-data/fireblog/users/alanisawesome/full_name ، فسترى القيمة "Alan Turing". يمكنك أيضًا حفظ البيانات مباشرةً في موقع تابع:

جافا
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"
});
بيثون
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'
})
اذهب
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)
}

سيؤدي المثالان أعلاه - كتابة كلتا القيمتين في نفس الوقت ككائن وكتابتهما بشكل منفصل إلى مواقع تابعة - إلى حفظ البيانات نفسها في قاعدة البيانات الخاصة بك:

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

سيقوم المثال الأول بتشغيل حدث واحد فقط على العملاء الذين يشاهدون البيانات ، في حين أن المثال الثاني سيؤدي إلى حدثين. من المهم أن نلاحظ أنه إذا كانت البيانات موجودة بالفعل في usersRef ، فإن الأسلوب الأول usersRef ، ولكن الطريقة الثانية usersRef فقط قيمة كل عقدة usersRef منفصلة بينما تترك الأطفال الآخرين usersRef دون تغيير.

تحديث البيانات المحفوظة

إذا كنت تريد الكتابة إلى عدة توابع لموقع قاعدة بيانات في نفس الوقت دون استبدال العقد الفرعية الأخرى ، فيمكنك استخدام طريقة التحديث كما هو موضح أدناه:

جافا
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"
});
بيثون
hopper_ref = users_ref.child('gracehop')
hopper_ref.update({
    'nickname': 'Amazing Grace'
})
اذهب
hopperRef := usersRef.Child("gracehop")
if err := hopperRef.Update(ctx, map[string]interface{}{
	"nickname": "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating child:", err)
}

سيؤدي هذا إلى تحديث بيانات Grace لتضمين لقبها. إذا كنت قد استخدمت مجموعة هنا بدلاً من التحديث ، full_name كلاً من full_name و date_of_birth من hopperRef .

كما تدعم قاعدة بيانات Firebase Realtime التحديثات متعددة المسارات. وهذا يعني أن التحديث يمكنه الآن تحديث القيم في مواقع متعددة في قاعدة بياناتك في نفس الوقت ، وهي ميزة قوية تسمح لك بمحو بياناتك . باستخدام التحديثات متعددة المسارات ، يمكنك إضافة ألقاب إلى كل من Grace و Alan في نفس الوقت:

جافا
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"
});
بيثون
users_ref.update({
    'alanisawesome/nickname': 'Alan The Machine',
    'gracehop/nickname': 'Amazing Grace'
})
اذهب
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)
}

بعد هذا التحديث ، تمت إضافة ألقاب كل من آلان وجريس:

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

لاحظ أن محاولة تحديث الكائنات عن طريق كتابة كائنات بمسارات مضمنة سيؤدي إلى سلوك مختلف. دعنا نلقي نظرة على ما يحدث إذا حاولت بدلاً من ذلك تحديث Grace و Alan بهذه الطريقة:

جافا
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"
  }
});
بيثون
users_ref.update({
    'alanisawesome': {
        'nickname': 'Alan The Machine'
    },
    'gracehop': {
        'nickname': 'Amazing Grace'
    }
})
اذهب
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)
}

يؤدي هذا إلى سلوك مختلف ، أي استبدال العقدة بأكملها /users :

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

إضافة رد اتصال مكتمل

في Node.js و Java Admin SDKs ، إذا كنت ترغب في معرفة متى تم الالتزام ببياناتك ، يمكنك إضافة رد اتصال كامل. تأخذ كل من طريقتين الضبط والتحديث في حزم SDK هذه رد اتصال إتمام اختياري يتم استدعاؤه عند الالتزام بالكتابة في قاعدة البيانات. إذا كانت المكالمة غير ناجحة لسبب ما ، يتم تمرير رد الاتصال كائن خطأ يشير إلى سبب حدوث الفشل. في حزم SDK لـ Python و Go Admin ، يتم حظر جميع طرق الكتابة. بمعنى ، لا تعود طرق الكتابة حتى تلتزم عمليات الكتابة بقاعدة البيانات.

جافا
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.");
  }
});

حفظ قوائم البيانات

عند إنشاء قوائم البيانات ، من المهم أن تضع في اعتبارك طبيعة المستخدمين المتعددين لمعظم التطبيقات وتعديل هيكل القائمة وفقًا لذلك. بالتوسع في المثال أعلاه ، دعنا نضيف مشاركات المدونة إلى تطبيقك. قد تكون غريزتك الأولى هي استخدام مجموعة لتخزين الأطفال باستخدام فهارس أعداد متزايدة تلقائيًا ، مثل ما يلي:

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

إذا قام مستخدم بإضافة منشور جديد ، فسيتم تخزينه كـ /posts/2 . سيعمل هذا إذا كان مؤلف واحد فقط يضيف منشورات ، ولكن في تطبيق التدوين التعاوني الخاص بك ، يمكن للعديد من المستخدمين إضافة منشورات في نفس الوقت. إذا كتب مؤلفان إلى /posts/2 وقت واحد ، فسيتم حذف أحد المنشورات بواسطة الآخر.

لحل هذه المشكلة ، يوفر عملاء Firebase وظيفة push() تولد مفتاحًا فريدًا لكل طفل جديد . باستخدام المفاتيح الفرعية الفريدة ، يمكن للعديد من العملاء إضافة الأطفال إلى نفس الموقع في نفس الوقت دون القلق بشأن تعارضات الكتابة.

جافا
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"
});
بيثون
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'
})
اذهب

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

يعتمد المفتاح الفريد على طابع زمني ، لذلك سيتم ترتيب عناصر القائمة تلقائيًا ترتيبًا زمنيًا. نظرًا لأن Firebase ينشئ مفتاحًا فريدًا لكل مشاركة مدونة ، فلن تحدث تعارضات في الكتابة إذا قام العديد من المستخدمين بإضافة منشور في نفس الوقت. تبدو بيانات قاعدة البيانات الخاصة بك الآن كما يلي:

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

في JavaScript و Python و Go ، يعد نمط push() ثم set() الاتصال فورًا set() أمرًا شائعًا للغاية بحيث تتيح لك Firebase SDK دمجها بتمرير البيانات لتعيينها مباشرةً push() النحو التالي:

جافا
// 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"
});
بيثون
# This is equivalent to the calls to push().set(...) above
posts_ref.push({
    'author': 'gracehop',
    'title': 'Announcing COBOL, a New Programming Language'
})
اذهب
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() إلى إرجاع مرجع إلى مسار البيانات الجديد ، والذي يمكنك استخدامه للحصول على المفتاح أو تعيين البيانات عليه. سيؤدي الرمز التالي إلى نفس البيانات كما في المثال أعلاه ، ولكن الآن سيكون لدينا إمكانية الوصول إلى المفتاح الفريد الذي تم إنشاؤه:

جافا
// 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;
بيثون
# 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
اذهب
// 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

كما ترى ، يمكنك الحصول على قيمة المفتاح الفريد من مرجع push() الخاص بك.

في القسم التالي حول استرداد البيانات ، سنتعلم كيفية قراءة هذه البيانات من قاعدة بيانات Firebase.

حفظ بيانات المعاملات

عند العمل مع البيانات المعقدة التي قد تتلف من خلال التعديلات المتزامنة ، مثل العدادات الإضافية ، توفر حزمة SDK عملية معاملات .

في Java و Node.js ، تقوم بإعطاء عملية المعاملة استرجاعين: وظيفة تحديث واستدعاء إكمال اختياري. في Python and Go ، يتم حظر عملية المعاملة وبالتالي فهي تقبل وظيفة التحديث فقط.

تأخذ وظيفة التحديث الحالة الحالية للبيانات كوسيطة ويجب أن تُرجع الحالة المرغوبة الجديدة التي ترغب في كتابتها. على سبيل المثال ، إذا كنت ترغب في زيادة عدد الأصوات الإيجابية في مشاركة مدونة معينة ، فيمكنك كتابة معاملة مثل ما يلي:

جافا
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;
});
بيثون
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')
اذهب
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)
}

يتحقق المثال أعلاه لمعرفة ما إذا كان العداد null أو لم يتم زيادته بعد ، حيث يمكن استدعاء المعاملات null إذا لم تتم كتابة قيمة افتراضية.

إذا تم تشغيل الرمز أعلاه بدون وظيفة معاملة وحاول عميلان زيادته في وقت واحد ، فسيكتب كلاهما 1 كقيمة جديدة ، مما يؤدي إلى زيادة واحدة بدلاً من اثنتين.

اتصال الشبكة والكتابة دون اتصال

يحتفظ عملاء Firebase Node.js و Java بنسختهم الداخلية لأي بيانات نشطة. عندما تتم كتابة البيانات ، يتم كتابتها إلى هذا الإصدار المحلي أولاً. ثم يقوم العميل بمزامنة تلك البيانات مع قاعدة البيانات ومع العملاء الآخرين على أساس "أفضل جهد".

ونتيجة لذلك ، ستقوم جميع عمليات الكتابة إلى قاعدة البيانات بتشغيل الأحداث المحلية على الفور ، حتى قبل كتابة أي بيانات في قاعدة البيانات. هذا يعني أنه عندما تكتب تطبيقًا باستخدام Firebase ، سيبقى تطبيقك متجاوبًا بغض النظر عن وقت استجابة الشبكة أو اتصال الإنترنت.

بمجرد إعادة إنشاء الاتصال ، سوف نتلقى مجموعة الأحداث المناسبة بحيث "يلحق العميل" بحالة الخادم الحالية ، دون الحاجة إلى كتابة أي رمز مخصص.

تأمين بياناتك

تحتوي قاعدة بيانات Firebase Realtime على لغة أمان تتيح لك تحديد المستخدمين الذين لديهم حق الوصول للقراءة والكتابة إلى العقد المختلفة لبياناتك. يمكنك قراءة المزيد عنها في تأمين بياناتك .