JPA-entiteiten opvragen met Jpql en Native SQL

voorbeeldtoepassing

de codefragmenten die in het artikel worden besproken, zijn afkomstig van de Java-bronbestanden die worden gebruikt in de voorbeeldtoepassing die bij het artikel hoort. Kijkend door het voorbeeld archief, kunt u merken dat dit een eenvoudige webapplicatie op basis van de Java Servlet en Java Persistence API technologieën. Voor de eenvoud, het maakt geen gebruik van enterprise beans, de uitgifte van JPQL queries rechtstreeks vanuit servlets. Het betekent echter niet dat u de JPQL—queries die hier in enterprise beans worden besproken, niet kunt gebruiken-U kunt JPQL-queries in alle Java EE-componenten definiëren.

figuur 1 illustreert de structuur van de steekproefentiteiten. Zoals je kunt zien, bevat het een set van entiteiten die met elkaar verbonden zijn met relaties van verschillende types. Een dergelijke vertakte structuur is nodig om het gebruik van jpql join queries te illustreren besproken in de Defining JPQL Joins sectie later in het artikel.

figuur 1 relaties tussen de entiteiten gebruikt binnen de sample applicatie

voor een gedetailleerde instructie over het opzetten en vervolgens starten van de sample app, kunt u verwijzen naar de readme.txt-bestand in de hoofdmap van het voorbeeldarchief.

Jpql gebruiken in Java EE applicaties

als je enige praktische ervaring hebt met databases, heb je waarschijnlijk al je voeten nat gemaakt met SQL, de standaard tool die een set van statements biedt voor het benaderen en manipuleren van informatie in relationele databases. In feite zijn er veel overeenkomsten tussen JPQL en SQL. Beide worden gebruikt om databasegegevens te benaderen en te manipuleren, op de lange termijn. En beide gebruiken niet-procedurele statements-commando ‘ s herkend door een speciale tolk. Verder is JPQL vergelijkbaar met SQL in zijn syntaxis.

het belangrijkste verschil tussen JPQL en SQL ligt in het feit dat de eerste betrekking heeft op JPA-entiteiten, terwijl de tweede rechtstreeks betrekking heeft op relationele gegevens. Als Java ontwikkelaar ben je misschien ook geïnteresseerd om te leren dat het gebruik van JPQL, in tegenstelling tot SQL/JDBC, de noodzaak voor u elimineert om JDBC API te gebruiken van uw Java—code-de container doet al dit werk voor u achter de schermen.

met JPQL kunt u queries definiëren met behulp van een van de volgende drie statements: selecteren, bijwerken of verwijderen. Het is interessant om op te merken dat de EntityManager API-interface biedt methoden die ook kunnen worden gebruikt voor het uitvoeren van ophalen, bijwerken en verwijderen van operaties over entiteiten. In het bijzonder, dit zijn zoeken, samenvoegen, en verwijderen methoden. Het gebruik van deze methoden, echter, is meestal beperkt tot een enkele entiteit instantie, tenzij cascading van kracht wordt, natuurlijk. In tegenstelling, jpql statements hebben een dergelijke beperking niet—u kunt bulk update en verwijderen operaties over sets van entiteiten definiëren, en queries retourneren sets van entiteit instanties definiëren.

om een jpql query uit te geven vanuit uw Java-code, moet u de juiste methoden van de EntityManager API en Query API gebruiken, waarbij u de volgende algemene stappen uitvoert:

  • 1. Verkrijg een instantie van EntityManager, met behulp van injectie of expliciet via een EntityManagerFactory instantie.
  • 2. Maak een instantie van Query door het aanroepen van een geschikte EntityManager ‘ s methode, zoals createQuery.
  • 3. Stel een query parameter of parameters, indien van toepassing, met behulp van een geschikte Query setParameter methode.
  • 4. Stel indien nodig het maximum aantal instanties in om op te halen en/of Specificeer de positie van de eerste instantie om op te halen, met behulp van de methoden van setmaxresult en/of setFirstResult Query.
  • 5. Indien nodig, Stel een leverancier-specifieke hint, met behulp van de setHint Query methode.
  • 6. Stel indien nodig de spoelmodus in voor de uitvoering van de query met de methode van de Setflushmode-Query, waarbij de spoelmodus van de entity manager wordt vervangen.
  • 7. Voer de query uit met behulp van de methode van een geschikte Query: getSingleResult of getResultList. In het geval van een update of delete operatie, echter, moet u de executeUpdate methode gebruiken, die het aantal entiteit exemplaren bijgewerkt of verwijderd retourneert.

