נכון, לספרינג יש גם כלים ל 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.
הקמת סביבת העבודה
בואו נתחיל בהגדרת DataSource שיהיה מחובר לבסיס הנתונים.
אפליקציית Standalone
ניתן תאורטית להשתמש בקובץ ה XML של ספרינג כקובץ קונפיגורציה. למשל: להגדרת הפרמטרים של החיבור לבסיס הנתונים.
בפועל – זה לא רעיון כ\”כ מוצלח: לקוח (איש IT) שיידרש לשנות קובץ יכול בקלות להסתבך או/ו להרוס משהו. קונפיגורציה כמו חיבור לבסיס הנתונים, שסביר שנצטרך לקנפג עבור כל התקנה – עדיף לשים בקובץ חיצוני ופשוט, כזה שיהיה זמין בקלות לכלי קונפיגורציה כמו chef או puppet. למשל: קובץ properties (המורכב מ keys ו values מופרדים ע\”י הסימן \”=\”).
הנה דוגמה לכזו תצורה:
- אנחנו מבקשים מ Spring לטעון ל ApplicationContext קובץ Properties חיצוני. כל ערכי הקובץ יתווספו ל context של ספרינג. שימוש ב classpath יכול להכשל אם החיפוש של ספרינג החל ב root classpath שונה, למשל של ספריית ה test – ושימוש ב *classpath יחפש אחר הקובץ בכל ה roots של ה classpath.
- באפליקציה שהיא Standalone הייתי רוצה לקבל יכולות בסיסיות של Connection Pooling, אותן נוכל לקבל מ Apache Commons ע\”י שימוש ב BasicDataSource.
- בעת חיסול האפליקציה נקרא ל ()DataSource.close שגורם לסגירה מסודרת של כל ה connections שנותרו.
- השמת משתני ה \”Connection String\” ל dataSource נעשית ע\”י ספרינג שמשתמש ב Setters הקיימים של מחלקת ה BasicDataSource בכדי לקבוע את הערכים, שבשלב 1 נטענו לסביבה.
JdbcTemplate
היכולות הבסיסיות של Spring-JDBC נגישות דרך שימוש במחלקה JdbcTemplate. מחלקה זו מנהלת את ה Connection, Statement וה ResultSet של JDBC ועוטפת את ניהול השגיאות.
נגדיר bean של JDBC Template ונשתמש ב DataSource שהגדרנו קודם:
הנה דוגמת קוד לשימוש ב JdbcTemplate כחלק מ DAO (אובייקט גישה לנתונים, קיצור של Data Access Object):
- נגדיר SQL Statement של INSERT עם מקום להכנסת ערכים (סימני השאלה).
- KeyHolder (או המימוש הפשוט GeneratedKeyHolder) היא מחלקה של Spring-JDBC המשמשת כמבנה נתונים שמחזיק key של הטבלה (מספר columns, כמו Primary Key). בפעולות Insert – ערך ההחזרה הוא ה key שניתן לרשומה החדשה.
- המתודה update משמשת לכל פעולת SQL של עדכון (יצירה, מחיקה, עדכון). במקרה שלנו – יצירה (INSERT).
- אנו מצהירים מהם ה colums שאנו רוצים לקבל בחזרה מפעולת העדכון. זהו ה primary key שנקבע ע\”י בסיס הנתונים.
- אנו מבצעים השמה מתוך ה Domain Object (במקרה שלנו: Person) לתוך ה prepared Statement, כדי למלא את ערכי ה ?,?,? ב SQL Statement – ואת הטיפוסים שלהם.
שימו לב שאין לנו בקוד טיפול בשגיאות, ואין את סגירת ה connection / statement – שהם error prone, וטיפול לא נכון בהם יכולים לפגוע ביציבות המערכת. כל אלו מטופלים עבורנו ע\”י המחלקה JdbcTemplate.
NamedParameterJdbcTemplate
אני רוצה להציג בקצרה, Template של Spring-JDBC שהוא טיפה יותר \”מפנק\”.
מעצבן אתכם לראות את הקוד של השמה של ערכים ל prepared Statement עם טיפוסים? הנה דרך מעט יותר אלגנטית לעשות זאת.
בכדי לגוון (ולהראות את ה RowMapper) בחרתי בשאילתה שמחזירה נתונים (SELECT) ולא מבצעת עדכון.
- במקרה הזה אין לנו ?,?,? בשאילתה, אלא placeholders המתחילים בסימן \”:\”.
- SqlParameterSource הוא מבנה נתונים של Spring המתאים ל NamedParameterTemplate. הוא משמש כפרמטר לפעולות השונות של ה NamedParameterJdbcTemplate.
- הפעם אנו לא עושים פעולת update כי אם query. ה RowMapper היא מחלקה שתפקידה לתרגם את ה ResultSet ל Domain Object. במקרה זה יצרנו אותה כ Anonymous Class.
- השמה \”סיזיפית\” של ערכים. אני לא מכיר דרך יעילה בהרבה לעשות זאת.