מבוא ל- OData עם Olingo

1. הקדמה

מדריך זה הוא המשך למדריך פרוטוקול ה- OData שלנו, שם בחנו את היסודות של פרוטוקול OData.

כעת נראה כיצד ליישם שירות OData פשוט באמצעות ספריית Apache Olingo.

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

2. מה זה אולינגו?

Olingo הוא אחד מיישומי ה- OData ה"מוצגים "הזמינים עבור סביבת Java - השנייה היא מסגרת ה- SDL OData. הוא מתוחזק על ידי קרן אפאצ'י ומורכב משלושה מודולים עיקריים:

  • Java V2 - ספריות לקוחות ושרתים התומכות ב- OData V2
  • Java V4 - ספריות שרתים התומכות ב- OData V4
  • Javascript V4 - Javascript, ספריית לקוח בלבד התומכת ב- OData V4

במאמר זה נסקור רק את ספריות ה- V2 Java בצד השרת, התומכות בשילוב ישיר עם JPA. השירות המתקבל תומך בפעולות CRUD ובתכונות אחרות של פרוטוקולי OData, כולל הזמנה, החלפה וסינון.

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

באשר לספריית לקוח JavaScript, אנו משאירים אותה לעת עתה מכיוון שמכיוון ש- OData הוא פרוטוקול מבוסס HTTP, אנו יכולים להשתמש בכל ספריית REST כדי לגשת אליו.

3. שירות Olingo Java V2

בואו ניצור שירות OData פשוט עם השניים EntitySetהשתמשנו בהקדמה הקצרה שלנו לפרוטוקול עצמו. בבסיסה, Olingo V2 הוא פשוט מערך של משאבי JAX-RS וככזה, עלינו לספק את התשתית הנדרשת על מנת להשתמש בו. כלומר, אנו זקוקים להטמעה של JAX-RS ולמיכל סרוולט תואם.

לדוגמא זו, בחרנו להשתמש באביב אתחול - מכיוון שהוא מספק דרך מהירה ליצור סביבה מתאימה לאירוח השירות שלנו. נשתמש גם במתאם ה- JPA של Olingo, אשר "מדבר" ישירות למשתמש המסופק על ידי המשתמש EntityManager על מנת לאסוף את כל הנתונים הדרושים ליצירת ה- OData EntityDataModel.

אמנם לא דרישה מחמירה, כולל מתאם JPA מפשט מאוד את משימת יצירת השירות שלנו.

מלבד תלות סטנדרטית של Spring Boot, עלינו להוסיף כמה צנצנות של Olingo:

 org.apache.olingo olingo-odata2-core 2.0.11 javax.ws.rs javax.ws.rs-api org.apache.olingo olingo-odata2-jpa-processor-core 2.0.11 org.apache.olingo olingo-odata2 -jpa-processor-ref 2.0.11 org.eclipse.persistence eclipselink 

הגרסה האחרונה של ספריות אלה זמינה במאגר המרכזי של Maven:

  • olingo-odata2-core
  • olingo-odata2-jpa-core-core
  • olingo-odata2-jpa-processor-ref

אנו זקוקים לאי הכללות הללו ברשימה זו מכיוון שלאולינגו יש תלות ב- EclipseLink כספקית ה- JPA שלה ומשתמשת גם בגרסת JAX-RS שונה מזו של Boot Boot.

3.1. שיעורי תחום

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