de volledige lijst van de EntityManager interface methoden, evenals de Query API interface methoden, kan worden gevonden in de Enterprise JavaBeans 3.0 specificatie: Java Persistence API document, dat deel uitmaakt van JSR-220.

Nu u een globaal idee hebt van hoe u een jpql-query kunt maken en vervolgens uitgeven, wilt u misschien enkele praktische voorbeelden zien. Het volgende codefragment is afkomstig van de doGet-methode van een servlet die een jpql-query gebruikt om informatie te verkrijgen over alle klanten die zijn opgeslagen in de onderliggende relationele tabel die is gekoppeld aan de klantentiteit die in de query is opgegeven.

  ... @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("----------------" + ""); } ... 

van bijzonder belang hier zijn de createQuery methode van de entitymanager instantie en de getResultList methode van de Query instantie. De EntityManager ‘ s createQuery wordt gebruikt om de Query instantie waarvan getResultList methode wordt vervolgens gebruikt om de jpql query doorgegeven aan createQuery uit te voeren als de parameter. Zoals je zou kunnen raden, de Query ‘ s getResultList methode geeft het resultaat van een query als een lijst waarvan de elementen, in dit specifieke voorbeeld, zijn gegoten om de klant te typen.

als u een enkel resultaat wilt ophalen, biedt de Query API-interface de getSingleResult-methode, zoals weergegeven in het volgende voorbeeld. Merk echter op dat het gebruik van getSingleResult een uitzondering zal veroorzaken als u meerdere resultaten terug krijgt.

ook dit voorbeeld illustreert het gebruik van de Setparameter methode van de Query waarmee u een argument kunt binden aan een query parameter. Met setParameter kunt u zowel named als positionele parameters binden. Hier bind je echter een benoemde parameter.

  ... 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()+""); ... 

het is interessant om op te merken dat het gebruik van een SELECT jpql statement niet de enige manier is om een enkele entiteit instantie op te halen. Als alternatief kunt u gebruik maken van de entitymanager ‘ s find methode, waarmee u een enkele entiteit instantie op te halen op basis van het ID van de entiteit doorgegeven als de parameter.

in sommige situaties moet u mogelijk slechts enkele informatie ophalen uit de instantie of instanties van de doelentiteit, waarbij een jpql-query wordt gedefinieerd tegen een bepaald veld of velden van de entiteit. Dit is hoe het bovenstaande fragment eruit zou zien, als u alleen de waarde van het veld cust_name van de klantentiteit wilt ophalen die hier wordt opgevraagd:

  ... 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+""); ... 

evenzo, om de volledige lijst van de namen van klanten te verkrijgen, kunt u de volgende code gebruiken:

  ... 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/>"); } ... 

Als u teruggaat naar SQL, herinnert u zich misschien dat de select-lijst van een SQL-query kan bestaan uit verschillende velden uit de tabel of tabellen die zijn gespecificeerd in De FROM-clausule. In JPQL kunt u ook een uitgebreide select-lijst gebruiken, waarbij u de gegevens alleen selecteert uit de entity-velden van belang. In dat geval moet u echter de klasse maken waarnaar u het query-resultaat zult casten. In de volgende sectie ziet u een voorbeeld van een jpql join query waarvan de selectielijst bestaat uit de velden die zijn afgeleid van meer dan één entiteit.

Jpql-Joins

net als SQL kunt u met JPQL join queries definiëren. In SQL definieert u echter normaal gesproken een join die records uit twee of meer tabellen en/of weergaven combineert, inclusief alleen verplichte velden uit die tabellen en weergaven in de select lijst van de join query. In tegenstelling, de select lijst van een jpql join query bevat meestal een enkele entiteit of zelfs een enkele entiteit veld. De reden hiervoor ligt in de aard van de PPV-technologie. Zodra u een entiteit instantie te verkrijgen, kunt u vervolgens navigeren naar de gerelateerde instanties met behulp van overeenkomstige getter methoden. Deze aanpak maakt het onnodig voor u om een query die alle gerelateerde entiteit exemplaren van belang in een keer zal terugkeren definiëren.

