Posts Tagged ‘sqlite’

HTML5 storage الجزء الثانى

14/08/2009

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

فى هذا الجزء أتناول كيفيه تخزين البيانات فى قاعده بيانات SQLite من خلال local JavaScript Database api

مقدمه
إنشاء و فتح قاعده البيانات
إنشاء جدول جديد
إضافه بيانات للجدول
إستخدام العبارات الجاهزه
إختيار بيانات من الجدول
التعامل مع اخطاء تنفيذ ال SQL
أرقام الأخطاء و مدلولاتها
التعامل مع اخطاء ال transaction
التعامل مع اصدارات قاعده البيانات
الدعم من المتصفحات
للمزيد

مقدمه

تحتوى مواصفات HTML5 على وصف ل Local JavaScript Database قاعده بيانات محليه تمكنك من تخزين البيانات داخل متصفح المستخدم فى قاعده بيانات SQlite بدلا من الكوكيز التى تقتصر مساحتها التخزينيه على 4 كيلو فقط ، قاعده البيانات SQLite على ملايين اجهزه المحمول و الاجهزه الكفيه و السيرفر و الكثير من تطبيقات سطح المكتب لصغر حجمها و مرونتها و تدعيمها شبه الكامل ل SQL ANSI ، للمزيد عن SQL و SQLite داخل المتصفح من هنا ، العمليات التى تتم على قاعده البيانات تكون غير تزامنيه asynchronous اى تتطلب كل عمليه تسجيل وظيفه تستمع لحدث نجاح العمليه او حدوث خطأ ، كما ان العمليات كلها تتم بنظام transaction لأن بيئه المتصفح غير مستقره ، يمكن لأى عمليه ان يقطعها refresh او اغلاق نافذه المتصفح ، اى انه إذا حدث خطأ فى عمليه مكونه من عده خطوات فإن قاعده البيانات لن تتأثر بأى من هذه الخطوات ، مثلا اذا كنت تغير بعض البيانات من جدول A ثم تضيف بيانات فى جدول B و تحذف بيانات من جدول C و قمت بإغلاق المتصفح فى منتصف خطوه اضافه البيانات للجدول B ، فإن خطوه تغيير البيانات من الجدول A سيتم التراجع عنها و لن يحدث اى تأثير ، إلا اذا تمت جميع عمليات ال transaction بنجاح .

إنشاء و فتح قاعده بيانات

قبل ان تتمكن من إستخدام قاعده البيانات عليك اولا فتح اتصال بها ، عندما تقوم بفتح اتصال سيتم انشاء قاعده بيانات جديده فارغه اذا لم يتم انشائها من قبل ، لذلك فإن عمليه فتح و انشاء قاعده البيانات متماثله ، يتم فتح الاتصال بقاعده البيناات عن طريق عن طريق الوظيفه openDatabase التى تقبل اربع عبارات : العباره الاولى هى إسم قاعده البيانات ، العباره التانيه هى إصدار قاعده البيانات للمزيد انظر الجزء الخاص بالتعامل مع اصدارات قاعد البيانات ، العباره الثالثه هى الإسم الذى يستخدمه المتصفح للتفاعل مع المستخدم مثلا عند طلب إذن بزياده الحجم المتاح لقاعده البيانات ، العباره الرابعه هى الحجم الذى تتوقع ان تشغله قاعده البيانات التى تقوم بالعمل عليها و اذا تعدت هذا الحجم المتفق عليه سيتم أخذ إذن المستخدم بأعطاء المزيد من الحجم لقاعده البيانات ، المثال التالى يوضح إنشاء قاعده بيانات بإسم keepondev إصدار 1.0 و حجمها 64 كيلوبايت و لها الإسم keepondev database لإستخدامه عند التفاعل مع المستخدم :

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);

إنشاء جدول جديد

بقيه امثله هذا الموضوع تعتمد على جدول بإسم goals يحتوى على عمودين id و name :

CREATE TABLE IF NOT EXISTS goals (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL
);

