انشاء كلاس الـ Entity

Mohammad Laifمنذ سنة

بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته

في هذا الدرس سنتعلم كيفية انشاء كلاس من نوع Entity حتى نهيئ المشروع لإنشاء قاعدة بيانات باستخدام الـ Room. فهي تعتبر الخطوه الاولى لإستخدام مكتبة الـ Room بل الخطوه الاولى لبناء تطبيقك!

 

ماذا سوف تقرئ في هذا الدرس؟

  • ماهي الـ Entity.
  • ماهو الفرق بين الـ Entity و الـ Model.
  • من ماذا تتكون كلاسات الـ Entity.
  • طريقة انشاء كلاسات الـ Entity.
  • طريقة ربط الكلاسين كعلاقة ام وطفل (مجلد وملف) باستخدام المفتاح ForeignKeys كما في الـ SQLite ولكن التطبيق هنا لقاعدة بيانات الـ Room.

 

ماهي الـ Entity؟
كلاس تمثل جدول في قاعدة البيانات. اي انك اذا اردت انشاء جدول ما فعليك انشاء كلاس من نوع Entity. اي انها تشارك في انشاء الـ Database Schema و عناصرها تحتوي على ContentValue وعلى مجموعة من الـ Uri.

 

ماهو الفرق بين الـ Entity و الـ Model؟
لايوجد فرق تقريباً, سوى ان الـ Entity يجب ان تحتوي على حقل id حتى تستخدم في قواعد البيانات. والفرق الوحيد الذي سوف تراه هو التسمية! فسنقوم بتسمية الـ Models لدينا Subject و Card الى SubjectEntity و CardEntity من الان فصاعداً. واذا احببت القرائة اكثر عن الفروقات اطلع على المصدر Entity vs Model vs View Model.

 

من الان فصاعداً قم بتسمية كلاسات الـ Model لديك بـ ModelEntity اي اذا كان تطبيقك يحتوي على كلاس موديل Book من الافضل اعادة تسميتها الى BookEntity حتى تكون على وفاق مع تسميات الـ Room فقط لاغير.

 

ماذا سننشئ؟

سننشئ في هذا الدرس كلاسين من نوع Entity وهما:

  • SubjectEntity.
  • CardEntity.

وهاتين الكلاسين سوف يمثلون جدولين في قاعدة البيانات مثال على ذلك:

Subject Table:
+------------+----------+-------+--------+
| subject_id | mTitle   | mDate | mColor |
+------------+----------+-------+--------+
| 1          | Math     | 2018  | 1      |
+------------+----------+-------+--------+
| 2          | Computer | 2019  | 9      |
+------------+----------+-------+--------+

subject_id <--- Foreign Key ---> mParentId

Card Table:
+---------+-----------+------------------+-----------------+-------+--------+
| card_id | mParentId | mFrontSide       | mBackSide       | mDate | mColor |
+---------+-----------+------------------+-----------------+-------+--------+
| 1       | 2         | front side text. | back side text. | 2019  | 2      |
+---------+-----------+------------------+-----------------+-------+--------+
| 2       | 2         | front side text. | back side text. | 2019  | 4      |
+---------+-----------+------------------+-----------------+-------+--------+

وسوف تكون العلاقة بينهما كعلاقة الملف والمجلد! المادة (Subject) و بطائقها (Card). اي كل مادة ستحتوي على عدد من البطاقات, وسوف نقوم بربطهم باستخدام مفتاح الـ Foreign Key كما هو معتاد في الـ SQLite.
 

من ماذا تتكون كلاسات الـ Entity؟
كل كلاس Entity يجب ان تتكون من:

  1. Fields (يجب ان يكون هناك حقل id).
  2. Construction.
  3. Getter and Setter.

هذه الاشياء الزاميه لصنع كلاس Entity مع الملاحظة انه يجب ان يكون هناك فقط Construction واحد, وإلا سوف يكون هنالك تضارب بينهم (سوف نتطرق للحل).

 

انشاء كلاس الـ SubjectEntity
نبدء بتعليم الكلاس بالنوتيشن Entity سيكون اسم الجدول في قاعدة البيانات باسم الكلاس, ونستطيع استخدام الخيار tableName لتغييره الى اسم مناسب:

@Entity(tableName = "subjects")
public class SubjectEntity {}

 

نقوم بانشاء حقل خاص للـ id ونعلمه بالنوتيشن PrimaryKey ومع الخيار autoGenerate نجعله ذي انشاء تلقائي, وايضاً نستخدم النوتيشن ColumnInfo مع الخيار name لتغيير اسمه ليتناسب معنا لاحقاً :

@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "subject_id")
private int mId;

 

نقوم بكتابة الحقول الاخرى الخاصة بعنصرنا بدون الحاجة الى تغيير اي شئ فيهم:

