Skip to content

Best_Practice_ar

رامي مناف edited this page Aug 27, 2020 · 8 revisions

أفضل استخدام لقاعدة البيانات

قواعد حفظ البيانات

قواعد حفظ البيانات هي مجموعة من القوانين الّتي يسمح لك الالتزام بها بالتعامل بسهولة وسلاسة مع قاعدة البيانات, وعند حدوث أيّ خطأ راجع هذه القواعد.

قواعد كتابة الصّفوف:

  • يفضّل أن يتّبع الصّفّ قواعد حبوب قهوة جافا Java Beans حتى يستطيع المستخدم استخدام نفس الصفوف في بيئات عمل مثل Spring.
  • إن كان لديك حقولٌ لا تريد حفظها فيمكنك استخدام الكلمة المفتاحية transient قبل كتابة نوع المتغيّر إن كنت لم تحدد طريقة كتابة الصّفّ بنفسك.
  • حدد بنية الصّفوف الخاصة بك واحرص على عدم تكرار كتابة السطور باستخدام OOP.
  • اكتب جميع العمليات التي تحتاجها على شكل دوالٍّ في الصّفّ.
  • عدّل دائمًا على الدّالّة equals, لأن بعض أوامر صفوف تعتمد على هذه الدّالّة, ولن يعمل الأمر بالطريقة الصحيحة إن لم يتمّ التعديل عليها.

يوجد في صفوف ثلاثة Serializers أحدها JavaSerializer, فإذا قررت استخدامه فعليك أن تتبع تعليمات Java Serialization API في كتابة الصفوف حتى تستطيع استخدامه. ابدأ بالتأكّد من أنّ الصف يستخدم النافذة Serializable أو أنّ أحدًا من آبائه يستخدمها, وسيتمّ كتابة جميع حقول التي لم تُسبق بالكلمة transient أو final أو static, ويفضل دائمًا تحديد طريقة كتابة الصف وذلك عن طريق خطوات بسيطة ومهمّة في نفس الوقت, ويتمّ ذلك عن طريق كتابة دالّتين مسؤولتين عن كتابة وقراءة كائنات الصّفّ وهما readObject الّتي تستقبل ObjectInputStream كمعامل, وتقوم بقراءة بيانات الكائن, و writeObject الّتي تستقبل ObjectOutputStream كمعامل, وتقوم بكتابة بيانات الكائن, فعند تحديدهما ستتمكّن من إضافة وحذف أيّ حقلٍ من الصّفّ بسهولةٍ دون الحاجة لمسح البيانات المخزّنة باستخدام الصّفّ قبل التعديل , أمّا عن كيفيّة كتابة أو قراءة بيانات الكائن فذلك يتمّ عن طريق المعاملات الممررة, فالمعامل ObjectOutputStream في الدّالّة writeObject يحتوي على دوالّ لكتابة شتى أنواع البيانات البدائية والّتي يبدأ اسمها بالكلمة write يتبعها نوع البيانات مثلًا: writeFloat وwriteInt إلخ, وتختلف String عن البقيّة بكونها تكتب بالدالّة writeUTF, وفي حال كون الصّف يحتوي على حقولٍ لكائنات فيمكن كتابة تلك الكائنات عبر الدّالّة writeObject, ونفس الشيء تمامًا في حالة قراءة الكائن, فيتمّ استخدام ObjectInputStream والدّوالّ الّتي تبدأ بالكلمة read.

في حالة عدم تحديد طرق الكتابة والقراءة بنفسك فلن تتمكّن من استرجاع البيانات المخزّنة في الصّفّ قبل التعديل فذلك سيطلق استثناءً, لذلك فإن عمليّة تحديد طريقة القراءة والكتابة مهمّةٌ للمدى الطويل.

عند قراءة الكائن في الدالّة readObject لن يتمّ إنشاء الكائن من الباني الفارغ, لذلك فكل الحقول ستحوي اللا قيمة

هذا المثال يوضح تلك القواعد جميعها.

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Objects;

public class Student implements Serializable {

    private static final long serialVersionUID = 4702347432L;
    
    private String name;
    private int age;
    private String nationality;
    