bijvoorbeeld, om informatie te verkrijgen over orders samen met hun regelitems in SQL, moet u een join query definiëren op zowel de inkooporders als de orderLineItems tabellen, waarbij de velden uit de beide tabellen in de select lijst van de query worden gespecificeerd. Bij het gebruik van JPQL, echter, kunt u een query alleen over de Purchase Order entiteit te definiëren, en vervolgens navigeer naar overeenkomstige OrderLineItem gevallen met behulp van de Purchase Order getorderlineitem methode zoals vereist. In dit voorbeeld wilt u misschien een jpql query over de inkooporder en OrderLineItem entiteiten alleen definiëren als u opgehaald inkooporder exemplaren moet filteren op basis van een voorwaarde of voorwaarden die op OrderLineItem worden toegepast.

het volgende fragment toont een voorbeeld van jpql join query in actie. Om beter te begrijpen hoe de betrokken entiteiten zijn gerelateerd aan elkaar, kunt u terug naar figuur 1 weergegeven in de voorbeeldtoepassing sectie eerder in het artikel.

  ... 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/>"); ... 

in het bovenstaande voorbeeld, gebruikt u de max aggregate functie in de Select clausule van de join query om het hoogste prijs product te bepalen, van de producten die door Tortuga Trading zijn geleverd en minstens één keer zijn besteld.

een meer voorkomende situatie is echter wanneer u bijvoorbeeld de totale prijs moet berekenen van de bestelde producten die door een bepaalde leverancier zijn geleverd. Dit is waar de sum aggregate functie van pas kan komen. In SQL kan zo ‘ n join query er zo uitzien:

  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'; 

helaas staat de SUM-functie in JPQL je niet toe om een rekenkundige uitdrukking als argument door te geven. Wat dit in de praktijk betekent, is dat je p.prijs*l.hoeveelheid niet als argument kunt doorgeven aan de som van de JPQL. Er zijn echter manieren om dit probleem te omzeilen. In het volgende voorbeeld definieer je class LineItemSum waarvan de constructor vervolgens wordt gebruikt in de select lijst van de query, waarbij p.price en L.quantity als parameters worden gebruikt. Wat de LineItemSum constructor doet is P.prijs vermenigvuldigen met L. hoeveelheid, het opslaan van het resultaat naar zijn rslt klasse variabele. Vervolgens kun je door de lijst met LineItemSum die door de query is opgehaald, de waarden van de rslt variabele van de LineItemSum optellen. Het volgende fragment laat zien hoe dit alles in code kan worden geïmplementeerd:

  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/>"); } } 

onder andere, het bovenstaande voorbeeld illustreert hoe je een aangepaste Java-klasse zou kunnen gebruiken, niet een entiteit klasse, in de jpql query ‘ s select lijst die de velden afgeleid van meer dan één entiteit bevat, casting het resultaat van de query naar die klasse. In de meeste gevallen, echter, zult u te maken hebben met query ‘ s die een instantie of een lijst van instanties van een bepaalde entiteit ontvangen.

opgehaalde entiteiten en de huidige persistentie Context

de zoekresultaten in de artikelvoorbeelden zijn tot nu toe eenvoudig uitgeprint. In real-world applicaties, hoewel, u kan nodig zijn om een aantal verdere bewerkingen uit te voeren op de query resultaten. Het kan bijvoorbeeld nodig zijn om de opgehaalde exemplaren bij te werken en ze vervolgens terug te zetten naar de database. Dit roept de vraag op: zijn de instanties die worden opgehaald door een jpql query klaar om verder te worden verwerkt door de applicatie, of een aantal extra stappen nodig zijn om ze klaar voor dat te maken? In het bijzonder, het zou interessant zijn om te leren in welke staat, met betrekking tot de huidige persistentie context, opgehaalde entiteit instanties zijn.

als u enige ervaring hebt met Java Persistence, moet u weten wat een persistence context is. Om samen te vatten, een persistence context is een set van entiteit instanties beheerd door de EntityManager instantie geassocieerd met die context. In de voorgaande voorbeelden gebruikte je de EntityManager ‘ s createQuery methode om een instantie van Query te maken voor het uitvoeren van een jpql query. Eigenlijk bevat de EntityManager API meer dan twintig methoden om de levenscyclus van entiteiten te beheren, transacties te controleren en Query-exemplaren te maken waarvan de methoden vervolgens worden gebruikt om de opgegeven query uit te voeren en het query-resultaat op te halen.