يمكنك انشاء هذا الجدول من خلال تنفيذ جمله ال SQL السابقه على قاعده البيانات keepondev عن طريق الوظيفه executeSql التابعه للكائن SQLtransaction – فى المثال بإسم tx – الذى يتم تمريره للوظيفه التى تمرر للوظيفه transaction 8O التابعه للكائن الناتج من openDatabase كما يوضح الكود التالى :

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);

function resultHandler(){
    alert("success");
};
function errorHandler(){
    alert("fail");
};
db.transaction(function(tx){
    var sql = "CREATE TABLE IF NOT EXISTS goals(";
    sql += "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
    sql += "name TEXT NOT NULL DEFAULT 'secret goal');";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

الوظيفه transaction تقبل وظيفه يمرر لها كائن من نوع SQLTransaction بإسم tx فى المثال السابق ، الكائن tx يحتوى الوظيفه executeSql التى تقبل اربع عبارات ، العباره الاولى هى جمله ال SQL التى قمت بوضعها فى المتغير sql و كتبتها على اكثر من سطر حتى لا تخرج عن عرض المدونه ، العباره التانيه مصفوفه تقبل قيم العبارت الجاهزه للمزيد انظر الجزء الخاص بإستخدام العبارات الجاهزه prepared statements ، العباره الثالثه وظيفه يتم إستدعائها اذا تم تنفيذ جمله ال SQL بنجاح ، و العباره الرابعه وظيفه يتم إستدعائها اذا حدث خطأ اثناء تنفيذ جمله ال SQL ، تلك الوظيفتين ستقوم بإظهار نافذه منبثقه ب success او fail فقط ، للمزيد انظر الجزء الخاص بأخطاء تنفيذ ال SQL و الجزء الخاص بأخطاء ال transaction ، يمكنك التأكد من انشاء الجدول و قاعده البيانات بنجاح داخل المتصفح Safari 4 من خلال فتح ال tab الخاص ب database الذى يمكن الحصول عليه من Sesstings > preferences > advanced > show develop menu in menu bar ، ستجد قائمه جديده بإسم develop دخلت فى القائمه file ، قم بقتح tab ال database كما توضح الصوره التاليه :

Safari Database tab

داخل tab ال database ستجد قاعده بيانات بإسم keepondev تحتوى على جدول فارغ بإسم goals ، يالا بينا نملاه .

إضافه بيانات للجدول

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

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);
db.transaction(function(tx){
    var sql = "INSERT INTO goals (name) VALUES ('world domination')";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

الشىء الوحيد المختلف فى المثال السابق هى جمله ال INSERT التى تقوم بإضافه صف جديد فى الجدول goals و تظبط قيمه العمود name الى world domination اما العمود id فيأخذ رقم تلقائى ، هناك مأخذ على طريقه الاضافه السابقه : لو احتوت القيمه على ‘ سيحدث error و يمكن ايضا حقن جمله SQL و عمل هجمه من نوع SQL injection ، الطريقه الافضل لإضافه البيانات هى إستخدام العبارت الجاهزه .

إستخدام العبارات الجاهزه

إستخدام العباره الجاهزه يمكنك من إستخدام ال ‘ و ال ” داخل القيم بدون الخوف على كسر جمله ال SQL ، ثم انه يستحيل حقن SQL داخل العبارت الجاهزه و عليه لا يمكن حدوث هجمات SQL injection ، و تزيد كفائه تنفيذ عمليه اضافه اكثر من صف فى الجدول .

قم بوضع علامه استفهام ؟ بدلا من وضع قيمه العمود فى جمله ال SQL ، و بداخل المصفوفه [] التى هى العباره التانيه للوظيفه executeSql قم بإضافه القيمه ، او ضيفها من ناتج مدخل المستخدم كما يوضح الكود البسيط التالى :

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);
db.transaction(function(tx){
    var sql = "INSERT INTO goals (name) VALUES (?)";
    tx.executeSql(sql, ["Conference speaker"], resultHandler, errorHandler);
    tx.executeSql(sql, ["technology innovator"], resultHandler, errorHandler);
    tx.executeSql(sql, ["JavaScript evangelist"], resultHandler, errorHandler);
    tx.executeSql(sql, ["most infeluence person of all time"], resultHandler
    , errorhandler);
    tx.executeSql(sql, ["space trip"], resultHandler, errorhandler);
});

يمكنك منع التكرار فى الكود بإستخدام partial function و النتيجه كما تظهر فى الصوره التاليه :

database tab showing keepondev goals table

إختيار بيانات من الجدول

اختيار بيانات من الجدول عباره عن استخدام جمله SQL من نوع SELECT بالإضافه إلى إستقبال البيانات من خلال الوظيفه resultHandler التى كانت وظيفتها فى الامثله السابقه عمل alert بالنص Success فقط ، عند نجاح تنفيذ جمله ال SQL على قاعده البيانات ، يتم تمرير عبارتين للوظيفه resultHandler ، العباره الاولى هى كائن من نوع SQLtTansaction و العباره التانيه كائن من نوع SQLResultSet يحتوى على الصفوف الناتجه من تنفيذ جمله ال SQL ، حيث يحتوى على الخاصيه rows التى هى بمثابه مصقوفه تمثل صفوف الجدول التى رجعت ، يمكننا استخلاص البيانات منها كما يوضح المثال البسيط التالى :

function resultHandler(tx, result){
    var rows = result.rows;
    var rowsLen = rows.length;
    for(var i = 0; i < rowsLen; i += 1){
        var row = rows.item(i);
        var id = row["id"];
        var name = row["name"];
        alert(id + " : " + name);
    }
};

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);
db.transaction(function(tx){
    var sql = "SELECT * FROM goals";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

فى المثال السابق قمت بإستخلاص جميع البيانات من الجدول goals و استقبلت الناتج داخل الوظيفه resultHandler و قمت بعمل alert للصف لكن يمكنك حقنه فى ال DOM و عمل pagination و عمل LIMIT داخل جمله ال SQL حتى لا تتأثر كفائه التطبيق .

إذا كانت الجمله السابقه INSERT يمكنك معرفه ال id الجديد للصف من خلال result.insertId و عدد الصفوف التى تأثرت من result.rowsModified من داخل resultHandler .

التعامل مع اخطاء تنفيذ ال SQL

يتم إستدعاء الوظيفه errorHandler عند حدوث خطأ فى تنفيذ جمله ال SQL مع تمرير عبارتين لها ، العباره الاولى هى كائن من نوع SQLTransaction ، و العباره التانيه كائن من نوع SQLError يحتوى على معلومات عن الخطأ الذى حدث ، الوظيفه errorHandler اذا قامت بإرجاع true سيتم الغاء جميع العمليات فى ال transaction الحالى ، و اذا قامت ب return false سيتم التغاضى عن الخطأ و لن يتم الغاء ال transaction ، الكائن SQLError يحتوى على خاصيتين ، الخاصيه الاولى message و هى رساله توضح الخطأ الذى حدث يمكنك استخدامها فى ال debugging ، اما الخاصيه التانيه هى code فهى رقم يدل على الخطأ الذى حدث ، انظر الجزء الخاص بأرقام الاخطاء .

function errorHandler(tx, error){
    alert(error.message); // unrecognized token: 3awzo
    alert(error.code); // 1
};
db.transaction(function(tx){
    var sql = "SELECT elly ana 3awzo yabni";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

أرقام الاخطاء و مدلولاتها

داخل الوظيفه errorHandler ناتج error.code رقم يوضح طبيعه الخطأ ، ال 0 يوضح ان الخطأ غير متعلق بقاعده البيانات ، و 1 يوضح ان الخطأ له علاقه بقاعده البيانات ، 2 يوضح ان الخطأ اصدار قاعده البيانات ليس اصدار القاعده التى طلبتها للمزيد انظر الجزء الخاص بإصدارات قاعده البيانات ، و 3 يوضح ان نتيجه جمله ال SQL كبيره جدا و فى هذه الحاله عليك إستخدام LIMIT و OFFSET حتى تحدد عد الصفوف الناتجه من جمله ال SQL ، و 4 توضح ان الحجم المتاح لقاعده البيانات قد تعدى و ان المستخدم رفض اعطاء المزيد من الحجم عند سؤال المتصفح له ، و 5 يدل على ان الخطأ من نوع lock contention error و عليك اعاده تنفيذ ال transaction ، و 6 يل على constraint error مثلا عند تكرار قيمه فى صفين لعمود معلم ك UNIQUE او عدم اعطاء قيمه لعمود NOT NULL .

التعامل مع اخطاء ال transaction

كما تمكننا من التعامل مع اخطاء تنفيذ جمله ال SQL ، يمكنك ايضا اتخاذ اجراءات عند وقوع خطأ فى تنفيذ ال transaction الذى قد يحتوى على تنفيذ عده جمل SQL ، الوظيفه transaction تقبل ثلاث عبارات ، فى الامثله السابقه كلها رأينا العباره الاولى فقط و هى عباره عن وظيفه يمرر لها الكائن SQLTransaction ، اما العباره الثانيه التى تمرر للوظيفه transaction هى وظيفه يتم استدعائها عند حدوث خطأ فى ال transaction و العباره الثالثه يتم استدعائها عند نجاح ال transaction كما يوضح المثال البسيط التالى :

function txError(error){
    alert(error.message);
    alert(error.code);
};
function txSuccess(){
    alert("transaction succeed");
};

db.transaction(function(tx){ 
    // execute some sql queries here
}, txError, txSuccess);

فى المثال السابق سيتم استدعاء txSuccess اذا نجح ال transaction بدوت اى اخطاء ، و سيتم استدعاء الوظيفه txError مع تمرير كائن SQLError لها ، يمكن استخلاص بيانات منها من خلال error.message او error.code للمزيد انظر أرقام الاخطاء و مدلولاتها .

التعامل مع اصدارات قاعده البيانات

الوظيفه openDatabase التى تقوم بفتح قاعده البيانات تقبل العباره التانيه لها إصدار قاعده البيانات ، اذا قمت بتحديد اصدار خطأ سيتم استدعاء الوظيفه المرتبطه بحدوث الخطأ ، انظر الجزء الخاص بأرقام الاخطاء و مدلولاتها ، و اذا اعطيت اصدار القاعده عباره عن نص فارغ ، سيتم فتح قاعده البيانات بغض النظر عن إصدارها كما يوضح الكود البسيط التالى :

var db = openDatabase("keepondev", "", "Keepondev database", 65536);

يمكنك معرفه اصدار قاعده البيانات بعد ذلك من خلال الخاصيه verison كما يلى

alert(db.version); // 1.0

الكود السابق نتج عنه 1.0 لأنه لا يوجد إصدارات من قاعده بيانات بإسم keepondev إلا الاصدار 1.0 الذى قمنا بإنشاءه و اضافه فيه بيانات من قبل .

يمكنك ترقيه قاعده البيانات إلى اصدار احدث مع الحفاظ على البيانات بداخها كما هى او تغيرها من خلال الوظيفه changeVersion التى تقبل اربع عبارات ، العباره الاولى هى اصدار قاعده البيانات التى تريد تغيرها ، العباره التانيه هى الاصدار الجديد ، العباره التالته هى الوظيفه التى تقوم بتغير الجداول او تغير ال schema و تقبل كائن من نوع SQLTransaction ، العباره الرابعه هى الوظيفه التى يتم استدعائها اذا حدث خطأ اثناء الترقيه مع تمرير الكائن SQLError لها ، اما العباره الخامسه و الاخيره هى الوظيفه التى يتم استدعائها اذا نجحت عمليه الترقيه ، كما يوضح المثال البسيط التالى :

function upgrade(tx){
    var sql = "ALTER TABLE goals RENAME TO targets";
    tx.executeSql(sql, [], resultHandler, errorHandler);
};
function upgradeError(error){
    alert("upgrade error :" + error.message);
};
function upgradeSuccess(){
    alert("upgrade completed");
}
db.changeVersion("1.0", "2.0", upgrade, upgradeError, upgradeSuccess);

فى المثال السابق تم ترقيه قاعده البيانات إلى الاصدار 2.0 مع تغيير إسم الجدول goals إلى targets مع الاحتفاظ بالبيانات كما هى كما توضح الصوره التاليه :

keepondev database version 2.0 and goals table converted to targets

الدعم من المتصفحات

وقت كتابه التدوينه ال local javascript database api مدعم فى safari 3.1+ و iphone os 2.0+ و المتصفحات الاخرى قريبا ان شاء الله .

للمزيد

ADC > using JavaScript Databases

HTML5 storage الجزء الاول

13/07/2009

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

فى هذا الجزء أتناول الكائن sessionStorage لتخزين البيانات بين الصفحات ، و الكائن localStorage لتخزين البيانات بين الصفحات و حتى بعد اغلاق المتصفح ، و الكائن globalStorage الذى تم الغاءه من مواصفات HTML5 لكنه لازال متواجد فى FireFox2 .

مقدمه
الكائن sessionStorage
الكائن localStorage
الكائن globalStorage
الحدث storage
الدعم من المتصفحات
المزيد

مقدمه

تحتوى HTML5 على الكثير من الكائنات لتخزين البيانات فى المتصفح بدلا من ال cookies التى تقتصر مساحتها التخزينيه على 4Kb و غير امنه و استخراج البيانات منها يتطلب اعراب ال النص المخزن ، و ذلك لتوفير وسيله اكثر امانا و ضخمه لتخزين ازواج البيانات key/value pairs لإستخدامها لاحقا اما بين الصفحات بإستخدام sessionStorage او بين الصفحات و حتى بعد اغلاق المتصفخ و فتحه بإستخدام الكائن localStorage ، او تخزين البيانات فى قواعد بيانات SQLite كما سنرى الجزء فى الثانى ان شاء الله ، او تخزين ملفات التطبيق مثل js,css,html,jpg … الخ حتى يعمل التطبيق اوفلاين عند انقطاع الانترنت و سرعه بدأ التطبيق عندما يرجع اتصال النترنت لأن ملفات التطبيق كلها على جهاز المستخدم يخزنها المتصفح ، ملفات السيرفر فقط مثل php مخزنه على السيرفر كما سنرى فى الاجزاء اللاحقه ان شاء الله .

sessionStorage

الكائن sessionStorage يقوم بتخزين البيانات فى جلسه الصفحه page session التى تبدأ منذ بدايه فتح الصفحه و حتى اغلاق النافذه او ال tab ، و بهذا يمكنك تخزين و الوصول إلى البيانات بين الصفح المختلفه مادام فى نفس ال tab او النافذه و تحت نفس الدومين ، و يتم مسح هذه البيانات فور اغلاق ال tab او النافذه و لا يمكنك الوصول للبيانات عند فتح هذه الصفحه مره اخرى ، يمكن تخزين و الوصول للبيانات من sessionStorage عن طريق الوظيفتين setItem و getItem كما يوضح المثال البسيط التالى :

// set key/values in this page
sessionStorage.setItem("name", "Mostafa Farghaly");
sessionStorage.setItem("age", 22);
// get values in another page
alert(sessionStorage.getItem("name")); // Mostafa Farghaly
alert(sessionStorage.getItem("age")); //22

ملحوظه : قم بتجربه ال sessionStorage من ال localhost ، لأن التجربه من داخل مسار عادى سينتج عنه خطأ .

او مباشره بدون وظائف عن طريق كتابه اسم ال key كما يوضح الكود البسيط التالى :

sessionStorage.name = "Mostafa Farghaly";
alert(sessionStorage.name); // Mostafa Farghaly

يحتوى الكائن sessionStorage على الخاصيه length التى بها عدد القيم المخزنه فى ال sessionStorage للدومين الحالى :

alert(sessionStorage.length); //2

و يحتوى ايضا على الوظيفه key التى تقبل رقم و ترجع اسم ال key المرتبط به :

alert(sessionStorage.key(0)); //name
alert(sessionStorage.key(1)); //age

و يحتوى ايضا على الوظيفه removeItem التى تقبل key و تقوم بمحو ال key/value بأكمله و تقوم بتقليل ال length واحد 1 كما يلى :

sessionStorage.removeItem("name");
alert(sessionStorage.name); //null

و يحتوى على الوظيفه clear التى تقوم بمحو جميع ال key/value .

الكائن sessionStorage و localStorage مشتقين من النوع Storage :

alert(sessionStorage instanceof Storage); //true

localStorage

الكائن localStorage يطابق interface الكائن sessionStorage و عليه فإنه يحتوى على نفسه خصائصه و وظائفه ، المهم هو ان طريقه عمله تختلف ، حيث يمكنه تخزين و الوصول للبيانات فى صوره ازواج key/value من بدايه كتابتها إلى مسحها ، القيم تظل محفوظه عند التنقل بين الصفحات لنفس الدومين حتى بعد اغلاق النافذه او حتى اعاده تشغيل المتصفح او الكمبيوتر لنفس الدومين .

// set key/value in localStorage
localStorage.goal = "world domination";
//close the page, open it
alert(localStorage.goal); //world domination

globalStorage

firefox2 قام بإدراج الكائن globalStorage الذى تم محوه من مواصفات HTML5 بعد اصدار Firefox2 ، الكائن globalStorage له أيضا نفس ال interface الخاص ب sessionStorage و localStorage لكنه يعمل تماما مثل localStorage ، و قد تم محوه من المواصفات و تقديم localStorage ، يقوم ال globalStorage بتخزين القيم على مستوى الدومين كما يلى :

globalStorage['www.keepondev.com'].name = "Mostafa Farghaly";
alert(globalStorage['www.keepondev.com'].name); //Mostafa Farghaly

يمكنك استخدام الوظيفه التاليه للحصول على globalStorage او localStorage فى حاله تواجد احداهما مع اعطاء الاولويه للتانى :

function getStorage(){
    return (localStorage) ? localStorage : globalStorage[location.hostname];
}

var storage = getStorage();
storage.whyProgramme = "for fun";

ال localStorage مقيد بال host+schema+port لذلك http://www.keepondev.com لا يمكنه قراءه بيانات https://www.keepondev.com لتغير ال schema من http إلى https ، اما ال globalStorage فمقيد بال host فقط ، لذلك الاول اكثر امانا .

الحدث storage

يمكن الاستماع الى الحدث storage الذى يتولد عند كتابه او تعديل او محو اى key/value للكائن localStorage او sessionStorage ، يتم تمرير كائن الحدث storage event إلى الوظيفه التى تستمع للحدث ، و يحتوى على الكثير من الخصائص التى تعرفك المزيد عن الحدث مثل اى key تم تغيره او اضافته ، ماهى القيمه الجديده لهذا ال key … الخ كما يوضح المثال البسيط التالى :


document.body.addEventListener("storage", function(storageEvent){
    alert("the modified key's " + storageEvent.key); //name
    alert("the new value's " + storageEvent.newValue); //John Resig
    alert("the old value was " + storageEvent.oldValue); //Mostafa Farghaly
    alert("the page that made the change's " + storageEvent.url);
    // keepondev.com
    alert("the window where change was made's " storageEvent.source);
    // this [window object]
});

sessionStorage.name = "John Resig"; // fires storage event and call the listening function

الصفحات التى تحت نفس الدومين الواحد فقط هى التى تستطيع الاستماع للحدث storage و الوصول لخصائص كائن الحدث storage .

browser support

وقت كتابه هذه التدوينه :
الكائنات localStorage و sessionStorage مدعم فى FireFox3+, IE8, Safari4
الكائن globalStorage موجود فى Firefox2
انترنت اكسبلورر يحتوى على userDate behaviour لهه وظائف مماثله لتخزين البيانات .

للمزيد
cookies
MDC DOM Storage
IE userData
Flash local Storage
Apple developer connection key-value storage


Follow

Get every new post delivered to your Inbox.