2014-10-08

Spring-JDBC: שדרוג קטן לעבודה ב JDBC

בפוסט (קצר) זה אני רוצה לשתף בכלי שהתחלנו להשתמש בו לאחרונה - Spring JDBC.

נכון, לספרינג יש גם כלים ל JPA / חיבור ל Hibernate, - אבל לפעמים לא רוצים לעבוד עם ORM - אלא עם JDBC פשוט.

(כדרך אגב, ה Suite של ספרינג כולל רכיב בשם iBatis שהוא הפשטה / אוטומציה מעל JDBC. קצת יותר מ JDBC Templates אותם אנו סוקרים בפוסט זה -  וקצת פחות מ ORM. עקרון הבסיס שלו הוא קשירה של SQL Statements שכתב המפתח לאובייקטי Java וניהול ההפעלה שלהם.)

בכל מקרה אנו רוצים לעבוד עם הבסיס - עם Spring-JDBC וכלי ה JDBC Templates שלו.

JDBC Templates מסייעים בפישוט הגישה לבסיס הנתונים בכך שהם מטפלים בכמה נושאים:

  • פתיחה / סגירה של משאבים מול בסיס הנתונים (Connection, Statement, וכו') - נושא שטיפול לא נכון בו יכול לכלות את ה connection pool ולגרום לקריסה של מערכות.
  • פישוט ניהול Transactions  - על בסיס Spring Transactions
  • עטיפה של Exceptions טכניים של JDBC ב Exceptions יותר ידידותיים. למשל: לקבל CannotGetJdbcConnectionException במקום SQLException עם ErrorCode סתום כלשהו.
 סה"כ Spring-JDBC היא שכבת ריפוד קלה לעבודה כמעט ישירה מעל JDBC.




הקמת סביבת העבודה


צעד ראשון נדרש הוא שיהיה לנו חיבור לבסיס הנתונים (ושיהיה גם בסיס נתונים, כמובן. אני מניח שיש לכם). JDBC Templates עובדים מול DataSource - שהוא הממשק של ג'אווה ל Factory המייצר DB Connections.
בואו נתחיל בהגדרת DataSource שיהיה מחובר לבסיס הנתונים.

הערה: חילקתי שלב זה ל2 וריאציות :אפליקציה שהיא Standalone, ואפליקציה שרצה על Application Server.


אפליקציית Standalone

ניתן תאורטית להשתמש בקובץ ה XML של ספרינג כקובץ קונפיגורציה. למשל: להגדרת הפרמטרים של החיבור לבסיס הנתונים.
בפועל - זה לא רעיון כ"כ מוצלח: לקוח (איש IT) שיידרש לשנות קובץ יכול בקלות להסתבך או/ו להרוס משהו. קונפיגורציה כמו חיבור לבסיס הנתונים, שסביר שנצטרך לקנפג עבור כל התקנה - עדיף לשים בקובץ חיצוני ופשוט, כזה שיהיה זמין בקלות לכלי קונפיגורציה כמו chef או puppet. למשל: קובץ properties (המורכב מ keys ו values מופרדים ע"י הסימן "=").

הנה דוגמה לכזו תצורה:


  1. אנחנו מבקשים מ Spring לטעון ל ApplicationContext קובץ Properties חיצוני. כל ערכי הקובץ יתווספו ל context של ספרינג. שימוש ב classpath יכול להכשל אם החיפוש של ספרינג החל ב root classpath שונה, למשל של ספריית ה test - ושימוש ב *classpath יחפש אחר הקובץ בכל ה roots של ה classpath.
  2. באפליקציה שהיא Standalone הייתי רוצה לקבל יכולות בסיסיות של Connection Pooling, אותן נוכל לקבל מ Apache Commons ע"י שימוש ב BasicDataSource.
  3. בעת חיסול האפליקציה נקרא ל ()DataSource.close שגורם לסגירה מסודרת של כל ה connections שנותרו.
  4. השמת משתני ה "Connection String" ל dataSource נעשית ע"י ספרינג שמשתמש ב Setters הקיימים של מחלקת ה BasicDataSource בכדי לקבוע את הערכים, שבשלב 1 נטענו לסביבה.
ובכדי להשלים את התמונה - הנה קובץ ה properties:



סביבת Application Server