    public Student(String name, int age, String nationality){
        this.nationality = nationality;
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getNationality() {
        return nationality;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    

    private void readObject(ObjectInputStream in) throws IOException {
        name = in.readUTF();
        age = in.readInt();
        nationality = in.readUTF();
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeUTF(name);
        out.writeInt(age);
        out.writeUTF(nationality);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Student other = (Student) obj;
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        if (!Objects.equals(this.age, other.age)) {
            return false;
        }
        if (!Objects.equals(this.nationality, other.nationality)) {
            return false;
        }
        return true;
    }

}

إن لاحظت, فإن تسلسل كتابة عمليات الكتابة هو نفسه تسلسل عمليات القراءة في الدّالّتين readObject وwriteObject, وهذا الأمر ضروريٌ جدًا.

خدعٌ وحيل لتسهيل استخدام JavaSerializer

يمكنك التّحايل على بعض المشاكل الّتي قد تواجهك في المستقبل أثناء كتابة الصّفوف الّتي سيتمّ حفظها في قاعدة البيانات, وهذا شرحٌ لأهمّ تلك المشاكل:

حفظ حقولٍ لا تستخدم النّافذة Serializable

عند بناء الصفوف المعقّدة ستجد أنّ أغلب حقولك لا تستخدم النّافذة Serializable, ويمكن التحايل على تلك المشكلة بتحديد طريقة كتابة وقراءة الكائن ثمّ استخراج البيانات الأساسيّة منه وحفظها, وعند القراءة فتقوم بقراءة البيانات الأساسيّة ثم تحويلها للكائن, فمثلًا خصائص JavaFX أو JavaFX Properties مثل SimpleStringProperty لا تستخدم النّافذة Serializable, ويمكن التحايل على ذلك الأمر في دالّتي قراءة وكتابة الكائن هكذا:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import javafx.beans.property.SimpleStringProperty;

public class Person implements Serializable {

    private static final long serialVersionUID = 8036435743L;
    
    private SimpleStringProperty name;

   public Person(){
      name = new SimpleStringProperty(this, "name", "");
   }
   
   private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeUTF(name.getValue());
    }
          
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        this.name = new SimpleStringProperty(this, "name", in.readUTF());
    }

}

إضافة وحذف حقولٍ من الصّفّ

بعد إطلاق مشروعك ورغبتك بتحديث المشروع قد تحتاج لحذف أو إضافة حقل أو أكثر, مما سيتسبب في إطلاق استثناءات أثناء قراءة البيانات القديمة, لكن يمكن التحايل على ذلك إذا كنت قد حددت طرق القراءة والكتابة من البداية, ويتطلب فهم ذلك فهم سبب انطلاق تلك الاستثناءات, فعند حذف حقل ما مثلًا لن يظهر أيّ استثناء (طبعًا في حالة قد تمّ تحديد طرق القراءة والكتابة), لكنّ ذلك الأمر قد يحدث بعض الأخطاء مستقبلًا إن أُضيف مكانه أو بعده حقولٌ أخرى في دالتي القراءة والكتابة, لذلك لتفادي هذا الأمر يمكنك كتابة القيمة الافتراضية لذلك الحقل في دالة الكتابة مثلا: 0, نص فارغ, إلخ, وعند القراءة اقرأ قيمته لكن لا تضعها في أيّ متغيّر (أهملها), أمّا عند إضافة حقلٍ آخر فيجب وضعه في نهاية عمليات الكتابة في دالّة الكتابة, وفي نهاية عمليات القراءة في دالّة القراءة, لكن عندما يقرأ البرنامج البيانات القديمة سيضطر لقراءة الحقل الجديد الغير مسجلٍ, وعندها سيُطلق الاستثناء EOFException, وهذا الاستثناء يطلق عند محاولة القراءة بعدما انتهت البيانات, ولتفادي هذا الاستثناء يمكننا إحاطة جميع الحقول الجديدة بالتركيب try-catch بحيث تلتقط ذلك الاستثناء, ويمكنك في تركيب catch وضع قيم افتراضيّة للحقول الجديدة, وهكذا عندما تقوم دالّة القراءة بقراءة البيانات القديمة سيُطلق الاستثناء وسيتمّ إمساكه بحيث لا يتسبب بإيقاف البرنامج, وهذا مثالٌ على صفٍّ والصّفّ بعد تحديثه:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 555646L;
    
    private String username;
    private String password;
    private String image;

    private void readObject(ObjectInputStream in) throws IOException {
        username = in.readUTF();
        password = in.readUTF();
        image = in.readUTF();
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeUTF(username);
        out.writeUTF(password);
        out.writeUTF(image);
    }
}

وهذا الصفّ بعد حذف الحقل image وإضافة الحقل nickname:

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 555646L;
    
    private String username;
    private String nickname;
    private String password;

    private void readObject(ObjectInputStream in) throws IOException {
        username = in.readUTF();
        password = in.readUTF();
        in.readUTF();
        try{
        nickname = in.readUTF();
        }catch(EOFException ex){}
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeUTF(username);
        out.writeUTF(password);
        out.writeUTF(null);
        out.writeUTF(nickname);
    }
}

Clone this wiki locally