met betrekking tot een persistentiecontext kan een entiteit zich in een van de volgende vier toestanden bevinden: nieuw, beheerd, losgemaakt of verwijderd. Met behulp van een geschikte EntityManager ‘ s methode, kunt u de status van een bepaalde entiteit instantie te wijzigen als dat nodig is. Het is interessant om op te merken, echter, dat alleen instanties in managed state worden gesynchroniseerd met de database, wanneer spoelen naar de database optreedt. Om precies te zijn, de entiteit instanties in de verwijderde staat worden ook gesynchroniseerd, wat betekent dat de database records die overeenkomen met die instanties worden verwijderd uit de database.

daarentegen worden instanties in de nieuwe of losstaande status niet gesynchroniseerd met de database. Als u bijvoorbeeld een nieuwe inkooporder-instantie maakt en vervolgens de flushmethode van de EntityManager aanroept, verschijnt er geen ander record in de inkooporders-tabel waaraan de inkooporderentiteit is toegewezen. Dit komt omdat die nieuwe inkooporder instantie niet is gekoppeld aan de persistentie context. Hier is hoe de code eruit zou kunnen zien:

  ... 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(); ... 

om het probleem op te lossen, moet u een beroep doen op de entitymanager ‘ s persist methode voor de nieuwe inkooporder instantie voordat u de flush aanroept, zoals weergegeven in het volgende voorbeeld:

  ... 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(); ... 

als alternatief, mits u de cascade-optie hebt ingesteld op PERSIST or ALL bij het definiëren van de relatie met inkooporder in de klantentiteit, kunt u de nieuw aangemaakte inkooporder-instantie toevoegen aan de lijst van de orders die zijn gekoppeld aan de klantinstantie, en de persist-operatie vervangen door de volgende:

  cust.getPurchaseOrders().add(ord); 

de bovenstaande discussie over entity instance statussen leidt ons tot de interessante vraag of de entity instances opgehaald door een jpql query automatisch worden beheerd, of je moet ervoor zorgen om expliciet hun status in te stellen om te worden beheerd. Volgens de JPA-specificatie, ongeacht de manier waarop je entiteiten opvraagt-of het nu de zoekmethode van de EntityManager is of een query—worden ze automatisch gekoppeld aan de huidige persistence context. Dit betekent dat entiteiten die door een jpql-query worden opgehaald, automatisch worden beheerd. U kunt bijvoorbeeld de waarde van het veld van een opgehaalde instantie wijzigen en die wijziging vervolgens synchroniseren met de database door de flushmethode van de EntityManager aan te roepen of de huidige transactie te committen. U hoeft zich geen zorgen te maken over de toestand van de instanties in verband met de opgehaalde instanties ofwel. Het feit is dat de eerste keer dat u toegang tot een bijbehorende instantie wordt automatisch beheerd. Hier is een eenvoudig voorbeeld dat laat zien hoe dit alles in de praktijk werkt:

  ... 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/>"); ... 

merk op dat u niet de persist methode voor de opgehaalde inkooporder instantie, noch voor de bijbehorende OrderLineItem instantie wordt hier gewijzigd. Ondanks dit feit, zullen de wijzigingen in de eerste regel item in de bestelling worden voortgezet in de database bij het plegen van de transactie. Dit gebeurt omdat zowel de opgehaalde entiteit instanties en hun associaties automatisch worden gekoppeld aan de huidige persistentie context. Zoals eerder vermeld, de eerste worden beheerd wanneer ze worden opgehaald, en de laatste zijn gekoppeld aan de context als je ze opent.

in sommige situaties kunt u associaties aan de context koppelen bij het uitvoeren van de query, in plaats van bij de eerste toegang. Dit is waar een FETCH JOIN van pas komt. Zeggen, u wilt alle bestellingen die behoren tot een bepaalde klant te verkrijgen, bij het ophalen van die klant instantie. Deze aanpak garandeert dat je te maken hebt met de orders van de klant beschikbaar op het moment van de query uitvoering. Als, bijvoorbeeld, een nieuwe bestelling wordt toegevoegd aan een andere context en vervolgens gesynchroniseerd met de database voordat u de eerste keer toegang tot de lijst met bestellingen in verband met de klant instantie opgehaald, zult u deze wijziging niet zien totdat u de status van de klant instantie te vernieuwen uit de database. In het volgende fragment, gebruikt u de join query die de klant instantie waarvan cust_id is 1 retourneert, en haalt de inkooporder exemplaren die zijn gekoppeld aan de klant instantie wordt opgehaald.

  ... 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(); ... 

omdat het geen deel uitmaakt van de expliciete query resultaat, de inkooporder entiteit gevallen in verband met de klant instantie opgehaald hier worden ook opgehaald en gekoppeld aan de huidige persistentie context op de query uitvoering.

