עבודה עם מסדי נתונים בצורה פחות כואבת חלק ראשון

אזהרה: מדריך זה מאוד ארוך (5 דפים מלאים עם תמונות וקוד).
טבלת authors

כמעט כל מתכנת מגיע לשלב שהוא (או היא) צריכים לטפל במסדי נתונים. כיום יש 3-4 גישות עבודה מקובלות עם מסדי נתונים:

  1. עבודה ישירה מול מסד נתונים ספציפי לפי API של היצרן
  2. עבודה עם ספרייה שמאגדת פונקציות דומות עבור מבחר מסדי נתונים
  3. מימוש עצמי של מסד נתונים (יש חברות בתעשייה שעושות טעויות כאלו)
  4. עבודה עם כלי ORM אשר מנסות להשכיח את העבודה שיש מסד נתונים ומנסה לספק יכולת לעבוד כמה שיותר בתחביר של השפה בה אנחנו עובדים.

האפשרות השלישית שציינתי קיימת בעיקר בחברות בינוניות וגדולות, אבל היא יחסית זניחה.

דלפי וכיום לזרוס כבר בשנות ה 90 הבינו כי נדרש שינוי משמעותי בעבודה עם מסדי נתונים, והם מספקים משהו שהוא חצי אפשרות השנייה וחצי האפשרות הרביעית ברשימה (משהו ביניים, שהוא בין שפה טבעית לבין API אחיד לכל מסדי הנתונים).

הבסיס הזה נקרא TDataset. הבסיס מאפשר לנו לקבל דבק אחיד של פעולות מבוססות מסדי נתונים, כאשר כל מה שאנחנו צריכים להחליף זו האפשרות של מה מנוע מסד הנתונים אליו אנחנו רוצים להתחבר. ופוסט זה עוסק בנושא הזה בצורה הפשוטה ביותר ללא נגיעה בכמעט כל האפשרויות ש Dataset מאפשר לנו לבצע.

בגדול מאוד בלי להיכנס יותר מידי לנושא, TDataset מאפשר לנו לדעת על המצב הנוכחי של טבלה/שאילתא (איזו פעולה היא ביצעה/ביקשה לבצע), אפשרות ליצור טבלה, אינדקסים ויחסי גומלין בין טבלאות, הכל באמצעות תחביר פסקל ולא דרך שימוש בשאילתות שאנחנו כותבים. אפשר לשלוט ברמה גבוהה יותר במה כל שדה עושה, כולל יחסי גומלין בין טבלאות גם כאשר מנוע מסד הנתונים לא תומך בזה (כדוגמת SQLite). אבל כאמור זה ממש בגדול בלי להיכנס כיצד עושים את זה.

אז איך נתחיל בעבודה שלנו בלזרוס עם שימוש במסד נתונים עם כמה שפחות כאב ? ובכן נתחיל בהתקנת החבילה (במידה ולא עשיתם את זה) בשם SQLDBLaz.

לשונית data accessלאחר שלזרוס חזר לנו מההתקנה, ניצור פרוייקט חדש בשם my_blog (הרי כל המדריכים כיום למסדי נתונים מלמדים להקים blog, אז למה לא אני ? אל תדאגו לא ניצור במדריך זה אתר). ונוסיף לו מלשונית הרכיבים שלנו את ‎TDatasource הממוקמת בלשונית Data Access. הפקד מופיע עם צלמית של מסד נתונים עם הרבה חצים (הפקד השמאלי ביותר בתמונה).

במידה ויש אצלכם פחות רכיבים בלשונית, זה בסדר. אצלי מותקנים רכיבים נוספים שלא מותקנים בברירת מחדל בלזרוס ובחלקם ניגע בפוסטים הבאים.

לפני שנמשיך חשוב לי להסביר מה בעצם הולך להיות מהנקודה הזו.ישנם הרבה צורות חיבור למסדי נתונים, והרבה מימושים. רובם מתבססים על ה API של TDataset (שהוא רחב יותר מאשר מחלקה אחת), אבל מציעים צורות עבודה שונות. הפקדים של SQLDBLaz משתמשים ב API שמגיע עם FPC עבור מסדי נתונים למשל, בעוד ש FIBComponents יוצר API משל עצמו עבור Firebird אבל עדיין משתמש ב API של TDataset. בנוסף ל SQLDBLaz יש תמיכה בעוד מסדי נתונים שונים בצורות אחרות בלזרוס שאפשר להתקין, כך שאנחנו לא באמת מחוייבים להשתמש רק בגישה אחת בלבד. כמו כן, ישנה ספרייה בשם ZeosDB (אשר לא נתמכת כראוי לדעתי האישית ב64 ביט), שהיא ספרייה צד שלישי לObject Pascal שעובדת עם כמה מהדרים, והרבה משתמשים בה, וגם היא תומכת ב TDataset.

