Dotazu JPA Entity s JPQL a Nativní SQL

Ukázková Aplikace

kód úryvky diskutovány v článku jsou převzaty z Java zdrojové soubory použité v ukázkové aplikace doprovázející článek. Při pohledu na ukázkový archiv si můžete všimnout, že se jedná o jednoduchou webovou aplikaci založenou na technologiích Java Servlet a Java Persistence API. Pro jednoduchost nepoužívá podnikové fazole a vydává dotazy JPQL přímo ze servletů. Neznamená to však, že nebudete moci využít JPQL dotazy tady diskutovali v enterprise fazole—můžete definovat JPQL dotazů v jakékoliv Java EE komponenty.

Obrázek 1 znázorňuje strukturu subjektů vzorku. Jak vidíte, obsahuje soubor entit, které se navzájem vztahují se vztahy různých typů. Taková rozvětvená struktura je potřebná pro ilustraci použití dotazů jpql join diskutovaných v sekci definování Jpql Joins dále v článku.

Obrázek 1 Vztahy mezi subjekty využívány v rámci ukázkové aplikace

Pro podrobné instrukce o tom, jak nastavit a pak spustit ukázkovou aplikaci, můžete odkazovat na soubor readme.txt soubor v kořenovém adresáři ukázkového archivu.

Pomocí JPQL v Java EE aplikací

Pokud máte nějaké praktické zkušenosti s databázemi, s největší pravděpodobností jste již dostali vaše nohy mokré s SQL, standardní nástroj nabízí sadu příkazů pro přístup a manipulaci s informací v relačních databázích. Ve skutečnosti existuje mnoho podobností mezi JPQL a SQL. Oba se používají k přístupu a manipulaci s daty databáze, v dlouhodobém horizontu. A oba používají neprocedurální příkazy-příkazy rozpoznané speciálním tlumočníkem. Kromě toho je JPQL ve své syntaxi podobný SQL.

hlavní rozdíl mezi JPQL a SQL spočívá v tom, že první se zabývá entitami JPA, zatímco druhá se zabývá přímo relačními daty. Jako vývojář Java vás také možná zajímá, že používání JPQL, na rozdíl od SQL/JDBC, eliminuje potřebu používat JDBC API z vašeho kódu Java-kontejner dělá vše pro vás v zákulisí.

JPQL umožňuje definovat dotazy pomocí jednoho z následujících tří příkazů: vybrat, Aktualizovat nebo odstranit. Je zajímavé poznamenat, že rozhraní API EntityManager nabízí metody, které lze také použít k provádění operací načítání, aktualizace a mazání nad entitami. Jedná se zejména o metody najít, sloučit a odebrat. Použití těchto metod je však obvykle omezeno na jednu instanci entity, pokud se kaskádování samozřejmě neprojeví. Naproti tomu příkazy JPQL takové omezení nemají—můžete definovat operace hromadné aktualizace a mazání nad sadami entit a definovat dotazy vracející sady instancí entit.

Chcete-li vydat dotaz JPQL z vašeho kódu Java, musíte použít vhodné metody API EntityManager a Query API a provést následující obecné kroky:

  • 1. Získejte instanci EntityManager, pomocí injekce nebo explicitně prostřednictvím instance EntityManagerFactory.
  • 2. Vytvořte instanci dotazu vyvoláním příslušné metody EntityManager, například createQuery.
  • 3. Nastavte parametr dotazu nebo parametry, pokud existují, pomocí metody setParameter příslušného dotazu.
  • 4. Pokud je potřeba, nastavit maximální počet instancí pro získání a/nebo určit polohu prvního stupně k načtení, pomocí setMaxResults a/nebo setFirstResult Dotazu metod.
  • 5. V případě potřeby nastavte nápovědu specifickou pro dodavatele pomocí metody dotazu setHint.
  • 6. V případě potřeby nastavte režim flush pro provádění dotazu metodou dotazu setFlushMode a přepište režim flush Správce entity.
  • 7. Spusťte dotaz pomocí metody příslušného dotazu: getSingleResult nebo getResultList. V případě operace aktualizace nebo odstranění však musíte použít metodu executeUpdate, která vrací počet instancí entity aktualizovaných nebo odstraněných.