private String mTitle;
private Date mDate;
private Integer mColor;

 

الان بعد ان كتبنا جميع حقولنا نقمو بكتابة Construction ياخد جميع هذه الحقول (من متطلبات الـ Room):

// Room Construction
public SubjectEntity(@NonNull int id, String title, Date date, Integer color) {
    mId = id;
    mTitle = title;
    mDate = date;
    mColor = color;
}

 

نقوم بكتابة Construction خاص بنا مع استخدام النوتيشن Ignore حتى لايحدث تضارب في الـ Room مع الاخر:

// Our Construction
@Ignore
public SubjectEntity(String title, Date date, Integer color) {
    mTitle = title;
    mDate = date;
    mColor = color;
}

لاحظ الفرق بينهم هو وجود الحقل mId وعدم وجوده. فالـ Room تحتاج اليه حتى تستطيع كتابة id للعنصر في قاعدة البيانات.

 

في النهاية نقوم بكتابة جميع الـ Getter و Sitter للحقول:

// Getter and Setter pattern required for Room.
public int getId() {
    return mId;
}

public void setId(int id) {
    mId = id;
}

public String getTitle() {
    return mTitle;
}

public void setTitle(String title) {
    mTitle = title;
}

public Date getDate() {
    return mDate;
}

public void setDate(Date date) {
    mDate = date;
}

public Integer getColor() {
    return mColor;
}

public void setColor(Integer color) {
    mColor = color;
}

 

وتصبح الشفره للكلاس SubjectEntity كاملة كالتالي (انتبه لإستخدام الـ import حتى تتلافى الاخطاء):

package com.mzdhr.flashcards.database.entity;

import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;

import java.util.Date;

@Entity(tableName = "subjects")
public class SubjectEntity {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "subject_id")
    private int mId;
    private String mTitle;
    private Date mDate;
    private Integer mColor;

    // Room Construction
    public SubjectEntity(@NonNull int id, String title, Date date, Integer color) {
        mId = id;
        mTitle = title;
        mDate = date;
        mColor = color;
    }

    // Our Construction
    @Ignore
    public SubjectEntity(String title, Date date, Integer color) {
        mTitle = title;
        mDate = date;
        mColor = color;
    }

    // Getter and Setter pattern required for Room.
    public int getId() {
        return mId;
    }

    public void setId(int id) {
        mId = id;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        mTitle = title;
    }

    public Date getDate() {
        return mDate;
    }

    public void setDate(Date date) {
        mDate = date;
    }

    public Integer getColor() {
        return mColor;
    }

    public void setColor(Integer color) {
        mColor = color;
    }
}

 

انشاء كلاس الـ CardEntity

الخطوات مشابهه للخطوات السابقة, ولكن هنا يوجد اختلاف بسيط وهو:

سنقوم باضافة خيار foreignKeys لنوتيشن الـ Entity الخاص بهذه الكلاس. وايضاً سنقوم بأضافة حقل خاص للـ  Subject Id حتى نستطيع ربطهم مع بعضهم البعض كعلاقة مجلد و ملف. اي ان الكلاس SubjectEntity سوف تكون هي الام او المجلد, اما الكلاس CardEntity سوف تكون هي الطفل او الملف.

 

طريقة ربط الجدولين (الكلاسين SubjectEntity و CardEntity) باستخدام ForeignKeys

نقوم بانشاء حقل id في هذه الكلاس وظيفته هي تخزين الـ id الخاص بكلاس الـ Subject وسوف نسميه card_parent_id كالتالي:

@ColumnInfo(name = "card_parent_id")
private int mParentId;

 

الان نقوم بكتابة خيار الـ foreignKeys في النوتيشن Entity مع الاخرين للكلاس على هذا الشكل:

@Entity(
        tableName = "cards",
        foreignKeys = {
                @ForeignKey(entity = SubjectEntity.class,
                        parentColumns = "subject_id",
                        childColumns = "card_parent_id",
                        onDelete = ForeignKey.CASCADE)},
        indices = {@Index(value = "card_parent_id")}
)
public class CardEntity {
...
}

تعقيب:

  • الخيار tableName هو لتغيير اسم الجدول في قاعدة البيانات.
  • استخدمنا الخيار foreignKeys حتى نقوم بعملية الربط بين الجدولين التي تمثلاهما كلاس SubjectEntry و CardEntry.
  • استخدمنا الخيار entity حتى نقوم بتعليم كلاس الام (SubjectEntry).
  • ثم استخدمنا الخيار parentColumns لتعليم حقل الام (المجلد كمثال) (SubjectEntry) الموجود في كلاسها.
  • واستخدمنا الخيار childColumns لتعليم حقل الطفل (الملف كمثال) الموجود في هذه الكلاس.
  • اما الخيار indces فقمنا باستخدامه ليصبح الامر سريعاً في الاستعلام من قاعدة البيانات, حتى لاتقوم الـ SQL بالاستعلام عن الجداول كامله وهي تبحث عن الـ ids (اطلع على المصدر Defining Data قسم Annotate indices and uniqueness والمصدر SQL As Understood By SQLite).

 