עכשיו ניגשטרנזאקציות של מסד הנתונים ללשונית של SQLDb לשונית sqldbונבחר את החיבור למסד הנתונים שלנו. אני אישית עובד עם firebird. לאחר שבחנו את מסד הנתונים שלנו, נבחר את פקד הטרנזאקציות, ונחבר אותו אל מסד הנתונים שלנו (נוכל לבחור את זה בצורה גרפית ב Object Inspector).

העיקרון של SQLDB הוא שאנחנו תמיד נעבוד עם טרנזאקציות, ובמקרה של Firebird, אנחנו בכל מקרה מחוייבים מכורח צורת העבודה של מסד הנתונים. יותר מאוחר בפוסט נראה איך נשתמש ברכיב זה.רכיב השאילתא

הגדרות נוספות שנעשה במסד הנתונים הם הגדרת מסד הנתונים (במידה ומדובר ב firebird נגדיר או את מיקום מסד הנתונים או את ה alias שלו), נגדיר את כתובת שרת מסד הנתונים (אלא אם מדובר ב SQLite3), משתמש וסיסמה להתחבר ויש לנו חיבור מלא של מסד הנתונים שלנו.

עכשיו נוסיף שאילתא לרשימת הרכיבים שלנו. נחבר את השילתא למסד הנתונים ונראה שהוא גם לקח את ההגדרות של הטרנזאקציות שלנו.

ניצור למסד הנתונים שלנו טבלה (מחוץ ללזרוס כרגע) בשם authors:

CREATE TABLE AUTHORS
(
ID Integer NOT NULL,
NAME Varchar(255),
CAN_AUTHOR "BOOLEAN" DEFAULT 1,
ADDED Timestamp DEFAULT CURRENT_TIMESTAMP  NOT NULL,
CHANGED Timestamp DEFAULT CURRENT_TIMESTAMP  NOT NULL,
PRIMARY KEY (ID)
);

כמובן שתשנו את זה בהתאם למסד הנתונים שלכם (אני מספק בסוף הפוסט קובץ להורדה עם הקוד כולל ה SQL המלא).

לאחר היצירה, נכתוב ברכיב השאילתא שלנו ב"תכונה" של SQL (בלבד) את הקוד הבא:שאילתת select

SELECT ID, NAME, CAN_AUTHOR, ADDED, CHANGED FROM AUTHORS

ואז נלך לרכיב של Datasource ונספק לו את רכיב השאילתא שלנו ונשמור את השינויים.

לפני שאמשיך יש כמה דברים שחשוב לי אפשר לפתוח חיבור ישיר למסד הנתונים שלנו בזמן תכנון ! מאוד מומלץ לא לעשות את זה למעט הצורך שלנו בזמןעריכת שדות התכון לדברים ספציפיים. במקום זה נכתוב 2 שורות קוד שיפתחו ויסגרו בשבילנו את החיבור בזמן ריצה במקום בזמן תכנון.

הוספת שדותכרגע נפעיל את החיבור של מסד הנתונים על ידי העברת ערך של Connected ל true (לחיצה כפולה עושה את העבודה). וניגש לרכיב השאילתא שלנו ונפעיל גם אותה עם התכונה Active בערך של true.

לחיצה כפולה על פקד השאילתא שלנו (או לחיצה על מקש ימני והפעלת הערך הראשון בתפריט: Edit Fields) בשביל לערוך שדות.

בברירת מחדל נקבל חלון לבן (תלוי בצבעי המערכת שלכם) וריק.  נלחץ על מקש ימני ונבחר את האפשרות Add Fields, ואז נסמן ונבחר את כולם (לחיצה על ID ואז לחיצה על מקש ה Shift ולחיצה על שדה הChanged) ועל OK. ועכשיו חלון עריכת השדות שלנו מלא בשדות.