úplný seznam EntityManager metod rozhraní, stejně jako Query API rozhraní, metody, lze nalézt v Enterprise JavaBeans 3.0 Specifikace: Java Persistence API dokument, který je součástí JSR-220.

Nyní, když máte hrubou představu o tom, jak můžete vytvořit a poté vydat dotaz JPQL, možná budete chtít vidět některé praktické příklady. Následující fragment kódu je převzat z servlet je doGet metoda, která využívá JPQL dotazu pro získání informace o všechny zákazníky uloženy v podkladové relační tabulky spojené se Zákazníkem subjektu, zadaný v dotazu.

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

zvláštní význam zde jsou createQuery metoda EntityManager stupně a getResultList metoda Dotazu instance. Na EntityManager je createQuery se používá k vytvoření Dotazu instance, jejichž getResultList metody se pak používá k provedení JPQL dotazu předány createQuery jako parametr. Jak asi tušíte, metoda getResultList dotazu vrací výsledek dotazu jako seznam, jehož prvky jsou v tomto konkrétním příkladu obsazeny tak, aby zadaly zákazníka.

pokud potřebujete načíst jeden výsledek, rozhraní API dotazu nabízí metodu getSingleResult, jak je ukázáno v následujícím příkladu. Všimněte si však, že použití getSingleResult způsobí výjimku, pokud získáte více výsledků zpět.

také tento příklad ilustruje použití metody setParameter dotazu, pomocí které můžete svázat argument s parametrem dotazu. Pomocí setparametru můžete svázat pojmenované i poziční parametry. Zde však vázáte pojmenovaný parametr.

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

je zajímavé poznamenat, že použití příkazu SELECT jpql není jediným způsobem, jak jít, pokud jde o načtení instance jedné entity. Alternativně můžete použít metodu hledání EntityManager, která vám umožní načíst jednu instanci entity na základě id entity předaného jako parametr.

v některých situacích může být nutné načíst pouze některé informace z instance nebo instancí cílové entity a definovat dotaz JPQL proti určitému poli nebo polím entity. To je to, co výše uvedený fragment kódu by to vypadalo, pokud potřebujete načíst pouze hodnotu cust_name pole Zákazníka subjekt stupně dotazovány zde:

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

Podobně, chcete-li získat celý seznam zákazníků, můžete použít následující kód:

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

Otočil se zpátky k SQL, možná si vzpomenete, že seznam select SQL dotaz může být složen z více polí z tabulky či tabulek uvedené v klauzuli FROM. V JPQL, můžete také použít složený Seznam select, výběr dat pouze z polí entity zájmu. V takovém případě však musíte vytvořit třídu, do které odevzdáte výsledek dotazu. V následující části uvidíte příklad dotazu jpql join, jehož seznam select se skládá z polí odvozených od více než jedné entity.

definování Jpql spojení

stejně jako SQL, JPQL umožňuje definovat join dotazy. V SQL však obvykle definujete spojení, které kombinuje záznamy ze dvou nebo více tabulek a / nebo pohledů, včetně pouze požadovaných polí z těchto tabulek a pohledů v seznamu select dotazu join. Naproti tomu Seznam select dotazu jpql join obvykle obsahuje jednu entitu nebo dokonce jedno pole entity. Důvodem je povaha technologie JPA. Jakmile získáte instanci entity, můžete přejít na související instance pomocí odpovídajících metod getteru. Díky tomuto přístupu není nutné definovat dotaz, který vrátí všechny související instance entity najednou.

