Bevezetés az OData-ba az Olingóval

1. Bemutatkozás

Ez az oktatóanyag az OData Protokoll útmutatónk folytatása, ahol feltártuk az OData protokoll alapjait.

Most meglátjuk, hogyan lehet egy egyszerű OData szolgáltatást megvalósítani az Apache Olingo könyvtár segítségével.

Ez a könyvtár keretet biztosít az adatok OData protokoll használatával történő feltárásához, ezáltal egyszerű, szabványokon alapuló hozzáférést tesz lehetővé olyan információkhoz, amelyek egyébként el lennének zárva a belső adatbázisokban.

2. Mi az Olingo?

Az Olingo az egyik „kiemelt” OData implementáció, amely elérhető a Java környezetben - a másik az SDL OData keretrendszer. Az Apache Alapítvány fenntartja, és három fő modulból áll:

  • Java V2 - az OData V2-t támogató kliens és szerver könyvtárak
  • Java V4 - az OData V4-et támogató kiszolgálókönyvtárak
  • Javascript V4 - Javascript, csak kliens könyvtár, amely támogatja az OData V4-et

Ebben a cikkben csak a szerveroldali V2 Java könyvtárakra terjedünk ki, amelyek támogatják a JPA-val való közvetlen integrációt. Az így létrejött szolgáltatás támogatja a CRUD műveleteket és az OData protokoll egyéb szolgáltatásait, beleértve a rendelést, a lapozást és a szűrést.

Az Olingo V4 viszont csak a protokoll alacsonyabb szintű aspektusait kezeli, például a tartalom típusú egyeztetést és az URL elemzést. Ez azt jelenti, hogy rajtunk, fejlesztőkön múlik, hogy kódoljunk-e minden apró részletet olyan dolgokkal kapcsolatban, mint a metaadatok előállítása, háttér-lekérdezések generálása az URL-paraméterek alapján stb.

Ami a JavaScript kliens könyvtárat illeti, egyelőre kihagyjuk, mert mivel az OData HTTP alapú protokoll, bármely REST könyvtárat használhatunk a hozzáféréshez.

3. Egy Olingo Java V2 szolgáltatás

Hozzunk létre egy egyszerű OData szolgáltatást a kettővel EntitySets amelyeket a protokoll rövid bevezetésében használtunk. Alapjában véve az Olingo V2 egyszerűen JAX-RS erőforrások összessége, és mint ilyen, meg kell adnunk a szükséges infrastruktúrát annak használatához. Szükségünk van egy JAX-RS megvalósításra és egy kompatibilis szervlet tárolóra.

Ebben a példában a Spring Boot használatát választottuk - mivel gyors módja annak, hogy megfelelő környezetet teremtsen a szolgáltatásunk tárolására. Használjuk az Olingo JPA adapterét is, amely közvetlenül „beszél” egy felhasználó által szállítottal EntityManager az OData létrehozásához szükséges összes adat összegyűjtése érdekében EntityDataModel.

Noha nem szigorú követelmény, a JPA-adapter is nagyban leegyszerűsíti a szolgáltatásunk létrehozásának feladatát.

A szokásos Spring Boot függőségek mellett hozzá kell adnunk néhány Olingo edényt:

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

Ezeknek a könyvtáraknak a legfrissebb verziója elérhető a Maven központi adattárában:

  • olingo-odata2-mag
  • olingo-odata2-jpa-processzor-mag
  • olingo-odata2-jpa-processzor-ref

Szükségünk van ezekre a kizárásokra ebben a listában, mert az Olingo függ az EclipseLink-től, mint JPA-szolgáltatótól, és a JAX-RS verzióját is használja, mint a Spring Boot.

3.1. Domain osztályok

A JPA-alapú OData szolgáltatás Olingóval való megvalósításának első lépése a tartományi entitások létrehozása. Ebben az egyszerű példában csak két osztályt hozunk létre - CarMaker és Autómodell - egyetlen egy a sokhoz viszonyban:

@Entity @Table (name = "car_maker") public class CarMaker {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; @NotNull privát karakterlánc neve; @OneToMany (mappedBy = "maker", orphanRemoval = true, cascade = CascadeType.ALL) privát lista modellek; // ... kihagyók, beállítók és hashcode elhagyva} @Entity @Table (name = "car_model") public class CarModel {@Id @GeneratedValue (strategy = GenerationType.AUTO) private Long id; @NotNull privát karakterlánc neve; @NotNull privát Egész év; @NotNull private String sku; @ManyToOne (opcionális = false, fetch = FetchType.LAZY) @JoinColumn (name = "maker_fk") privát CarMaker készítő; // ... getterek, beállítók és hashcode elhagyva}

3.2. ODataJPAServiceFactory Végrehajtás

A fő alkotóelem, amelyet az Olingo számára át kell adnunk a JPA-tartomány adatainak kiszolgálása érdekében, egy absztrakt osztály, az ún. ODataJPAServiceFactory. Ennek az osztálynak ki kell terjednie ODataServiceFactory és adapterként működik a JPA és az OData között. Megnevezzük ezt a gyárat AutókODataJPAServiceFactory, a domainünk fő témája után:

A @Component public class CarsODataJPAServiceFactory kiterjeszti az ODataJPAServiceFactory {// egyéb metódusokat kihagyva ... ODataContext octx = ctx.getODataContext (); HttpServletRequest kérés = (HttpServletRequest) octx.getParameter (ODataContext.HTTP_SERVLET_REQUEST_OBJECT); EntityManager em = (EntityManager) kérés .getAttribute (EntityManagerFilter.EM_REQUEST_ATTRIBUTE); ctx.setEntityManager (em); ctx.setPersistenceUnitName ("alapértelmezett"); ctx.setContainerManaged (true); return ctx; }} 

Olingo felhívja a inicializáljaJPAContext () módszer, ha ez az osztály új ODataJPAContext minden OData kérés kezelésére használt. Itt használjuk a getODataJPAContext () metódust az alaposztályból egy „sima” példány megszerzéséhez, amelyet aztán testre szabunk.

Ez a folyamat kissé összezavarodott, ezért rajzoljunk egy UML-sorrendet annak vizualizálására, hogy mindez hogyan történik:

Vegye figyelembe, hogy szándékosan használjuk setEntityManager () ahelyett setEntityManagerFactory (). Szerezhetnénk egyet a Springből, de ha átadjuk az Olingónak, akkor ütközik azzal, ahogy a Spring Boot kezeli az életciklusát - különösen tranzakciók esetén.

Emiatt igénybe veszünk egy már létezőt EntityManager és tájékoztassa arról, hogy az életciklusát a külső irányítja. Az injektált EntityManager a példány az aktuális kérésnél elérhető attribútumból származik. Később meglátjuk, hogyan állítsuk be ezt az attribútumot.

3.3. Jersey erőforrás regisztráció

A következő lépés a regisztráció ServiceFactory Olingo futási idejével, és regisztrálja Olingo belépési pontját a JAX-RS futásidejébe. A-ban csináljuk ResourceConfig származtatott osztály, ahol meghatározzuk a szolgáltatásunk OData elérési útját is / odata:

@Component @ApplicationPath ("/ odata") nyilvános osztály A JerseyConfig kiterjeszti a ResourceConfig {public JerseyConfig (CarsODataJPAServiceFactory serviceFactory, EntityManagerFactory emf) {ODataApplication app = new ODataApplication (); app .getClasses () .forEach (c -> {if (! ODataRootLocator.class.isAssignableFrom (c)) {register (c);}}); regisztráció (új CarsRootLocator (serviceFactory)); register (új EntityManagerFilter (emf)); } // ... egyéb módszerek kihagyva}

Olingo biztosítja ODataApplication egy szokásos JAX-RS Alkalmazás osztály, amely regisztrál néhány szolgáltatót a szokásos visszahívás használatával getClasses ().

A. Kivételével mindet használhatjuk ODataRootLocator osztály as-is. Ez a bizonyos felelős a mi példáinkért ODataJPAServiceFactory megvalósítás Java használatával newInstance () módszer. De mivel azt akarjuk, hogy a Spring kezelje helyettünk, helyére kell állítani egy egyedi lokátorral.

Ez a lokátor egy nagyon egyszerű JAX-RS erőforrás, amely kibővíti az Olingo készletét ODataRootLocator és visszaadja a tavasszal kezelt ServiceFactory amikor szükség van:

A @Path ("/") public class CarsRootLocator kiterjeszti az ODataRootLocator {private CarsODataJPAServiceFactory serviceFactory; public CarsRootLocator (CarsODataJPAServiceFactory serviceFactory) {this.serviceFactory = serviceFactory; } @Orride public ODataServiceFactory getServiceFactory () {return this.serviceFactory; }} 

3.4. EntityManager Szűrő

Az OData szolgáltatásunk utolsó darabja a EntityManagerFilter. Ez a szűrő egy EntityManager az aktuális kérelemben, így elérhető a ServiceFactory. Ez egy egyszerű JAX-RS @ Szolgáltató osztály, amely mindkettőt megvalósítja ContainerRequestFilter és ContainerResponseFilter interfészeket, így megfelelően tudja kezelni a tranzakciókat:

@Provider public static class EntityManagerFilter ContainerRequestFilter, ContainerResponseFilter {public static final String EM_REQUEST_ATTRIBUTE = EntityManagerFilter.class.getName () + "_ENTITY_MANAGER"; privát végleges EntityManagerFactory emf; @Context private HttpServletRequest httpRequest; public EntityManagerFilter (EntityManagerFactory emf) {this.emf = emf; } @Orride public void filter (ContainerRequestContext ctx) dobja az IOException {EntityManager em = this.emf.createEntityManager (); httpRequest.setAttribute (EM_REQUEST_ATTRIBUTE, em); if (! "GET" .equalsIgnoreCase (ctx.getMethod ())) {em.getTransaction (). begin (); }} @Orride public void filter (ContainerRequestContext requestContext, ContainerResponseContext responseContext) dobja az IOException {EntityManager em = (EntityManager) httpRequest.getAttribute (EM_REQUEST_ATTRIBUTE); if (! "GET" .equalsIgnoreCase (requestContext.getMethod ())) {EntityTransaction t = em.getTransaction (); if (t.isActive () &&! t.getRollbackOnly ()) {t.commit (); }} em.close (); }} 

Az első szűrő() Az erőforrás-kérelem kezdetén meghívott metódus a megadottat használja EntityManagerFactory hogy újat hozzon létre EntityManager példány, amelyet aztán egy attribútum alá helyezünk, hogy később a ServiceFactory. A GET kéréseket is kihagyjuk, mivel nem lehetnek mellékhatásai, ezért nem lesz szükség tranzakcióra.

A második szűrő() metódust hívják meg, miután az Olingo befejezte a kérés feldolgozását. Itt ellenőrizzük a kérés módszerét is, és szükség esetén elvégezzük a tranzakciót.

3.5. Tesztelés

Teszteljük a megvalósításunkat egyszerű használatával becsavar parancsokat. Az első, amit tehetünk, az, hogy megszerezzük a szolgáltatásokat $ metaadatok dokumentum:

curl // localhost: 8080 / odata / $ metadata

A várakozásoknak megfelelően a dokumentum két típust tartalmaz - CarMaker és Autómodell - és egy egyesület. Most játsszunk még egy kicsit a szolgáltatásunkkal, lekérve a legfelső szintű gyűjteményeket és entitásokat:

curl // 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 

Most teszteljünk egy egyszerű lekérdezést, amely az összeset visszaadja Autó gyártók ahol neve „B” betűvel kezdődik:

curl // localhost: 8080 / odata / CarMakers? $ filter = startswith (név, 'B') 

Az URL-ek teljes listája az OData Protocol Guide cikkünkben található.

5. Következtetés

Ebben a cikkben azt láthattuk, hogyan lehet létrehozni egy egyszerű OData szolgáltatást, amelyet JPA tartomány támogat az Olingo V2 használatával.

Az írás kezdetén egy nyitott kérdés merült fel az Olingo JIRA-jának nyomon követésében a V4 JPA moduljának munkáin, de az utolsó megjegyzés 2016-ig nyúlik vissza. Van egy harmadik féltől származó nyílt forráskódú JPA-adapter is, amelyet az SAP GitHub-tárházában tárolnak, bár nem jelent meg, úgy tűnik, hogy ezen a ponton sokkal teljesebb, mint Olingoé.

Szokás szerint a cikk összes kódja elérhető a GitHubon.