הפעולה הבאה שנעשה היא ללכת ל ID ולסמן את התכונה של Readonly ל true ונשים False לתכונה Required. אנחנו עושים זאת בשביל להזין את הערך שלו בצורה "אוטומטית" במסד הנתונים (במקרה של Firebird מדובר בTrigger). במידה ולא נבצע את הצעדים האלו, כאשר נרצה לשמור רשומה חדשה, נקבל חריגה (Exception) שלא ציינו ערך לשדה שהוא חובה (מאוחר יותר נראה למה).

אותו הדבר נעשה גם לשדות Added ו Changed היות וגם אותם אני משנה עם טריגרים במקום ידנית ולא נזין את הערכים ידנית אצלנו.

במידה ולא הינו רוצים לקחת את ה ID (אנחנו זקוקים לו, אבל לא אסביר בחלק זה למה), לא היינו צריכים לעשות את צעד הוספת השדות, אבל מומלץ בחום כן לעשות זאת. את השדות של Added ו Changed הוספנו בשביל לימוד נטו ולא מעבר לפחות כרגע.

שימו לב שהוספת השדות קובעת איך לזרוס ורכיבים שונים בלזרוס יתנהגו לכל שדה, גם אם ההתנהגות שונה לגמרי מההתנהגות המקורית שמוגדרת במסד הנתונים עצמו. במדריך הבא נראה את זה מגיע לידי ביטוי. אנחנו גם יכולים להסתיר ככה שדות בצורה וויזואלית במידה ואנחנו משתמשים ברכיב כדוגמת Grid (רשת), במקום להציג את השדה, אך לא אכנס לזה במדריך זה.

טאב data controlsועכשיו מתחילה העבודה עם פקדים גרפיים. ניגש ללשונית של Data Control. חשוב לדעת לפני שנמשיך שהפקדים הגרפיים של Data Control כמעט לגמרי זהים לפקדים "הרגילים" שיש לנו (למעט 2 פקדים ספציפיים שנגע בהם במדריך הבא), עם שינוי אחד: הם יודעים לעבוד עם ‎TDataset  ולעקוב אחריו וגם לשתף פעולה איתו בצורה שהיא אוטומטית עבורינו, ובכך הם חוסכים לנו עבודה. אך במידה והם לא עונים על הגמישות שאנחנו צריכים, תמיד אפשר לעבוד עם פקדים רגילים, ולעבוד "קשה יותר" לעשות את הקישור בצורה תכנותית, או ליצור פקדים משל עצמנו.

בטאב, נזרוק את הרכיב TDBNavigator . תפקיד הרכיב הוא לתת לנו פעולות "מוכרות" אשר חוזרות על עצמן בDataset שלנו. כרגע אנחנו נוסיף לו את ה Datasource שלנו ונגיד לתכונה של align להיות alTop. כך הפקד ייושר כלפי מעלה, וכל שינוי בגודל החלון שלנו ישנה גם את הפקד הזה. בנוסף ב Visible Buttons נעיף את התמיכה בDelete (אני לא אוהב למחוק פיזית ממסד נתונים, אלא רק להסתיר אותם בברירת מחדל ולסמן אותם פנימית כ"מחוקים"). ואלו כרגע כל הפעולות שנעשה עם הפקד הזה.

נלך לטאב הראשון (Standard) ונוסיף עכשיו 2 פקדי TLabel  ונחליט שהם נמצאים ב Left 8. נוכל לעשות את זה לשניהם בייחד אם נבחר אחד, ואז נלחץ על shift ונבחר את השני,  ואז נלך לתכונה של left ונכתוב שם 8. אפשר לבחור גם עם העכבר עם מקש שמאלי לחוץ וגרירה של הסמן. ודרכים נוספות, שאתן לכם לגלות לבד, ולהחליט מה נוח לכם.

עכשיו נלך לתווית ההראשונה, ונכתוב לה "Name" בתכונה של Caption. ונעבר לתווית הבאה, ונכתוב שם "Can Author" בתכונה של Caption. וסיימנו לעבוד עם הפקדים האלו, למרות שאפשר לספק להם עוד יכולת שלא אכנס אליה במדריך זה, והיא לתת פוקוס לרכיב שיכול לקבל פוקוס על ידי תכונה בשם FocusControl אבל לא אכנס לזה במדריך זה.

עכשיו נחזור לטאב של Data Control ונבחר את הפקד TDBEdit ונשים אותו ליד התווית הראשונה. וTDBCheckBox ליש התווית השניה. עכשיו נמחק את ה Caption של TDBCheckBox (אני מחפש מראה אחיד שלא נוכל לקבל אם נשתמש בCaption של הפקד).

