תבנית DAO בג'אווה

1. סקירה כללית

דפוס ה- Data Access Object (DAO) הוא דפוס מבני המאפשר לנו לבודד את שכבת היישום / העסק משכבת ​​ההתמדה (בדרך כלל בסיס נתונים יחסי, אך זה יכול להיות כל מנגנון התמדה אחר) באמצעות ממשק API מופשט..

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

במדריך זה נצלול עמוק ליישום התבנית ונלמד כיצד להשתמש בה לצורך הפשטת שיחות למנהל ישות JPA.

2. יישום פשוט

כדי להבין כיצד פועל דפוס ה- DAO, בואו ליצור דוגמה בסיסית.

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

2.1. מחלקת התחום

מאחר שהיישום שלנו יעבוד עם משתמשים, עלינו להגדיר מחלקה אחת בלבד ליישום מודל התחום שלה:

מחלקה ציבורית משתמש {שם פרטי מחרוזת; דוא"ל מחרוזת פרטי; // בונים / סטרים סטנדרטיים / גטררים}

ה מִשׁתַמֵשׁ class הוא רק מיכל רגיל לנתוני משתמשים, ולכן הוא לא מיישם שום התנהגות אחרת שכדאי להדגיש.

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

ובכן, זה בדיוק הנושא שדפוס ה- DAO מנסה לטפל בו.

2.2. ממשק ה- API של DAO

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

הנה ה- API של DAO:

ממשק ציבורי Dao {אופציונלי לקבל (מזהה ארוך); רשימה getAll (); בטל שמירה (T t); עדכון בטל (T t, מחרוזת [] params); מחיקה בטלה (T t); }

ממעוף הציפור ברור לראות שה- דאו ממשק מגדיר ממשק API מופשט שמבצע פעולות CRUD על אובייקטים מהסוג ט.

בגלל רמת ההפשטה הגבוהה שמציע הממשק, קל ליצור יישום קונקרטי ועדין שעובד איתו מִשׁתַמֵשׁ חפצים.

2.3. ה UserDao מעמד

בואו נגדיר יישום ספציפי למשתמש של ה- דאו מִמְשָׁק:

מחלקה ציבורית UserDao מיישמת את Dao {משתמשים ברשימה פרטית = ArrayList חדש (); UserDao ציבורי () {users.add (משתמש חדש ("ג'ון", "[דוא"ל מוגן]")); users.add (משתמש חדש ("סוזן", "[דוא"ל מוגן]")); } @Override public אופציונלי לקבל (מזהה ארוך) {return Optional.ofNullable (users.get ((int) id)); } @Override רשימה ציבורית getAll () {משתמשים חוזרים; } @ עקוף שמור ריקות ציבורית (משתמש משתמש) {users.add (משתמש); } עדכון הריק הציבורי @ @ עקוב (משתמש משתמש, מחרוזת [] פראמסים) {user.setName (Objects.requireNonNull (params [0], "השם לא יכול להיות ריק")); user.setEmail (Objects.requireNonNull (params [1], "אימייל לא יכול להיות ריק")); users.add (משתמש); } @ עקוב מחיקה של ריק ריק (משתמש משתמש) {משתמשים. הסר (משתמש); }}

ה UserDao class מיישמת את כל הפונקציונליות הנדרשת לאחזור, עדכון והסרה מִשׁתַמֵשׁ חפצים.

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

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

בעוד ששני ה מִשׁתַמֵשׁ ו UserDao כיתות מתקיימות באופן עצמאי באותו יישום, אנחנו עדיין צריכים לראות כיצד האחרון יכול לשמש כדי לשמור על שכבת ההתמדה מוסתרת מהגיון היישום:

מחלקה ציבורית UserApplication {פרטית סטטית Dao userDao; סטטי ציבורי ריק ריק (String [] args) {userDao = UserDao חדש (); משתמש משתמש 1 = getUser (0); System.out.println (user1); userDao.update (user1, מחרוזת חדשה [] {"ג'ייק", "[דוא"ל מוגן]"}); משתמש משתמש 2 = getUser (1); userDao.delete (user2); userDao.save (משתמש חדש ("ג'ולי", "[דוא"ל מוגן]")); userDao.getAll (). forEach (user -> System.out.println (user.getName ())); } משתמש סטטי פרטי getUser (מזהה ארוך) {משתמש אופציונלי = userDao.get (id); להחזיר user.orElseGet (() -> משתמש חדש ("משתמש לא קיים", "ללא דוא"ל")); }}

הדוגמה נרקמת, אך היא מראה בקצרה את המניעים שמאחורי דפוס ה- DAO. במקרה זה, רָאשִׁי השיטה רק משתמשת ב- UserDao למשל לבצע פעולות CRUD בכמה מִשׁתַמֵשׁ חפצים.

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

3. שימוש בתבנית עם JPA

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

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

3.1. ה JpaUserDao מעמד

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

המחלקה הציבורית JpaUserDao מיישמת את Dao {EntityManager entityManager הפרטי; // קונסטרוקטורים סטנדרטיים @ Override public אופציונלי לקבל (מזהה ארוך) {return Optional.ofNullable (entityManager.find (User.class, id)); } @Override רשימה ציבורית getAll () {Query query = entityManager.createQuery ("בחר E מתוך משתמש e"); השב שאילתה.getResultList (); } @Override שמירת ריקות ציבורית (משתמש משתמש) {executeInsideTransaction (entityManager -> entityManager.persist (user)); } עדכון הריק הציבורי @ @ עקוב (משתמש משתמש, מחרוזת [] פראמסים) {user.setName (Objects.requireNonNull (params [0], "השם לא יכול להיות ריק")); user.setEmail (Objects.requireNonNull (params [1], "אימייל לא יכול להיות ריק")); executeInsideTransaction (entityManager -> entityManager.merge (משתמש)); } @ מחק ריק בטלונות ציבורי (משתמש משתמש) {executeInsideTransaction (entityManager -> entityManager.remove (user)); } חלל פרטי executeInsideTransaction (פעולת צרכנים) {EntityTransaction tx = entityManager.getTransaction (); נסה את {tx.begin (); action.accept (entityManager); tx.commit (); } לתפוס (RuntimeException e) {tx.rollback (); לזרוק e; }}}

ה JpaUserDao בכיתה מסוגלת לעבוד עם כל מסד נתונים יחסי הנתמך על ידי יישום ה- JPA.

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

במילים פשוטות, יש לנו ממשק API המותאם ספציפית לתחום, ולא לממשק ה- API של מנהל הישויות כולו.

3.2. משחזר את ה- מִשׁתַמֵשׁ מעמד

במקרה זה נשתמש ב- Hibernate כהטמעת ברירת המחדל של JPA, ובכך נשקף את ה- מִשׁתַמֵשׁ בכיתה בהתאם:

@Entity @Table (name = "משתמשים") משתמש בכיתה ציבורית {@Id @GeneratedValue (אסטרטגיה = GenerationType.AUTO) מזהה פרטי פרטי; שם מחרוזת פרטי; דוא"ל מחרוזת פרטי; // קונסטרוקציות סטנדרטיות / סטרים / גטררים}

3.3. אתחול מנהל ישויות JPA מבחינה תכנותית

בהנחה שכבר יש לנו מופע עובד של MySQL הפועל באופן מקומי או מרחוק וטבלת מסדי נתונים "משתמשים" מאוכלס בכמה רשומות משתמשים, עלינו להשיג מנהל ישות JPA כדי שנוכל להשתמש ב- JpaUserDao מחלקה לביצוע פעולות CRUD במסד הנתונים.

ברוב המקרים, אנו משיגים זאת באמצעות טיפוסי "Persistence.xml" קובץ, שהוא הגישה הסטנדרטית.

במקרה זה, ניקח "ללא XML" לגשת ולקבל את מנהל הישויות עם Java רגיל דרך ה- Hibernate EntityManagerFactoryBuilderImpl מעמד.

לקבלת הסבר מפורט על אופן האתחול של יישום JPA עם Java, אנא עיין במאמר זה.

3.4. ה יישום משתמש מעמד

לבסוף, בואו לשקול מחדש את ההתחלה יישום משתמש בכיתה, כך שהיא תוכל לעבוד עם JpaUserDao למשל ולבצע פעולות CRUD ב - Windows מִשׁתַמֵשׁ ישויות:

מחלקה ציבורית UserApplication {פרטי סטטי Dao jpaUserDao; // בונים סטנדרטיים ציבור סטטי ריק ריק (String [] args) {User user1 = getUser (1); System.out.println (user1); updateUser (user1, מחרוזת חדשה [] {"Jake", "[email protected]"}); saveUser (משתמש חדש ("מוניקה", "[דוא"ל מוגן]")); deleteUser (getUser (2)); getAllUsers (). forEach (user -> System.out.println (user.getName ())); } משתמש סטטי ציבורי getUser (מזהה ארוך) {משתמש אופציונלי = jpaUserDao.get (id); להחזיר user.orElseGet (() -> משתמש חדש ("משתמש לא קיים", "ללא דוא"ל")); } רשימה סטטית ציבורית getAllUsers () {return jpaUserDao.getAll (); } עדכון ריק סטטי ציבורי משתמש (משתמש משתמש, מחרוזת [] פראמס) {jpaUserDao.update (משתמש, פראמס); } ציבורי ריק חסכון משתמש (משתמש משתמש) {jpaUserDao.save (משתמש); } בטל סטטי פומבי deleteUser (משתמש משתמש) {jpaUserDao.delete (משתמש); }}

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

ברוב היישומים יש מסגרת DI, האחראית על הזרקת a JpaUserDao למשל לתוך יישום משתמש מעמד. לשם הפשטות, השמטנו את פרטי התהליך הזה.

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

בנוסף, נוכל להחליף את MySQL לכל RDBMS אחר (ואפילו למסד נתונים שטוח) בהמשך הדרך, ועדיין היישום שלנו ימשיך לעבוד כצפוי, הודות לרמת ההפשטה שמספקת דאו ממשק ומנהל הישות.

4. מסקנה

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

כרגיל, כל דגימות הקוד המוצגות במאמר זה זמינות ב- GitHub.