@Entity @Table (name = "car_maker") CarMaker בכיתה ציבורית {@Id @GeneratedValue (אסטרטגיה = GenerationType.IDENTITY) פרטי מזהה ארוך; @NotNull שם פרטי מחרוזת; @OneToMany (mappedBy = "maker", orphanRemoval = true, cascade = CascadeType.ALL) דגמי רשימה פרטית; // ... הושמטו הגטרים, הקובעים וה- hashcode} @Entity @Table (name = "car_model") CarModel {@Id @GeneratedValue (אסטרטגיה = GenerationType.AUTO) פרטי ציבור ארוך מזהה; @NotNull שם פרטי מחרוזת; שנת שלם פרטית @NotNull; @NotNull מק"ט מחרוזת פרטי; @ManyToOne (אופציונלי = false, fetch = FetchType.LAZY) @JoinColumn (name = "maker_fk") יצרנית CarMaker פרטית; // ... הושמטו הגטרים, הקובעים וההאש קוד}

3.2. ODataJPAServiceFactory יישום

מרכיב המפתח שעלינו לספק לאולינגו על מנת להגיש נתונים מתחום JPA הוא יישום קונקרטי של מחלקה מופשטת הנקראת ODataJPAServiceFactory. שיעור זה אמור להאריך ODataServiceFactory ועובד כמתאם בין JPA ל- OData. נקרא למפעל הזה CarsODataJPAServiceFactory, לאחר הנושא העיקרי עבור התחום שלנו:

@Component public class CarsODataJPAServiceFactory מרחיב את ODataJPAServiceFactory {// שיטות אחרות שהושמטו ... @Override public ODataJPAContext initializeODataJPAContext () זורק ODataJPARuntimeException {ODataJPAContext ctx = getODataJPAContext (); ODataContext octx = ctx.getODataContext (); HttpServletRequest בקשה = (HttpServletRequest) octx.getParameter (ODataContext.HTTP_SERVLET_REQUEST_OBJECT); EntityManager em = (EntityManager) בקשה .getAttribute (EntityManagerFilter.EM_REQUEST_ATTRIBUTE); ctx.setEntityManager (em); ctx.setPersistenceUnitName ("ברירת מחדל"); ctx.setContainerManaged (נכון); החזר ctx; }} 

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

תהליך זה מפותל במקצת, אז בואו נצייר רצף UML כדי לדמיין כיצד כל זה קורה:

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

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

3.3. רישום משאבים בג'רזי

השלב הבא הוא רישום שלנו ServiceFactory עם זמן הריצה של אולינגו ורשמו את נקודת הכניסה של אולינגו עם זמן הריצה של JAX-RS. נעשה זאת בתוך א ResourceConfig מחלקה נגזרת, שבה אנו מגדירים גם את נתיב ה- OData עבור השירות שלנו / odata:

@Component @ApplicationPath ("/ odata") מחלקה ציבורית JerseyConfig מרחיב את ResourceConfig {public JerseyConfig (CarsODataJPAServiceFactory serviceFactory, EntityManagerFactory emf) {ODataApplication app = new ODataApplication (); app .getClasses () .forEach (c -> {if (! ODataRootLocator.class.isAssignableFrom (c)) {register (c);}}); הרשמה (CarsRootLocator חדש (serviceFactory)); הרשמה (EntityManagerFilter חדש (emf)); } // ... שיטות אחרות הושמטו}

אולינגו סיפק ODataApplication הוא JAX-RS רגיל יישום מחלקה שרושמת כמה ספקים באמצעות החזרה הסטנדרטית getClasses ().

אנחנו יכולים להשתמש בכולם חוץ מ ODataRootLocator כיתה כמו שהיא. הספציפי הזה אחראי ליישר את שלנו ODataJPAServiceFactory יישום באמצעות Java newInstance () שיטה. אך מכיוון שאנו רוצים שאביב ינהל את זה עבורנו, עלינו להחליף אותו באיתור מותאם אישית.

איתור זה הוא משאב JAX-RS פשוט מאוד המרחיב את מלאי אולינגו ODataRootLocator וזה מחזיר את אביבנו ServiceFactory במקרה הצורך:

@Path ("/") מעמד ציבורי CarsRootLocator מרחיב את ODataRootLocator {private CarsODataJPAServiceFactory serviceFactory; ציבורי CarsRootLocator (CarsODataJPAServiceFactory serviceFactory) {this.serviceFactory = serviceFactory; } @Override ציבורי ODataServiceFactory getServiceFactory () {להחזיר this.serviceFactory; }} 

3.4. EntityManager לְסַנֵן

החלק האחרון שנותר לשירות OData שלנו EntityManagerFilter. מסנן זה מזריק EntityManager בבקשה הנוכחית, כך שהיא זמינה ל ServiceFactory. זה JAX-RS פשוט @ ספק כיתה שמיישמת את שניהם ContainerRequestFilter ו ContainerResponseFilter ממשקים, כך שהוא יכול לטפל כראוי בעסקאות:

@Provider מחלקה סטטית ציבורית EntityManagerFilter מיישמת ContainerRequestFilter, ContainerResponseFilter {סופי סטטי ציבורי מחרוזת EM_REQUEST_ATTRIBUTE = EntityManagerFilter.class.getName () + "_ENTITY_MANAGER"; גמר פרטי EntityManagerFactory; @Context פרטי HttpServletRequest httpRequest; EntityManagerFilter (emf EntityManagerFactory) ציבורי {this.emf = emf; } @ מסנן ריקונים ציבורי @ ControllerRequestContext ctx) זורק IOException {EntityManager em = this.emf.createEntityManager (); httpRequest.setAttribute (EM_REQUEST_ATTRIBUTE, em); אם (! "GET" .equalsIgnoreCase (ctx.getMethod ())) {em.getTransaction (). start (); }} פילטר הריק הציבורי @ @ Override (ContainerRequestContext requestContext, ContainerResponseContext responseContext) זורק IOException {EntityManager em = (EntityManager) httpRequest.getAttribute (EM_REQUEST_ATTRIBUTE); אם (! "GET" .equalsIgnoreCase (requestContext.getMethod ())) {EntityTransaction t = em.getTransaction (); אם (t.isActive () &&! t.getRollbackOnly ()) {t.commit (); }} em.close (); }} 

הראשון לְסַנֵן() השיטה, הנקראת בתחילת בקשת משאבים, משתמשת בתנאי EntityManagerFactory ליצור חדש EntityManager מופע, שמועבר לאחר מכן לתכונה כך שניתן יהיה לשחזר אותה אחר כך על ידי ServiceFactory. אנו גם מדלגים על בקשות GET מכיוון שלא אמורות להיות תופעות לוואי ולכן לא נצטרך עסקה.

השני לְסַנֵן() השיטה נקראת לאחר שאולינגו סיים לעבד את הבקשה. כאן אנו גם בודקים את שיטת הבקשה ומתבצעים את העסקה במידת הצורך.

3.5. בדיקה

בואו לבדוק את היישום שלנו בעזרת פשוט סִלְסוּל פקודות. הראשון שאנחנו יכולים לעשות זה לקבל את השירותים מטא נתונים $ מסמך:

תלתל // localhost: 8080 / odata / $ metadata

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

תלתל // localhost: 8080 / odata / CarMakers curl // localhost: 8080 / odata / CarModels curl // localhost: 8080 / odata / CarMakers (1) curl // localhost: 8080 / odata / CarModels (1) curl // localhost : 8080 / odata / CarModels (1) / CarMakerDetails 

עכשיו, בואו לבדוק שאילתה פשוטה המחזירה את הכל מייצרי מכוניות איפה שמו מתחיל ב- 'B':

תלתל // localhost: 8080 / odata / CarMakers? $ filter = startswith (שם, 'B') 

רשימה מלאה יותר של כתובות אתרים לדוגמא זמינה במאמר המדריך שלנו בנושא פרוטוקול OData.

5. מסקנה

במאמר זה ראינו כיצד ליצור שירות OData פשוט המגובה על ידי תחום JPA באמצעות Olingo V2.

נכון לכתיבת שורות אלה, יש בעיה פתוחה ב- JIRA של אולינגו העוקבת אחר העבודות על מודול JPA עבור V4, אך ההערה האחרונה מתוארכת לשנת 2016. יש גם מתאם JPA של צד שלישי בעל קוד פתוח המתארח במאגר GitHub של SAP, אשר אף על פי שלא שוחרר, נראה שהוא יותר שלם בתכונה בשלב זה מזה של אולינגו.

כרגיל, כל הקוד עבור מאמר זה זמין ב- GitHub.


$config[zx-auto] not found$config[zx-overlay] not found