לאחר מכן נבחר גם את הרכיבים של הDBEdit וגם את DBCheckbox בייחד, ונשים להם בתכונה של Datasource את ה Datasource שלנו. עכשיו נבחר כל אחד בנפרד, ונתן להם את השדה המתאים (כלומר ל DBEdit ניתן את השדה Name ול DBCheckbox ניתן את Can_Author).

בDBCheckBox יש גם את 2 התכונות: ValueChecked ו ValueUnChecked. נשים בראשון את הערך 1 ובשני את הערך 0. במידה ונרצה להיות צדיקים נלך גם לSQLQuery1CAN_AUTHOR (השדה שיצרנו בצורה וויזואלית) ונספק לו MinValue של 0 ו MaxValue של 1, אבל לא חייבים.

עוד 2 צעדים ונוכל להריץ את התוכנה שלנו !!!

הצעד הבא יהיה להוסיף מטאב הAdditional את כפתור ה TBitBtn. יש לכפתור הזה 3 תכונות שאנחנו נרצה אותם:

  1. יכולת להציג תמונות
  2. לקבל פוקוס (בניגוד לאחיו שנקרא TSpeedButton)
  3. הוא מסוגל לבצע פעולות בלי שנכתוב לו קוד.

לאחר ששמנו אותו על החלון, נלך לתכונה Kind ונגיד לו bkClose. לאחר מכן נלך לתכונה ModalResult ונגיד לו להיות mrOK. נראה שיש לנו תמונה של Close וטקסט בהתאם. זה מה ש bkClose סיפק לנו. הModalResult מאפשר לנו מאוחר יותר ליצור מהחלון שלנו דיאלוג שתופס את הפוקוס מעל שאר החלונות וכשהוא נסגר, הוא מחזיר ערך של mrOK. עוד תכונה ש Kind עשה בשבילו היא, שבמידה ונלחץ על הכפתור בזמן ריצה הוא יעשה עבורינו את הפעולה Close בלי שנכתוב קוד בשביל זה.

עכשיו נריץ את התוכנה שלנו וננסה להזין נתונים (אם סגרתם את החיבור של מסד הנתונים ו/או השאילתא, אנא פתחו אותם לפני הריצה, או שהפקדים לא יהיו פעילים בזמן הריצה)🙂

במידה ואין בעיות כאירועים בobject inspectorי עקבנו אחרי ההוראות, נלחץ על כפתור ה Close ונראה שהוא עובד. עכשיו נריץ שוב את התוכנית שלנו ונגלה דבר מעצבן, המידע שעבדנו עליו מקודם לא נשמר ! כמו שאמרתי מקודם, אנחנו עובדים עם טרנזאקציות, ואנחנו צריכים להגיד לחיבור שלנו לשלוח את הפעולות שלנו (זה יכול להתבצע אפילו לפני סגירת החחלון, כלומר לשלוח את השינויים שעשינו בפעם אחת), ולכן לא נשלחו השינויים פיזית למסד הנתונים בלי שנגיד להם במפורש לעשות זאת. עכשיו נתחיל לכתוב קצת קוד. נלך לפקד השאילתא שלנו ונבחר בטאב האירועים ונבחר את האירוע AfterPost ונלחץ עליו 2 לחיצות מהירות (הדרך המהירה לעשות את זה). ושם נכתוב את הקוד הבא (מסומן בקו תחתי):

procedure TForm1.SQLQuery1AfterPost (DataSet : TDataSet );
begin
SQLQuery1.ApplyUpdates;
SQLTransaction1.CommitRetaining;

end;

וכן נלך ל Object Inspector חזרה לתכונות ונגיד לו UpdateMode לשים את הערך upWhereChanged.

כל מה שעכשיו נשאר לנו לעשות זה לסגור את החיבורים במצב תכנון, ולפתוח ולסגור אותם בזמן ריצה. את זה נעשה עם האירועים של החלון עצמו. נלחץ על החלון עצמו ונלך ב Object Inspector אל Events . ונבחר את האירוע של OnClose.

שם נסגור את החיבורים שלנו בצורה הבאה (הקוד עם הקו התחתי):

procedure TForm1.FormClose (Sender : TObject ; var CloseAction : TCloseAction );
begin
if Datasource1.State in [dsEdit, dsInsert] then
begin
SQLQuery1.ApplyUpdates;
SQLTransaction1.CommitRetaining;
end;
SQLQuery1.Close;
IBConnection1.Close;