chcete-li například získat informace o objednávkách, spolu s jejich řádkových položek v SQL, budete muset definovat připojit k dotazu na obou purchaseOrders a orderLineItems tabulky, určující pole z obou tabulek v seznamu select dotazu. Při použití JPQL však můžete definovat dotaz pouze nad entitou PurchaseOrder a poté podle potřeby přejít na odpovídající instance OrderLineItem pomocí metody Getorderlineitems PurchaseOrder. V tomto příkladu můžete chtít definovat dotaz JPQL nad entitami PurchaseOrder a OrderLineItem pouze v případě, že potřebujete filtrovat načtené instance PurchaseOrder na základě podmínky nebo podmínek aplikovaných na OrderLineItem.

následující úryvek ukazuje příklad dotazu jpql join v akci. Chcete-li lépe porozumět tomu, jak jsou zúčastněné subjekty vzájemně propojeny, můžete se vrátit zpět na obrázek 1 zobrazený v části ukázkové aplikace dříve v článku.

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

ve výše uvedeném příkladu použijete funkci MAX aggregate v klauzuli SELECT dotazu join k určení produktu s nejvyšší cenou, z těch, které byly dodány společností Tortuga Trading a byly objednány alespoň jednou.

běžnější situace je však, když potřebujete vypočítat, řekněme, celkovou cenu objednaných produktů, které byly dodány určitým dodavatelem. Zde se může hodit funkce SUM aggregate. V SQL, tak připojit dotaz může vypadat například takto:

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

Bohužel, funkce SUMA používaných v JPQL neumožňuje projít aritmetický výraz jako argument. V praxi to znamená, že nebudete moci předat p. cena * l. množství jako argument k součtu JPQL. Existují však způsoby, jak tento problém vyřešit. V následujícím příkladu, můžete definovat třídu LineItemSum, jejíž konstruktor je pak použit v seznamu select dotazu, přičemž p.cena a já.množství jako parametry. Co Konstruktor LineItemSum dělá, je vynásobit p. cena l. Množství, uložení výsledku do proměnné třídy rslt. Dále můžete iterovat seznam LineItemSum načtený dotazem, součet hodnot proměnné Rslt LineItemSum. Následující úryvek ukazuje, jak lze toto vše implementovat do kódu:

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

Mimo jiné, výše uvedený příklad ukazuje, jak můžete použít vlastní Java třídy, ne třídu entity, v JPQL dotazu vyberte seznam, který obsahuje pole odvozené od více než jednoho subjektu, casting výsledek dotazu do této třídy. Ve většině případů se však budete muset vypořádat s dotazy, které obdrží instanci nebo seznam instancí určité entity.

načtené instance entit a aktuální kontext Persistence

výsledky dotazu v příkladech článku byly dosud jednoduše vytištěny. V aplikacích v reálném světě však možná budete muset provést některé další operace s výsledky dotazu. Například, možná budete muset aktualizovat načtené instance a poté je přetrvávat zpět do databáze. To vyvolává otázku: jsou instance načteny dotazem JPQL připravené k dalšímu zpracování aplikací, nebo jsou vyžadovány některé další kroky, aby byly na to připraveny? Zejména by bylo zajímavé zjistit, v jakém stavu, pokud jde o současný kontext perzistence, jsou načtené instance entit.

pokud máte nějaké zkušenosti s Java Persistence, měli byste vědět, co je kontext persistence. Pro rekapitulaci, persistence kontext je sada instance entity spravované instanci EntityManager s tím spojené souvislosti. V předchozích příkladech jste použili metodu EntityManager createQuery k vytvoření instance dotazu pro spuštění dotazu JPQL. API EntityManager ve skutečnosti obsahuje více než dvacet metod pro správu životního cyklu instancí entit, řízení transakcí a vytváření instancí dotazu, jejichž metody se pak používají k provedení zadaného dotazu a načtení výsledku dotazu.