وتصبح الشفره للكلاس CardEntity كاملة كالتالي (انتبه لإستخدام الـ import حتى تتلافى الاخطاء):

package com.mzdhr.flashcards.database.entity;


import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;

import java.util.Date;

@Entity(
        tableName = "cards",
        foreignKeys = {
                @ForeignKey(entity = SubjectEntity.class,
                        parentColumns = "subject_id",
                        childColumns = "card_parent_id",
                        onDelete = ForeignKey.CASCADE)},
        indices = {@Index(value = "card_parent_id")}
)
public class CardEntity {
    

    /**
     * Fields
     */
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "card_id")
    private int mId;
    @ColumnInfo(name = "card_parent_id")
    private int mParentId;
    private String mFrontSide;
    private String mBackSide;
    private Date mDate;

    // Room Construction
    public CardEntity(int id, String frontSide, String backSide, int parentId, Date date) {
        mId = id;
        mFrontSide = frontSide;
        mBackSide = backSide;
        mParentId = parentId;
        mDate = date;
    }

    // Our Construction
    @Ignore
    public CardEntity(String frontSide, String backSide, int parentId, Date date) {
        mFrontSide = frontSide;
        mBackSide = backSide;
        mParentId = parentId;
        mDate = date;
    }

    // Getter and Setter pattern required for Room.
    public int getId() {
        return mId;
    }

    public void setId(int id) {
        mId = id;
    }

    public int getParentId() {
        return mParentId;
    }

    public void setParentId(int parentId) {
        mParentId = parentId;
    }

    public String getFrontSide() {
        return mFrontSide;
    }

    public void setFrontSide(String frontSide) {
        mFrontSide = frontSide;
    }

    public String getBackSide() {
        return mBackSide;
    }

    public void setBackSide(String backSide) {
        mBackSide = backSide;
    }

    public Date getDate() {
        return mDate;
    }

    public void setDate(Date date) {
        mDate = date;
    }
}

 

نهاية الدرس

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

 

المصادر:

التعليقات (3)

علاء:

مرحبا 

شكرا جزيلا لك اخي الكريم 

لكن ممكن اعرف شو هو هاد ال نوتيشن بالضبط يعني شو يسوي 

Mohammad Laif:

عفواً اخي, وشكراً على تعليقك الرائع.

نوتيشن, اقصد به الـ Annotation, ومعناته ملاحظة.

في الحياة الطبيعية, لما نقرئ كتاب ونصادف نص مهم نقوم بتعلميه باستخدام قلم ماركر اصفر ثم نقوم بكتابة ملاحظة على جنب الورقه حتى يتسنى لنا فهمه وتذكره اسرع في المستقبل. هذه العمليه تسمى انوتيشن اي اننا قمنا بعمل Annotation للنص.

في البرمجة, يوجد الكثير من الـ annotation حتى انت تستطيع صنع لك واحده خاصه وتحديد مهمتها.

طريقة استخدامهم هي: نقوم بكتابة احدى الـ Annotation فوق الشفرة البرمجية كملاحظة للـ Compiler حتى يتذكر ويفهم ولايفزع في المستقبل عندما نحاول تنفيذ وتشغيل شفرتنا البرمجية.

مثال: عندما نعمل وراثة لكلاس ما Parent Class, ونريد تغيير سلوك ما Method في الـ Child Class. نقوم بتعليم ذلك السلوك بالنوتيشن @Override حتى لايفزع الـ Compiler, ويتسائل لماذا سلوك الابن مختلف عن سلوك الاب.

 

بالنسبة للـ Room

هي مكتبة خارجية ولذلك يأتي معها Annotation خاصين بها. تجدهم في هذا الجدول مع معلومات لهم: android.arch.persistence.room (لاداعي لحفظهم, فقط تذكر اين تجدهم ومتى تستخدمهم).

مثال بسيط لهم:

@Entity

و

@ColumnInfo(name = "card_id")

لاحظ اننا نستطيع ارفاق بعض المعلومات بداخل الاقواس مع الـ Annotation للـ Compiler حتى يفهم اكثر.

 

اتمنى مفهوم الـ نوتيشن تبسط لك اخي وهناك سلسلة دروس رائعة على موقع الجافا الرسمي باسم: Lesson: Annotations.

DrRo:

شرح ممتاز .. شكرا جزيلا على هذه الدورة المفيدة

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !