JPA entitások lekérdezése Jpql-lel és natív SQL-lel
mintaalkalmazás
a cikkben tárgyalt kódrészletek a cikket kísérő mintaalkalmazásban használt Java forrásfájlokból származnak. Átnézve a minta archívumot, észreveheti, hogy ez egy egyszerű webes alkalmazás, amely a Java Servlet és a Java Persistence API technológiákon alapul. Az egyszerűség kedvéért, nem használja enterprise bab, kibocsátó JPQL lekérdezések közvetlenül a servlets. Ez azonban nem jelenti azt, hogy nem fogja tudni használni az enterprise beans—ban itt tárgyalt JPQL lekérdezéseket-a Jpql lekérdezéseket bármely Java EE összetevőben meghatározhatja.
az 1.ábra szemlélteti a minta entitások szerkezetét. Amint láthatja, az egymáshoz kapcsolódó entitások halmazát tartalmazza, Különböző típusú kapcsolatokkal. Ilyen elágazó struktúrára van szükség a jpql join lekérdezések használatának szemléltetéséhez, amelyeket a cikk későbbi részében a Jpql Joins meghatározása részben tárgyalunk.
1. ábra a minta alkalmazásban használt entitások közötti kapcsolatok
a minta alkalmazás beállításával, majd elindításával kapcsolatos részletes utasításokért olvassa el a readme-t.txt fájl a minta archívum gyökérkönyvtárában.
a Jpql használata Java EE alkalmazásokban
ha van némi gyakorlati tapasztalata az adatbázisokkal kapcsolatban, akkor valószínűleg már meg is nedvesítette a lábát az SQL-rel, amely a relációs adatbázisok információinak elérésére és manipulálására szolgáló szabványos eszköz. Valójában sok hasonlóság van a JPQL és az SQL között. Mindkettőt hosszú távon használják az adatbázis-adatok elérésére és manipulálására. Mindkettő nonprocedurális utasításokat használ-egy speciális tolmács által felismert parancsokat. Ezenkívül a JPQL szintaxisában hasonló az SQL-hez.
a fő különbség a JPQL és az SQL között abban rejlik, hogy az előbbi a JPA entitásokkal foglalkozik, míg az utóbbi közvetlenül a relációs adatokkal foglalkozik. Mint Java fejlesztő, akkor is talán érdekel, hogy megtanulják, hogy a JPQL, ellentétben az SQL/JDBC, szükségtelenné teszi, hogy használja JDBC API a Java kódot—a konténer nem minden ez a munka az Ön számára a színfalak mögött.
a JPQL lehetővé teszi a lekérdezések meghatározását a következő három utasítás egyikével: kiválasztás, frissítés vagy törlés. Érdekes megjegyezni, hogy az EntityManager API interfész olyan módszereket kínál, amelyek felhasználhatók az entitások feletti lekérési, frissítési és törlési műveletek végrehajtására is. Különösen ezek a keresési, egyesítési és eltávolítási módszerek. Ezeknek a módszereknek a használata, azonban, jellemzően egyetlen entitáspéldányra korlátozódik, kivéve, ha a lépcsőzetes érvénybe lép, természetesen. Ezzel szemben a JPQL utasítások nem rendelkeznek ilyen korlátozással—megadhatja a tömeges frissítési és törlési műveleteket az entitáskészletek felett, és meghatározhatja az entitáspéldányok készleteit visszaadó lekérdezéseket.
ahhoz, hogy jpql lekérdezést adjon ki a Java kódjából, az EntityManager API és a Query API megfelelő módszereit kell használnia, a következő általános lépések végrehajtásával:
- 1. Szerezzen be egy EntityManager példányt injekcióval vagy kifejezetten egy EntityManagerFactory példányon keresztül.
- 2. Hozzon létre egy lekérdezési példányt egy megfelelő EntityManager metódus meghívásával, például createQuery.
- 3. Állítson be egy lekérdezési paramétert vagy paramétereket, ha vannak ilyenek, a megfelelő lekérdezés setParameter módszerével.
- 4. Ha szükséges, a setmaxresults és/vagy a Setfirstresult lekérdezés metódusaival állítsa be a beolvasandó példányok maximális számát és/vagy adja meg az első beolvasandó példány helyét.
- 5. Ha szükséges, állítson be egy szállítóspecifikus tippet a setHint lekérdezés módszerével.
- 6. Szükség esetén állítsa be a lekérdezés végrehajtásának öblítési módját a setflushmode lekérdezés módszerével, felülírva az entity manager öblítési módját.
- 7. Végezze el a lekérdezést egy megfelelő lekérdezési módszerrel: getSingleResult vagy getResultList. Frissítési vagy törlési művelet esetén azonban az executeUpdate metódust kell használnia, amely a frissített vagy törölt entitáspéldányok számát adja vissza.
az EntityManager interfész módszerek teljes listája, valamint a lekérdezési API interfész módszerek megtalálhatók az Enterprise JavaBeans 3.0 specifikációban: Java Persistence API dokumentum, amely a JSR-220 része.
most, hogy van egy durva elképzelése arról, hogyan hozhat létre, majd adhat ki egy jpql lekérdezést, érdemes néhány gyakorlati példát látni. A következő kódrészlet egy servlet doGet metódusából származik,amely JPQL lekérdezést használ a lekérdezésben megadott ügyfél entitáshoz társított mögöttes relációs táblában tárolt összes ügyfélről.
... @PersistenceUnit private EntityManagerFactory emf; public void doGet( ... EntityManager em = emf.createEntityManager(); PrintWriter out = response.getWriter(); List<Customer> arr_cust = (List<Customer>)em.createQuery("SELECT c FROM Customer c") .getResultList(); out.println("List of all customers: "+""); Iterator i = arr_cust.iterator(); Customer cust; while (i.hasNext()) { cust = (Customer) i.next(); out.println(cust.getCust_id()+""); out.println(cust.getCust_name()+""); out.println(cust.getEmail()+""); out.println(cust.getPhone()+""); out.println("----------------" + ""); } ...
különösen érdekesek itt az EntityManager példány createQuery metódusa és a lekérdezési példány Getresultlist metódusa. A EntityManager a createQuery létrehozásához használt lekérdezés például, amelynek getResultList metódust ezután végre a JPQL lekérdezés átadott createQuery paraméterként. Mint sejteni lehet, A lekérdezés getresultlist metódusa egy lekérdezés eredményét adja vissza listaként, amelynek elemei ebben a példában az ügyfél típusba kerülnek.
ha egyetlen eredményt szeretne letölteni, a lekérdezési API felület a getSingleResult módszert kínálja, amint az a következő példában látható. Ne feledje azonban, hogy a getsingleresult használata kivételt okoz, ha több eredményt kap vissza.
ez a példa a lekérdezés setParameter metódusának használatát is szemlélteti, amelyen keresztül argumentumot köthet egy lekérdezési paraméterhez. A setParameter segítségével mind a megnevezett, mind a pozíciós paramétereket megkötheti. Itt azonban egy megnevezett paramétert köt.
... Integer cust_id =2; Customer cust = (Customer)em.createQuery("SELECT c FROM Customer c WHERE c.cust_id=:cust_id") .setParameter("cust_id", cust_id) .getSingleResult(); out.println("Customer with id "+cust.getCust_id()+" is: "+ cust.getCust_name()+""); ...
érdekes megjegyezni, hogy a SELECT jpql utasítás használata nem az egyetlen út, amikor egyetlen entitás példányt kell letölteni. Alternatív megoldásként használhatja az EntityManager Keresési módszerét, amely lehetővé teszi egyetlen entitás példány letöltését az entitás paraméterként átadott azonosítója alapján.
bizonyos helyzetekben előfordulhat, hogy csak néhány információt kell lekérnie a cél entitás példányból vagy példányokból, meghatározva egy jpql lekérdezést egy adott entitás mező vagy mezők ellen. Így nézne ki a fenti részlet, ha csak az itt lekérdezett ügyfél entitáspéldány cust_name mezőjének értékét kell lekérnie:
... Integer cust_id =2; String cust_name = (String)em.createQuery("SELECT c.cust_name FROM Customer c WHERE c.cust_id=:cust_id") .setParameter("cust_id", cust_id) .getSingleResult(); out.println("Customer with id "+cust_id+" is: "+cust_name+""); ...
Hasonlóképpen, az ügyfelek nevének teljes listájának megszerzéséhez a következő kódot használhatja:
... List<String> arr_cust_name = (List<String>)em.createQuery("SELECT c.cust_name FROM Customer c") .getResultList(); out.println("List of all customers: "+"<br/>"); Iterator i = arr_cust_name.iterator(); String cust_name; while (i.hasNext()) { cust_name = (String) i.next(); out.println(cust_name+"<br/>"); } ...
visszatérve az SQL-re, emlékeztethet arra, hogy az SQL-lekérdezés select listája a FROM záradékban megadott tábla vagy táblák több mezőjéből állhat. A JPQL-ben használhat egy álló select listát is, az adatokat csak az érdeklődésre számot tartó entitásmezőkből választja ki. Ebben az esetben azonban létre kell hoznia azt az osztályt, amelybe a lekérdezés eredményét leadja. A következő részben egy olyan jpql csatlakozási lekérdezés példáját láthatja, amelynek select listája egynél több entitásból származó mezőkből áll.
Jpql illesztések meghatározása
az SQL-hez hasonlóan a JPQL is lehetővé teszi a csatlakozási lekérdezések meghatározását. Az SQL-ben azonban általában olyan illesztést határoz meg, amely két vagy több táblából és/vagy nézetből származó rekordokat egyesít, beleértve a csatlakozási lekérdezés select listájában csak a táblák és nézetek kötelező mezőit. Ezzel szemben a jpql csatlakozási lekérdezés select listája általában egyetlen entitást vagy akár egyetlen entitás mezőt tartalmaz. Ennek oka a JPA technológia természetében rejlik. Miután beszerzett egy entitáspéldányt, a megfelelő getter módszerekkel navigálhat a kapcsolódó példányokhoz. Ez a megközelítés szükségtelenné teszi egy olyan lekérdezés meghatározását, amely egyszerre adja vissza az összes kapcsolódó entitáspéldányt.
például ahhoz, hogy információkat szerezzen a megrendelésekről az SQL-ben található sorokkal együtt, meg kell határoznia a join lekérdezést mind a purchaseOrders, mind az orderLineItems táblákon, megadva a lekérdezés select listájában a mindkét tábla mezőit. A JPQL használatakor azonban csak a PurchaseOrder entitáson keresztül definiálhat egy lekérdezést, majd szükség szerint a PurchaseOrder getorderlineitems metódusával navigálhat a megfelelő OrderLineItem példányokhoz. Ebben a példában csak akkor adhat meg JPQL lekérdezést a PurchaseOrder és OrderLineItem entitások felett, ha a letöltött PurchaseOrder példányokat az OrderLineItem-re alkalmazott feltétel vagy feltételek alapján kell szűrnie.
a következő kódrészlet egy példát mutat be a jpql join lekérdezésre működés közben. Annak érdekében, hogy jobban megértsük, hogyan kapcsolódnak egymáshoz az érintett entitások, visszatérhet a cikk korábbi mintaalkalmazás szakaszában bemutatott 1.ábrához.
... Double max = (Double) em.createQuery("SELECT MAX(p.price) FROM PurchaseOrder o JOIN o.orderLineItems l JOIN l.product p JOIN p.supplier s WHERE s.sup_name = 'Tortuga Trading'") .getSingleResult(); out.println("The highest price for an ordered product supplied by Tortuga Trading: "+ max + "<br/>"); ...
a fenti példában a Max aggregate függvényt használja a csatlakozási lekérdezés SELECT záradékában annak érdekében, hogy meghatározza a Tortuga Trading által szállított és legalább egyszer megrendelt termékek legmagasabb árát.
gyakoribb helyzet azonban az, amikor ki kell számítani, mondjuk, a megrendelt termékek teljes árát, amelyeket egy bizonyos szállító szállított. Ez az, ahol a SUM aggregate függvény jól jöhet. Az SQL-ben egy ilyen csatlakozási lekérdezés így nézhet ki:
SELECT SUM(p.price*l.quantity) FROM purchaseorders o JOIN orderlineitems l ON o.pono=l.pono JOIN products p ON l.prod_id=p.prod_id JOIN suppliers s ON p.sup_id=s.sup_id WHERE sup_name ='Tortuga Trading';
sajnos a JPQL-ben használt SUM függvény nem teszi lehetővé az aritmetikai kifejezés argumentumként történő átadását. Ez a gyakorlatban azt jelenti, hogy nem fogja tudni átadni p.ár*l.mennyiség mint érv a JPQL összegéhez. Vannak azonban módok a probléma megoldására. A következő példában definiáljuk a LineItemSum osztályt, amelynek konstruktorát a lekérdezés select listájában használjuk, figyelembe véve a P.price és az l.quantity paramétereket. Amit a LineItemSum konstruktor csinál, az szorozza meg p. ár l-vel. mennyiség, az eredményt az rslt osztályváltozójába mentve. Ezután megismételheti a lekérdezés által letöltött LineItemSum listát, összegezve a LineItemSum rslt változójának értékeit. A következő részlet bemutatja, hogyan lehet mindezt kódban megvalósítani:
package jpqlexample.servlets; ... class LineItemSum { private Double price; private Integer quantity; private Double rslt; public LineItemSum (Double price, Integer quantity){ this.rslt = quantity*price; } public Double getRslt () { return this.rslt; } public void setRslt (Double rslt) { this.rslt = rslt; } } public class JpqlJoinsServlet extends HttpServlet { ... public void doGet( ... List<LineItemSum> arr = (List<LineItemSum>)em.createQuery ("SELECT NEW jpqlexample.servlets.LineItemSum(p.price, l.quantity) FROM PurchaseOrder o JOIN o.orderLineItems l JOIN l.product p JOIN p.supplier s WHERE s.sup_name = 'Tortuga Trading'") .getResultList(); Iterator i = arr.iterator(); LineItemSum lineItemSum; Double sum = 0.0; while (i.hasNext()) { lineItemSum = (LineItemSum) i.next(); sum = sum + lineItemSum.getRslt(); } out.println("The total cost of the ordered products supplied by Tortuga Trading: "+ sum + "<br/>"); } }
a fenti példa többek között azt szemlélteti, hogyan használhat egyéni Java osztályt, nem entitás osztályt a JPQL lekérdezés select listájában, amely egynél több entitásból származó mezőket tartalmaz, a lekérdezés eredményét az adott osztályra vetítve. A legtöbb esetben azonban olyan lekérdezésekkel kell foglalkoznia, amelyek egy adott entitás példányát vagy példányainak listáját kapják.
a beolvasott Entitáspéldányok és az aktuális Perzisztencia környezet
az eddigi példák cikkben szereplő lekérdezési eredmények egyszerűen kinyomtatásra kerültek. A valós alkalmazásokban azonban előfordulhat, hogy további műveleteket kell végrehajtania a lekérdezés eredményein. Előfordulhat például, hogy frissítenie kell a letöltött példányokat, majd vissza kell tartania őket az adatbázisba. Ez felveti a kérdést: a jpql lekérdezés által beolvasott példányok készen állnak-e az alkalmazás további feldolgozására, vagy további lépésekre van szükség ahhoz, hogy készen álljanak erre? Különösen érdekes lenne megtudni, hogy a jelenlegi perzisztencia kontextust illetően milyen állapotban vannak a lekért entitáspéldányok.
ha van némi tapasztalata a Java Perzisztencia terén, akkor tudnia kell, mi a perzisztencia kontextus. Összefoglalva: a perzisztencia-környezet az adott környezethez társított EntityManager-példány által kezelt entitáspéldányok halmaza. Az előző példákban az EntityManager createQuery metódusát használta egy lekérdezés példányának létrehozásához egy JPQL lekérdezés végrehajtásához. Valójában az EntityManager API több mint húsz módszert tartalmaz az entitáspéldányok életciklusának kezelésére, a tranzakciók vezérlésére, valamint olyan Lekérdezéspéldányok létrehozására, amelyek metódusait ezután a megadott lekérdezés végrehajtására és a lekérdezés eredményének lekérésére használják.
a perzisztencia kontextus tekintetében az entitáspéldány a következő négy állapot egyikében lehet: új, felügyelt, leválasztott vagy eltávolított. A megfelelő EntityManager módszerével szükség szerint módosíthatja egy adott entitáspéldány állapotát. Érdekes azonban megjegyezni, hogy csak a felügyelt állapotban lévő példányok szinkronizálódnak az adatbázissal, amikor az adatbázisba öblítés történik. Pontosabban, az eltávolított állapotban lévő entitáspéldányok is szinkronizálva vannak, ami azt jelenti, hogy az adott példányoknak megfelelő adatbázisrekordok eltávolításra kerülnek az adatbázisból.
ezzel szemben az új vagy leválasztott állapotban lévő példányok nem lesznek szinkronizálva az adatbázissal. Ha például létrehoz egy új PurchaseOrder példányt, majd meghívja az EntityManager flush metódusát, akkor egy másik rekord nem jelenik meg abban a purchaseOrders táblában, amelyhez a PurchaseOrder entitás hozzá van rendelve. Ez azért van, mert az új PurchaseOrder példány nem lett csatolva a perzisztencia kontextushoz. Így nézhet ki a kód:
... em.getTransaction().begin(); Customer cust = (Customer) em.find(Customer.class, 1); PurchaseOrder ord = new PurchaseOrder(); ord.setOrder_date(new Date()); ord.setCustomer(cust); em.getTransaction().commit(); ...
a probléma megoldásához a flush meghívása előtt meg kell hívnia az EntityManager persist metódusát az új PurchaseOrder példányhoz, amint az a következő példában látható:
... em.getTransaction().begin(); Customer cust = (Customer) em.find(Customer.class, 1); PurchaseOrder ord = new PurchaseOrder(); ord.setOrder_date(new Date()); ord.setCustomer(cust); em.persist(ord); em.getTransaction().commit(); ...
Alternatív megoldásként, ha a cascade beállítást persistent vagy ALL értékre állította, amikor az ügyfél entitásban meghatározta a PurchaseOrder-rel való kapcsolatot, hozzáadhatja az újonnan létrehozott PurchaseOrder-példányt az ügyfélpéldányhoz társított megrendelések listájához, a persist művelet helyébe a következő lép:
cust.getPurchaseOrders().add(ord);
a fenti vita az entitáspéldány-állapotokról arra az érdekes kérdésre vezet minket, hogy a jpql lekérdezés által lekért entitáspéldányok automatikusan kezelhetővé válnak-e, vagy ügyelnie kell arra, hogy kifejezetten beállítsa állapotukat a kezelendő állapotra. A JPA specifikáció szerint az entitások lekérésének módjától függetlenül—függetlenül attól, hogy az EntityManager find metódusáról vagy lekérdezéséről van—e szó-ezek automatikusan kapcsolódnak az aktuális perzisztencia környezethez. Ez azt jelenti, hogy a jpql lekérdezés által lekért entitás példányok automatikusan kezelhetővé válnak. Módosíthatja például a letöltött példány mezőjének értékét, majd szinkronizálhatja azt az adatbázissal Az EntityManager flush metódusának meghívásával vagy az aktuális tranzakció végrehajtásával. Nem kell aggódnia a letöltött példányokhoz társított példányok állapota miatt sem. A helyzet az, hogy amikor először lép be egy társított példányba, az automatikusan kezelhetővé válik. Íme egy egyszerű példa, amely bemutatja, hogyan működik mindez a gyakorlatban:
... em.getTransaction().begin(); PurchaseOrder ord = (PurchaseOrder)em.createQuery("SELECT o FROM PurchaseOrder o WHERE o.pono = 1") .getSingleResult(); List<OrderLineItem> items = ord.getOrderLineItems(); Integer qnt = items.get(0).getQuantity(); out.println("Quantity of the first item : "+ qnt +"<br/>"); items.get(0).setQuantity(qnt+1); qnt = items.get(0).getQuantity(); em.getTransaction().commit(); out.println("Quantity of the first item : "+ qnt +"<br/>"); ...
ne feledje, hogy nem hívja meg a persist metódust a letöltött PurchaseOrder példányhoz, sem a kapcsolódó OrderLineItem példányhoz, amelyet itt módosítanak. Ennek ellenére a megrendelés első sorában végrehajtott módosítások a tranzakció végrehajtásakor megmaradnak az adatbázisban. Ez azért történik, mert mind a lekért entitáspéldányok, mind azok társításai automatikusan kapcsolódnak az aktuális perzisztencia-környezethez. Mint korábban említettük, az előbbiek kezelhetővé válnak, amikor letöltik őket, az utóbbiak pedig a kontextushoz kapcsolódnak, amikor hozzáférsz hozzájuk.
bizonyos esetekben előfordulhat, hogy a társításokat a lekérdezés végrehajtásakor, nem pedig az első hozzáféréskor kell a kontextushoz csatolni. Ez az, ahol a FETCH csatlakozni jól jön. Tegyük fel, hogy egy adott ügyfélhez tartozó összes megrendelést meg akarja szerezni, miután letöltötte az adott ügyfélpéldányt. Ez a megközelítés garantálja, hogy a lekérdezés végrehajtásakor rendelkezésre álló ügyfélmegrendelésekkel foglalkozik. Ha például egy új megrendelést ad hozzá egy másik környezethez, majd szinkronizálja az adatbázissal, mielőtt először hozzáférne a lekért ügyfélpéldányhoz társított rendelések listájához, akkor ezt a változást addig nem fogja látni, amíg nem frissíti az ügyfélpéldány állapotát az adatbázisból. A következő kódrészletben a join lekérdezést használja, amely visszaadja azt az Ügyfélpéldányt, amelynek cust_id értéke 1, és lekéri a beolvasott Ügyfélpéldányhoz társított PurchaseOrder példányokat.
... Customer cust = (Customer)em.createQuery("SELECT DISTINCT c FROM Customer c LEFT JOIN FETCH c.purchaseOrders WHERE c.cust_id=1") .getSingleResult(); ... List<PurchaseOrder> orders = cust.getPurchaseOrders(); ...
mivel nem része az explicit lekérdezés eredményének, az itt lekért Ügyfélpéldányhoz társított PurchaseOrder entitás-példányok a lekérdezés végrehajtásakor az aktuális perzisztencia-környezethez is lekérésre kerülnek.
natív SQL lekérdezések használata
érdekes megjegyezni, hogy nem korlátozódik a JPQL-re, amikor meghatározza a lekérdezéseket, amelyeket ezután a Query API-val kell végrehajtani. Meglepődhet, ha megtudja, hogy az EntityManager API módszereket kínál lekérdezési példányok létrehozására a natív SQL utasítások végrehajtásához. Az EntityManager módszerekkel létrehozott natív SQL lekérdezésekkel kapcsolatban a legfontosabb megérteni, hogy ezek, mint a JPQL lekérdezések, entitáspéldányokat adnak vissza, nem pedig adatbázis-táblarekordokat. Íme egy egyszerű példa egy dinamikus natív SQL lekérdezésre:
... List<Customer> customers = (List<Customer>)em.createNativeQuery ("SELECT * FROM customers", jpqlexample.entities.Customer.class) .getResultList(); Iterator i = customers.iterator(); Customer cust; out.println("Customers: " + "<br/>"); while (i.hasNext()) { cust = (Customer) i.next(); out.println(cust.getCust_name() +"<br/>"); } ...
a JPQL még mindig fejlődik, és nem rendelkezik sok ilyen fontos funkcióval az SQL-ben. A meghatározó Jpql csatlakozik korábbi szakaszban, látott egy példát a JPQL hiányosság: meg kellett csinálni egy csomó munkát a saját, mert a JPQL SUM aggregate függvény nem tudja, hogy egy aritmetikai kifejezés, mint a paraméter. Ezzel szemben az SQL SUM függvényének nincs ilyen korlátozása. Tehát ez egy jó példa arra, hogy a jpql helyettesítése natív SQL-vel hatékony lehet. A következő kód bemutatja, hogyan egyszerűsítheti a dolgokat ebben a példában, ha a natív SQL-t választja a JPQL helyett:
... String sup_name ="Tortuga Trading"; BigDecimal sum = (List)em.createNativeQuery("SELECT SUM(p.price*l.quantity) FROM orders o JOIN orderlineitems l ON o.pono=l.pono JOIN products p ON l.prod_id=p.prod_id JOIN suppliers s ON p.sup_id=s.sup_id WHERE sup_name =?1") .setParameter(1, sup_name) .getSingleResult(); out.println("The total cost of the ordered products supplied by Tortuga Trading: " + sum +"<br/>"); ...
a fenti példa többek között azt szemlélteti, hogy argumentumokat köthet a natív lekérdezési paraméterekhez. Különösen ugyanúgy kötheti az argumentumokat a helyzeti paraméterekhez, mintha egy JPQL lekérdezéssel foglalkozna.
a natív lekérdezések fő hátránya az eredménykötés összetettsége. A példában a lekérdezés egyetlen egyszerű típusú eredményt hoz létre, elkerülve ezzel a problémát. A gyakorlatban azonban gyakran összetett típusú eredménykészlettel kell foglalkoznia. Ebben az esetben deklarálnia kell egy entitást, amelyhez leképezheti a natív lekérdezést, vagy meg kell határoznia egy összetett eredményhalmazt, amely több entitásra vagy entitások és skalár eredmények keverékére van leképezve.
tárolt eljárások használata
a natív lekérdezések másik hátránya, hogy a Java kód közvetlenül függ az alapul szolgáló adatbázis-struktúrától. Ha módosítja ezt az alapul szolgáló struktúrát, akkor módosítania kell a szervletekben és/vagy más alkalmazásösszetevőkben érintett natív lekérdezéseket, majd újra kell fordítania és újra kell telepítenie ezeket az összetevőket. A probléma megoldása érdekében, miközben továbbra is natív lekérdezéseket használ, kihasználhatja a tárolt eljárások előnyeit, összetett SQL-lekérdezéseket helyezhet át az adatbázisban tárolt és végrehajtott programokba, majd ezeket a tárolt programokat hívhatja meg, ahelyett, hogy közvetlenül hívná az alapul szolgáló táblákat. Ez a gyakorlatban azt jelenti, hogy a tárolt eljárások megtakaríthatják az alapul szolgáló táblák kezelését közvetlenül a Java-kódban kódolt lekérdezésekből. Ennek a megközelítésnek az az előnye, hogy a legtöbb esetben nem kell módosítania a Java kódot az alapul szolgáló adatbázis-struktúra változásainak követéséhez. Ehelyett csak a tárolt eljárásokat kell rögzíteni.
visszatérve az előző szakaszban tárgyalt példára, az ott használt összetett illesztési lekérdezést áthelyezheti egy tárolt függvénybe, amely a következőképpen jön létre:
CREATE OR REPLACE FUNCTION sum_total(supplier VARCHAR2) RETURN NUMBER AS sup_sum NUMBER; BEGIN SELECT SUM(p.price*l.quantity) INTO sup_sum FROM orders o JOIN orderlineitems l ON o.pono=l.pono JOIN products p ON l.prod_id=p.prod_id JOIN suppliers s ON p.sup_id=s.sup_id WHERE sup_name = supplier; RETURN sup_sum; END; /
ez leegyszerűsíti a Java kódban használt natív lekérdezést, és eltávolítja a függőséget az alapul szolgáló táblákból:
... String sup_name ="Tortuga Trading"; BigDecimal sum = (BigDecimal)em.createNativeQuery("SELECT sum_total(?1) FROM DUAL") .setParameter(1, sup_name) .getSingleResult(); out.println("The total cost of the ordered products supplied by Tortuga Trading: " + sum +"<br/>"); ...
következtetés
ahogy ebben a cikkben megtanultad, a JPQL hatékony eszköz a relációs adatok eléréséhez a Java alkalmazásokból, a Java Perzisztencia felhasználásával. A JPQL, ellentétben az SQL / JDBC, akkor meg lekérdezéseket JPA entitások leképezett mögöttes adatbázis táblák helyett lekérdezése ezeket a táblákat közvetlenül, így foglalkozik egy réteg absztrakció, amely elrejti adatbázis részleteit az üzleti logika réteg. Azt is megtudta, hogy a jpql nem az egyetlen lehetőség, amikor lekérdezéseket hoz létre JPA entitások felett—bizonyos helyzetekben a natív SQL lekérdezések használata kényelmesebb.