אם אתם לא הקוד הראשון שרץ על ה Application Server - סביר שמישהו כבר רשם DataSource, טיפל ב connection pooling וכו', ורשם אותו ל JNDI. אתם רק צריכים להתייחס אליו. למשל:


פשוט!

מרגע זה נועל להתחיל לעבוד עם Spring-JDBC.



JdbcTemplate


היכולות הבסיסיות של Spring-JDBC נגישות דרך שימוש במחלקה JdbcTemplate. מחלקה זו מנהלת את ה Connection, Statement וה ResultSet של JDBC ועוטפת את ניהול השגיאות.

נגדיר bean של JDBC Template ונשתמש ב DataSource שהגדרנו קודם:



הנה דוגמת קוד לשימוש ב JdbcTemplate כחלק מ DAO (אובייקט גישה לנתונים, קיצור של Data Access Object):



  1. נגדיר SQL Statement של INSERT עם מקום להכנסת ערכים (סימני השאלה).
  2. KeyHolder (או המימוש הפשוט GeneratedKeyHolder) היא מחלקה של Spring-JDBC המשמשת כמבנה נתונים שמחזיק key של הטבלה (מספר columns, כמו Primary Key). בפעולות Insert - ערך ההחזרה הוא ה key שניתן לרשומה החדשה.
  3. המתודה update משמשת לכל פעולת SQL של עדכון (יצירה, מחיקה, עדכון). במקרה שלנו - יצירה (INSERT).
  4. אנו מצהירים מהם ה colums שאנו רוצים לקבל בחזרה מפעולת העדכון. זהו ה primary key שנקבע ע"י בסיס הנתונים.
  5. אנו מבצעים השמה מתוך ה Domain Object (במקרה שלנו: Person) לתוך ה prepared Statement, כדי למלא את ערכי ה ?,?,? ב SQL Statement - ואת הטיפוסים שלהם.

שימו לב שאין לנו בקוד טיפול בשגיאות, ואין את סגירת ה connection / statement - שהם error prone, וטיפול לא נכון בהם יכולים לפגוע ביציבות המערכת. כל אלו מטופלים עבורנו ע"י המחלקה JdbcTemplate.



NamedParameterJdbcTemplate


אני רוצה להציג בקצרה, Template של Spring-JDBC שהוא טיפה יותר "מפנק".
מעצבן אתכם לראות את הקוד של השמה של ערכים ל prepared Statement עם טיפוסים? הנה דרך מעט יותר אלגנטית לעשות זאת.

בכדי לגוון (ולהראות את ה RowMapper) בחרתי בשאילתה שמחזירה נתונים (SELECT) ולא מבצעת עדכון.



  1. במקרה הזה אין לנו ?,?,? בשאילתה, אלא placeholders המתחילים בסימן ":".
  2. SqlParameterSource הוא מבנה נתונים של Spring המתאים ל NamedParameterTemplate. הוא משמש כפרמטר לפעולות השונות של ה NamedParameterJdbcTemplate.
  3. הפעם אנו לא עושים פעולת update כי אם query. ה RowMapper היא מחלקה שתפקידה לתרגם את ה ResultSet ל Domain Object. במקרה זה יצרנו אותה כ Anonymous Class.
  4. השמה "סיזיפית" של ערכים. אני לא מכיר דרך יעילה בהרבה לעשות זאת.


שיהיה בהצלחה!




4 תגובות:

  1. אנונימי12/10/14 13:08

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

    השבמחק
    תשובות
    1. היי אנונימי,

      מודה באשמה: זה היה פוסט קצר ונקודתי מהרגיל - ודוגמאות הקוד התבססו על קוד שיש לנו בעבודה, ושם רוב הקונפיגורציות הן ב XML ("גישה ישנה") ולא ב Java Annotations ("גישה חדשה יותר").

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

      עדיין נראה לי שהפוסט הקצר הזה שימושי ומלמד - ויכול לחסוך כמה שעות של התברברויות, למי שכרגע עוסק במלאכה / שוקל לעבוד ב Spring-JDBC.

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

      תודה,

      ליאור

      מחק
    2. אנונימי14/10/14 11:50

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

      מחק
    3. תודה רבה, ותודה על הטיפ - אבדוק. :)

      ליאור

      מחק