end;

שורת ה if בודקת האם כרגע השאילתא שלנו היא במצב של הוספת ערכים או עריכתם. במקרה שכן אנחנו נשמור את השינויים (מומלץ לפני כן לשים חלון שישאל, אבל לא נעשה את זה בחלק זה). לאחר מכן, אנחנו סוגרים קודם את החיבור של השאילתא ורק אז את החיבור של מסד הנתונים.

לאחר מכן, נלחץ 2 לחיצות על החלון שלנו ונקבל אירוע חדש (ברירת מחדל) של יצירת החלון ושם ניצור את הפתיחה שלנו:

procedure TForm1 .FormCreate (Sender : TObject );
begin
IBConnection1.Open;
SQLQuery1.Open;

end;

כאן אנחנו פותחים קודם כל את החיבור של מסד הנתונים ואז את החיבור של השאילתא שלנו.

וזהו יש לנו תוכנה מוכנה שמציגה, מוסיפה ומעדכנת רשומות עבור טבלה מסויימת.

אם תשרישו את הפעולות האלו בעבודה שלכם, תראו שהם ממש פשוטות ומהירות לביצוע ולוקח לכם פחות מ5 דקות לעשות את כל מה שהמדריך הזה מציג.

להורדת הקוד המלא של החלק הזה, לחצו כאן.

10 מחשבות על “עבודה עם מסדי נתונים בצורה פחות כואבת חלק ראשון

  1. עודד

    כל הכבוד על המדריך המפורט. וכל הכבוד שכולו גם מופיע בקורא רסס שלי כך שאני יכול לקרוא אותו בלי להפסיק את רצף העבודה שלי🙂

    רק הערה אחת – התמונות ממש קטנות וקשה לראות מה קורה בהן. לפחות כשזה סביר אז כדאי לתת אותן בגודל מלא, וגם בשאר המקרים כדאי שהן יהיו יותר גדולות.

    נכון שאפשר ללחוץ עליהן, אבל זה מוציא אותך מקצב הקריאה. לפחות אם הן היו נפתחות ב-lightbox או משהו כזה שאפשר לרחף עם הסמן על התמונה כדי לראות אותה מוגדלת, זה היה יכול להיום מאוד שימושי.

  2. ik_5 מאת

    לקחתי לתשומת לבי את נושא התמונות. בחלק הבא (שאתחיל לכתוב אותו רק ביום שישי) תראה את השינוי.
    הרעיון של התמונות היה יותר לכוון אנשים שנתקים בלי להבין את הכוונה של משהו מסויים, מאשר למשוך אותך לכיוון של חייב להתסכל עליהם.

    את זה שאתה רואה ב RSS את המדריך המלא, אתה צריך להודות לבקשה של עירא🙂

    בכל מקרה, אני מקווה שזה עוזר לך ולאחרים.

  3. פינגבק: Firebird News » Painless Databases part 1 (example using firebird with #lazarus #delphi)

  4. De-Panther

    פששששש כל הכבוד על המדריך.
    זה עובד בצורה דומה למה שבדלפי.
    הם כבר סידרו את העניינים שהיו עם הדטה מודול?
    (הרעיון הוא סוג של FORM בלתי נראה, שמיועד לעבודה רק עם דטה בייס, במקום להקים בכל FORM את כל הקישור לדטה בייס מחדש, להשתמש בדטה מודול שמרכז את כל הפעולות שאתה צריך, כמו שאתה עושה USES לספריות, רק שזה נראה גם בצורה ויזואלית ומיועד רק לעבודה עם דטה בייס)

  5. ik_5 מאת

    אתה הולך להרוס לי את החלק השני של המדריך ?
    כן זה עובד עם data module. ובזה אפסיק להגיב, חכה לחלק הבא (שעוד לא מצאתי את הזמן להתחיל אותו)

  6. hola

    hermano se que no es hebreo pero gracias a google traslation puedo leer en español tu articulo, gracias por tu aporte la verdad aqui en venezuela no hay nada de programacion y bueno mis profesores solo dicen que todo ya esta hecho por los gringos, y bueno es un alisiente el ver tu articulo….
    gracias por todo.

  7. פינגבק: מפתחות זרים ללא מפתחות « לראות שונה

  8. פינגבק: Brook Framework | לראות שונה

כתיבת תגובה

הזינו את פרטיכם בטופס, או לחצו על אחד מהאייקונים כדי להשתמש בחשבון קיים:

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s