gebruik makend van Native SQL Queries

het is interessant om op te merken dat u niet beperkt bent tot JPQL wanneer u queries definieert die vervolgens met Query API moeten worden uitgevoerd. Het zal je misschien verbazen dat de EntityManager API methoden biedt voor het aanmaken van Query ‘ s voor het uitvoeren van native SQL statements. Het belangrijkste om te begrijpen over native SQL queries gemaakt met EntityManager methoden is dat ze, zoals JPQL queries, entity instances retourneren, in plaats van database tabel records. Hier is een eenvoudig voorbeeld van een dynamische native SQL query:

  ... 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/>"); } ... 

JPQL is nog steeds in ontwikkeling, en heeft niet veel van deze belangrijke functies beschikbaar in SQL. In het definiëren van Jpql Joins eerder sectie, zag je een voorbeeld van jpql ’s onvolledigheid: je moest veel werk op jezelf doen omdat de JPQL’ s SUM aggregate functie geen rekenkundige expressie als parameter kan nemen. In tegenstelling, de SQL SUM functie heeft niet zo ‘ n beperking. Dus, dit is een goed voorbeeld van waar het vervangen van JPQL door native SQL efficiënt zou kunnen zijn. De volgende code illustreert hoe je dingen in dit specifieke voorbeeld kunt vereenvoudigen door native SQL boven JPQL te kiezen:

  ... 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/>"); ... 

het bovenstaande voorbeeld illustreert onder andere dat u argumenten kunt binden aan native query parameters. In het bijzonder kun je argumenten binden aan positionele parameters op dezelfde manier als wanneer je te maken hebt met een jpql query.

het grootste nadeel van native queries is de complexiteit van resultaatbinding. In het voorbeeld, de query produceert een enkel resultaat van een eenvoudig type, dus het vermijden van dit probleem. In de praktijk heb je echter vaak te maken met een resultatenreeks van een complex type. In dit geval moet u een entiteit declareren waaraan u uw eigen query kunt toewijzen, of een complexe resultaatset definiëren die is toegewezen aan meerdere entiteiten of aan een combinatie van entiteiten en scalaire resultaten.

met behulp van opgeslagen Procedures

een ander nadeel van native queries is dat uw Java-code direct afhankelijk wordt van de onderliggende database-structuur. Als u die onderliggende structuur wijzigt, moet u de betreffende native query ‘ s in uw servlets en/of andere applicatiecomponenten aanpassen, waarna u die componenten opnieuw moet compileren en herschikken. Om dit probleem op te lossen, terwijl u nog steeds native query ’s gebruikt, kunt u gebruikmaken van opgeslagen procedures, complexe SQL-query’ s verplaatsen naar programma ’s die zijn opgeslagen en uitgevoerd in de database, en vervolgens die opgeslagen programma’ s aanroepen in plaats van rechtstreeks naar de onderliggende tabellen te bellen. Wat dit betekent in de praktijk is dat opgeslagen procedures bespaart u de moeite van het omgaan met de onderliggende tabellen rechtstreeks uit de query ‘ s die hard-gecodeerd in uw Java-code. Het voordeel van deze aanpak is dat u in de meeste gevallen uw Java-code niet hoeft te wijzigen om de wijzigingen in de onderliggende database-structuur te volgen. In plaats daarvan moeten alleen de opgeslagen procedures worden gerepareerd.

om terug te keren naar het voorbeeld besproken in de vorige paragraaf, kunt u de complexe join query die daar gebruikt wordt verplaatsen naar een opgeslagen functie, gemaakt als volgt:

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

dit vereenvoudigt de native query gebruikt in uw Java-code en verwijdert de afhankelijkheid van de onderliggende tabellen:

  ... 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/>"); ... 

conclusie

zoals u in dit artikel hebt geleerd, is JPQL een krachtige tool als het gaat om toegang tot relationele gegevens vanuit Java-toepassingen die Java-persistentie gebruiken. Met JPQL, in tegenstelling tot met SQL/JDBC, definieert u query ‘ s over JPA-entiteiten die zijn toegewezen aan onderliggende databasetabellen in plaats van die tabellen direct te bevragen, waardoor u te maken krijgt met een abstractielaag die databasedetails verbergt uit de business logic-laag. Je hebt ook geleerd dat JPQL niet de enige optie is als het gaat om het maken van query ’s over JPA—entiteiten-in sommige situaties is het gebruik van native SQL query’ s handiger.