pokud jde o kontext persistence, instance entity může být v jednom z následujících čtyř stavů: nová, spravovaná, oddělená nebo odstraněná. Pomocí vhodné metody EntityManager můžete podle potřeby změnit stav určité instance entity. Je však zajímavé poznamenat, že při splachování do databáze jsou do databáze synchronizovány pouze instance ve spravovaném stavu. Abych byl přesný, entita, instance v odstraněné stavu jsou také synchronizovány, což znamená, že záznamy v databázi odpovídající instance jsou odstraněny z databáze.

naproti tomu instance v novém nebo odpojeném stavu nebudou synchronizovány s databází. Pokud například vytvoříte novou instanci PurchaseOrder a poté vyvoláte flush metodu EntityManager, další záznam se nezobrazí v tabulce purchaseOrders, do které je entita PurchaseOrder mapována. Důvodem je, že tato nová instance PurchaseOrder nebyla připojena k kontextu persistence. Zde je to, co kód může vypadat:

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

Chcete-li problém vyřešit, musíte před vyvoláním flush vyvolat metodu entitymanager persist pro novou instanci PurchaseOrder, jak je ukázáno v následujícím příkladu:

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

Případně, pokud jste nastavili kaskády možnost PŘETRVÁVAJÍ nebo VŠECHNY při definování vztahu s PurchaseOrder v Zákaznickém subjekt, můžete přidat nově vytvořené PurchaseOrder instance do seznamu příkazy spojené se zákazníkem stupně, výměna přetrvávají operace s následující:

  cust.getPurchaseOrders().add(ord); 

výše uvedená diskuse ohledně subjektu instance států nás vede k zajímavé otázce, zda účetní jednotka případech vyvolány JPQL dotazu se stal automaticky řízena, nebo budete muset postarat explicitně nastavit jejich stav, aby být spravovány. Podle specifikace JPA, bez ohledu na způsob načítání entit-ať už se jedná o metodu hledání EntityManager nebo dotaz-jsou automaticky připojeny k aktuálnímu kontextu persistence. To znamená, že instance entit načtené dotazem JPQL se automaticky spravují. Můžete například změnit hodnotu pole načtené instance a poté tuto změnu synchronizovat s databází vyvoláním flush metody EntityManager nebo odevzdáním aktuální transakce. Nemusíte se starat o stav instancí spojených s načtenými instancemi. Faktem je, že při prvním přístupu k přidružené instanci se automaticky spravuje. Zde je jednoduchý příklad, který ukazuje, jak to všechno funguje v praxi:

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

Všimněte si, že nemusíte vyvolat přetrvávají metoda pro vyvolány PurchaseOrder instance, ani pro jeho příbuzné OrderLineItem stupně je upraven zde. Navzdory tomu, že změny provedené na tu první položku v pořadí, bude přetrvával do databáze při spáchání transakce. K tomu dochází, protože jak načtené instance entit, tak jejich asociace jsou automaticky připojeny k aktuálnímu kontextu persistence. Jak již bylo zmíněno dříve, první se spravují, když jsou načteny, a ty jsou připojeny ke kontextu, když k nim přistupujete.

v některých situacích můžete chtít, aby asociace byly připojeny ke kontextu při provádění dotazu, spíše než při prvním přístupu. To je místo, kde FETCH spojit přijde vhod. Řekněme, že chcete získat všechny objednávky patřící určitému zákazníkovi po načtení této instance zákazníka. Tento přístup zaručuje, že se zabýváte zákaznickými objednávkami dostupnými v době provedení dotazu. Pokud, například, nový příkaz je přidán do jiného kontextu, a pak synchronizovány do databáze předtím, než jste poprvé přístup na seznam příkazů souvisejících se zákazníkem stupně vyvolány, nebudete vidět změny, dokud neaktualizujete státě zákazníka instance z databáze. V následujícím úryvku použijete dotaz join, který vrací instanci zákazníka, jejíž cust_id je 1, a načte instance PurchaseOrder spojené s načtenou instancí zákazníka.

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

není součástí explicitní výsledek dotazu, Objednací subjekt případy spojené se Zákazníkem stupně citováno zde jsou také vyvolány a připojí se k aktuální persistence kontext při spuštění dotazu.

využití nativních SQL dotazů

je zajímavé poznamenat, že při definování dotazů, které mají být provedeny pomocí API dotazu, nejste omezeni na JPQL. Možná vás překvapí, že API EntityManager nabízí metody pro vytváření instancí dotazu pro provádění nativních příkazů SQL. Nejdůležitější věc je pochopit, o nativní SQL dotazy vytvořené pomocí EntityManager metody je, že oni, stejně jako JPQL dotazy, návrat subjektu případech, spíše než databáze tabulka záznamů. Zde je jednoduchý příklad dynamického nativního dotazu SQL:

  ... 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 se stále vyvíjí a nemá mnoho z těchto důležitých funkcí dostupných v SQL. V sekci definování Jpql Joins dříve jste viděli příklad neúplnosti JPQL: museli jste udělat hodně práce sami, protože funkce JPQL SUM aggregate nemůže brát aritmetický výraz jako parametr. Naproti tomu funkce SUM SQL takové omezení nemá. Toto je tedy dobrý příklad toho, kde by nahrazení JPQL nativním SQL mohlo být efektivní. Následující kód ilustruje, jak byste mohli zjednodušit věci, v tomto konkrétním příkladu výběrem nativní SQL přes JPQL:

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

Mimo jiné, výše uvedený příklad ukazuje, že můžete svázat argumenty native query parametry. Zejména můžete vázat argumenty na poziční parametry stejným způsobem, jako byste se zabývali dotazem JPQL.

hlavní nevýhodou nativních dotazů je složitost vazby výsledků. V příkladu dotaz vytvoří jediný výsledek jednoduchého typu, čímž se tomuto problému vyhne. V praxi se však často musíte vypořádat s výsledkovou sadou komplexního typu. V tomto případě budete muset deklarovat entitu, na kterou můžete mapovat svůj nativní dotaz, nebo definovat komplexní sadu výsledků mapovanou na více entit nebo na směs entit a skalárních výsledků.

použití uložených procedur

další nevýhodou nativních dotazů je, že váš Java kód se stává přímo závislým na základní struktuře databáze. Měli byste upravit ten základní struktury, budete muset nastavit nativní dotazy se týkaly v vaše servlety a/nebo další součásti aplikace, nutnosti překompilovat a přesunout tyto součásti po tom. Chcete-li vyřešit tento problém, zatímco ještě používá nativní dotazy, můžete využít uložené procedury, pohybu složité dotazy SQL do programů uložen a vykonán uvnitř databáze, a pak volat tyto uložené programy namísto toho, aby přímé volání do podkladové tabulky. Co to v praxi znamená, že uložené procedury, může vám ušetřit jednání s podkladové tabulky přímo z dotazů, které jsou zakódovány v Java kódu. Výhodou tohoto přístupu je, že ve většině případů nebudete muset upravovat kód Java, abyste sledovali změny v základní struktuře databáze. Místo toho bude nutné opravit pouze uložené postupy.

Otočil se zpátky k příkladu diskutovali v předchozí části, můžete se přesunout na složitější dotaz join používá tam do uložené funkce, vytvořil takto:

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

To zjednodušuje nativní dotazu použít ve vašem kódu v jazyce Java a odstraňuje závislost z podkladové tabulky:

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

Závěr

Jak jste se dozvěděli v tomto článku, JPQL je mocný nástroj, pokud jde o přístup k relační data v rámci Java aplikací s využitím Java Persistence. S JPQL, na rozdíl od SQL/JDBC, můžete definovat dotazy přes JPA entity mapovány na podkladové databáze tabulky, spíše než dotazem těchto tabulkách přímo, tedy co do činění s vrstvou abstrakce, která skrývá databáze údaje z obchodní logiky vrstvy. Také jste se dozvěděli, že JPQL není jedinou možností, pokud jde o vytváření dotazů přes entity JPA—v některých situacích je použití nativních dotazů SQL pohodlnější.