JPA-entiteettejä jpql: n ja natiivin SQL: n kanssa
näytesovellus
artikkelissa käsitellyt koodinpätkät on otettu artikkelin liitteenä olevassa näytesovelluksessa käytetyistä Java-lähdekooditiedostoista. Tarkastellessasi näytearkistoa saatat huomata, että tämä on yksinkertainen verkkosovellus, joka perustuu Java Servlet-ja Java Persistence API-teknologioihin. Yksinkertaisuuden vuoksi se ei käytä yrityspapuja, vaan antaa jpql-kyselyjä suoraan servleteistä. Se ei tarkoita, kuitenkin, että et voi hyödyntää jpql kyselyt käsitellään tässä enterprise beans-voit määritellä jpql kyselyt tahansa Java EE komponentit.
kuvassa 1 esitetään otosyksiköiden rakenne. Kuten näette, se sisältää joukon toisiinsa liittyviä kokonaisuuksia, joilla on erityyppisiä suhteita. Tällainen haarautuva rakenne tarvitaan havainnollistamaan jpql join-kyselyjen käyttöä, jota käsitellään Määrittelevässä jpql Joins-osiossa myöhemmin artikkelissa.
Kuva 1 Esimerkkisovelluksessa käytettyjen yksiköiden väliset suhteet
saadaksesi yksityiskohtaisen ohjeen näytesovelluksen perustamiseen ja käynnistämiseen, voit katsoa Readme-palvelusta.txt-tiedosto näytearkiston juurihakemistossa.
Jpql: n käyttäminen Java EE-sovelluksissa
jos sinulla on käytännön kokemusta tietokannoista, olet todennäköisesti jo saanut jalkasi märäksi SQL: llä, vakiotyökalulla, joka tarjoaa joukon lausuntoja relaatiotietokantojen tietojen saamiseksi ja manipuloimiseksi. Itse asiassa JPQL: n ja SQL: n välillä on paljon yhtäläisyyksiä. Molempia käytetään tietokantatietojen käyttämiseen ja manipulointiin pitkällä aikavälillä. Ja molemmat käyttävät ei-prosessoituja lausuntoja-komentoja, jotka tunnistaa erityinen tulkki. Lisäksi jpql muistuttaa syntaksiltaan SQL: ää.
jpql: n ja SQL: n suurin ero on siinä, että edellinen käsittelee JPA-entiteettejä, kun taas jälkimmäinen käsittelee suoraan relaatiodataa. Java-kehittäjänä saatat myös olla kiinnostunut oppimaan, että jpql: n käyttäminen, Toisin kuin SQL/JDBC, poistaa tarpeen käyttää JDBC API: ta Java—koodistasi-säiliö tekee kaiken tämän työn puolestasi kulissien takana.
JPQL: n avulla voit määritellä kyselyt käyttämällä jotakin seuraavista kolmesta lauseesta: valitse, Päivitä tai poista. On mielenkiintoista huomata, että EntityManager API käyttöliittymä tarjoaa menetelmiä, joita voidaan käyttää myös suorittaa hakea, päivittää ja poistaa toimintoja yli entitions. Erityisesti, nämä ovat Etsi, yhdistää, ja poista menetelmiä. Näiden menetelmien käyttö on kuitenkin tyypillisesti rajattu yhteen kokonaisuuteen, paitsi jos kaskadointi tulee voimaan, tietenkin. Sen sijaan JPQL—lausekkeissa ei ole tällaista rajoitusta-voit määritellä massapäivityksen ja poiston toiminnot entiteettijoukoille ja määritellä kyselyt, jotka palauttavat entiteettijoukot.
Jpql-kyselyn antamiseksi Java-koodisi sisältä, sinun on käytettävä sopivia menetelmiä EntityManager API: sta ja kyselyn API: sta, suorittaen seuraavat yleiset vaiheet:
- 1. Hanki entitymanager-instanssi injektiona tai eksplisiittisesti EntityManagerFactory-instanssin kautta.
- 2. Luo kyselyesimerkki vetoamalla sopivaan Entitymanagerin menetelmään, kuten createQuery.
- 3. Aseta kyselyparametri tai mahdolliset parametrit käyttäen sopivan kyselyn setparametrimenetelmää.
- 4. Aseta tarvittaessa noudettavien esiintymien enimmäismäärä ja/tai määritä haettavan ensimmäisen oikeusasteen sijainti käyttäen setmaxresultsin ja / tai setFirstResult-kyselyn menetelmiä.
- 5. Aseta tarvittaessa toimittajakohtainen vihje setHint-kyselyn menetelmällä.
- 6. Aseta tarvittaessa kyselyn suorituksen huuhtelutila setFlushMode-kyselyn menetelmällä, ohittaen entiteettihallinnan huuhtelutilan.
- 7. Suorita kysely sopivan kyselyn menetelmällä: getSingleResult tai getResultList. Jos kyseessä on päivitys-tai poistotoiminto, sinun on kuitenkin käytettävä executeUpdate-menetelmää, joka palauttaa päivitettyjen tai poistettujen entiteettiesiintymien määrän.
täysi luettelo EntityManager-rajapintamenetelmistä sekä kyselyn API-rajapintamenetelmistä löytyy Enterprise JavaBeans 3.0 Specification: Java Persistence API-dokumentista, joka on osa JSR-220: tä.
nyt kun sinulla on karkea käsitys siitä, miten voit luoda ja sitten antaa jpql-kyselyn, haluat ehkä nähdä joitakin käytännön esimerkkejä. Seuraava koodifragmentti on otettu servletin doGet – menetelmästä, joka käyttää JPQL-kyselyä saadakseen tietoa kaikista asiakkaista, jotka on tallennettu taustalla olevaan relaatiotaulukkoon, joka liittyy kyselyssä määriteltyyn Asiakasyksikköön.
... @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("----------------" + ""); } ...
erityisen kiinnostavia tässä ovat EntityManager-ilmentymän createQuery-menetelmä ja Kyselyesitteen getResultList-menetelmä. Entitymanagerin createqueryn avulla luodaan Kyselyesite, jonka getresultlist-menetelmää käytetään sitten createqueryyn parametrina siirretyn jpql-kyselyn suorittamiseen. Kuten arvata saattaa, kyselyn getResultList-menetelmä palauttaa kyselyn tuloksen listana, jonka elementit tässä nimenomaisessa esimerkissä on valettu kirjoittamaan asiakas.
jos haluat hakea yhden tuloksen, kyselyn API-rajapinta tarjoaa getSingleResult-menetelmän, kuten seuraavassa esimerkissä on esitetty. Huomaa kuitenkin, että getsingleresultin käyttäminen aiheuttaa poikkeuksen, jos saat useita tuloksia takaisin.
myös tämä esimerkki havainnollistaa kyselyn setparametrimenetelmän käyttöä, jonka avulla voidaan sitoa argumentti kyselyparametriin. Setparametrin avulla voit sitoa sekä nimettyjä että sijaintiparametreja. Tässä kuitenkin sidotaan nimetty parametri.
... 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()+""); ...
on mielenkiintoista huomata, että käyttämällä SELECT JPQL lauseke ei ole ainoa tapa mennä, kun se tulee hakemaan yhden kokonaisuuden ilmentymän. Vaihtoehtoisesti voit käyttää Entitymanagerin etsi-menetelmää, jonka avulla voit hakea yhden entiteetin ilmentymän parametrina annetun entiteetin id: n perusteella.
joissakin tilanteissa voi olla tarpeen hakea vain joitakin tietoja kohdeyhteisön ilmentymästä tai instansseista, jolloin määritellään JPQL-kysely tiettyä entiteettikenttää tai-kenttiä vastaan. Tältä näyttää yllä oleva pätkä, jos haluat hakea vain tässä kysellyn asiakasyksikön ilmentymän cust_name-kentän arvon:
... 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+""); ...
samoin, saada koko listan asiakkaiden nimet, voit käyttää seuraavaa koodia:
... 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/>"); } ...
palatessasi SQL: ään saatat muistaa, että SQL-kyselyn select-luettelo voi koostua useista kentistä FROM-lausekkeessa määritellystä taulukosta tai taulukoista. Jpql: ssä voit myös käyttää koostettua select-luetteloa, jossa tiedot valitaan vain kiinnostavista entiteettikentistä. Siinä tapauksessa sinun on kuitenkin luotava luokka, johon heität kyselyn tuloksen. Seuraavassa osiossa näet esimerkin jpql join-kyselystä, jonka select list koostuu useammasta kuin yhdestä entiteetistä johdetuista kentistä.
Jpql: n määrittely liittyy
SQL: n tavoin jpql: n avulla voit määritellä liittymiskyselyt. SQL-kielessä määritellään kuitenkin normaalisti join, joka yhdistää tietueet kahdesta tai useammasta taulukosta ja/tai näkymästä, mukaan lukien vain vaaditut kentät niistä taulukoista ja näkymistä join-kyselyn valitussa luettelossa. Sen sijaan jpql-liittymäkyselyn select-lista sisältää tyypillisesti yhden entiteetin tai jopa yhden entiteetin kentän. Syynä tähän on yhteisen parlamentaarisen edustajakokouksen teknologian luonne. Kun olet saanut entity instance, voit siirtyä sen liittyvät esiintymät käyttäen vastaavia getter menetelmiä. Tämä lähestymistapa tekee tarpeettomaksi määrittää kyselyn, joka palauttaa kaikki liittyvät entiteettiyksityiskohteet kerralla.
esimerkiksi saadaksesi tietoa tilauksista ja niiden riviesityksistä SQL: ssä, sinun on määriteltävä liittymiskysely sekä ostotilausten että tilauslinjojen taulukoissa ja eriteltävä kentät kummastakin taulukosta kyselyn valitussa luettelossa. Kun käytät JPQL, kuitenkin, saatat määritellä kyselyn vain Yli PurchaseOrder entity, ja sitten siirtyä vastaaviin OrderLineItem tapauksissa käyttäen PurchaseOrder ’ s getOrderLineItems menetelmä tarpeen. Tässä esimerkissä haluat ehkä määritellä Jpql-kyselyn ostotilauksen ja Tilauslineitem-yksiköiden yli vain, jos sinun täytyy suodattaa noudetut Tilausilmiöt tilauslineitem-ehdon tai-ehtojen perusteella.
Seuraava pätkä näyttää esimerkin jpql join query-kyselystä toiminnassa. Jotta ymmärtäisit paremmin, miten kyseiset yhteisöt liittyvät toisiinsa, voit kääntyä takaisin kuvioon 1, joka on esitetty artikkelin aiemmin Näytesovellusosiossa.
... 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/>"); ...
yllä olevassa esimerkissä voit käyttää yhdistämiskyselyn valitse-lausekkeessa olevaa Max-aggregaattifunktiota määrittääksesi korkeimman hinnan tuotteen, jonka Tortuga Trading on toimittanut ja joka on tilattu vähintään kerran.
yleisempi tilanne on kuitenkin se, kun pitää laskea vaikkapa tietyn toimittajan toimittamien tilattujen tuotteiden kokonaishinta. Tämä on, jos summa yhteenlaskettu toiminto voi tulla kätevä. SQL: ssä tällainen liittymiskysely saattaa näyttää tältä:
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';
valitettavasti JPQL: ssä käytetty SUMMAFUNKTIO ei salli aritmeettisen lausekkeen läpäisemistä argumenttina. Tämä tarkoittaa käytännössä sitä, että et voi siirtää P.hinta*l.määrä argumenttina JPQL: n summa. On kuitenkin olemassa tapoja kiertää tätä asiaa. Seuraavassa esimerkissä määritellään Luokka LineItemSum, jonka konstruktoria käytetään kyselyn select-luettelossa ottaen parametreiksi p.hinta ja L.määrä. Mitä LineItemSum constructor tekee on kertoa p. hinta l. määrä, säästää tulos sen rslt luokan muuttuja. Seuraavaksi voit iteroida kyselyllä haetun LineItemSum-listan kautta, summaamalla Lineitemsumin rslt-muuttujan arvot. Seuraava pätkä näyttää, miten kaikki tämä voidaan toteuttaa koodina:
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/>"); } }
yllä oleva esimerkki havainnollistaa muun muassa, miten voit käyttää jpql-kyselyn select-luettelossa mukautettua Java-luokkaa, ei entiteettiluokkaa, joka sisältää useammasta kuin yhdestä entiteetistä johdetut kentät ja valaa kyselyn tuloksen kyseiseen luokkaan. Useimmissa tapauksissa, kuitenkin, joudut käsittelemään kyselyitä, jotka saavat esimerkiksi tai luettelon esiintymiä tietyn yksikön.
Retrieved Entity Instances and the Current Persistence Context
artikkeliesimerkkien kyselytulokset tähän mennessä on yksinkertaisesti tulostettu. Reaalimaailman sovelluksissa, kuitenkin, saatat joutua suorittamaan joitakin lisätoimia kyselyn tuloksia. Esimerkiksi, saatat joutua päivittämään haetut esiintymät ja sitten säilyttää ne takaisin tietokantaan. Tämä herättää kysymyksen: ovatko jpql-kyselyn hakemat esiintymät valmiita sovelluksen jatkokäsittelyyn, tai tarvitaan joitakin lisätoimenpiteitä, jotta ne ovat valmiita tähän? Erityisesti olisi mielenkiintoista tietää, missä tilassa, koskien nykyistä pysyvyyttä yhteydessä, haetut entiteetti esiintymät ovat.
jos sinulla on jonkin verran kokemusta Javan pysyvyydestä, sinun pitäisi tietää, mikä pysyvyyskonteksti on. Kertaukseksi pysyvyyskonteksti on joukko entiteettiasteikkoja, joita hallinnoi kyseiseen kontekstiin liittyvä EntityManager-ilmentymä. Edellisissä esimerkeissä käytit Entitymanagerin createQuery-menetelmää luodaksesi kyselyesimerkin JPQL-kyselyn suorittamiseen. Itse asiassa EntityManager API sisältää yli kaksikymmentä tapaa hallita entiteetin esiintymien elinkaarta, hallita tapahtumia ja luoda kyselyesineitä, joiden menetelmiä käytetään määritetyn kyselyn suorittamiseen ja kyselyn tuloksen hakemiseen.
pysyvyyden osalta yhteisöesitys voi olla jossakin seuraavista neljästä tilasta: Uusi, hallittu, irrotettu tai poistettu. Käyttämällä sopivaa Entitymanagerin menetelmää, voit muuttaa tietyn entiteetin tilaa tarpeen mukaan. On kuitenkin mielenkiintoista huomata, että vain hallitun tilan esiintymät synkronoidaan tietokantaan, kun huuhtelu tietokantaan tapahtuu. Jos tarkkoja ollaan, poistetussa tilassa olevat entiteettiesiintymät ovat myös synkronoituja, eli kyseisiä ilmentymiä vastaavat tietokantatallenteet poistetaan tietokannasta.
sen sijaan uuden tai irrallisen tilan ilmentymiä ei synkronoida tietokantaan. Jos esimerkiksi luot uuden Ostotilausilmaisun ja sitten käytät Entitymanagerin huuhtelumenetelmää, tilaajataulukossa, johon Tilaajayhteisö on yhdistetty, ei ole toista tietuetta. Tämä johtuu siitä, että uutta ostotilausta ei ole liitetty pysyvyyteen. Tältä koodi voi näyttää:
... 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(); ...
voit korjata ongelman, sinun täytyy vedota EntityManager ’ s itsepintainen menetelmä uuden Ostajatilauksen tapauksessa ennen vedota värin, kuten seuraavassa esimerkissä on esitetty:
... 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(); ...
Vaihtoehtoisesti, jos olet asettanut cascade-vaihtoehdon jatkuvaksi tai kokonaan määrittäessäsi suhdetta Asiakasyhteisössä olevaan ostotilaukseen, voit lisätä äskettäin luodun Ostotilausilmaisimen asiakkaan instanssiin liittyvien tilausten luetteloon ja korvata jatkuvan toiminnon seuraavalla:
cust.getPurchaseOrders().add(ord);
yllä oleva keskustelu entiteetin ilmentymätiloista johtaa mielenkiintoiseen kysymykseen siitä, tuleeko jpql-kyselyllä haetuista entiteettiyksilöistä automaattisesti hallittavia, vai onko sinun huolehdittava siitä, että niiden tila asetetaan erikseen hallittavaksi. Yhteisen parlamentaarisen edustajakokouksen määrittelyn mukaan riippumatta tavasta, jolla noudat entiteettejä—olipa kyseessä Entitymanagerin löytömenetelmä tai kysely—ne liitetään automaattisesti nykyiseen pysyvyyskontekstiin. Tämä tarkoittaa sitä, että jpql-kyselyllä haetut entiteettiyksityiskohdat tulevat automaattisesti hallittaviksi. Voit esimerkiksi muuttaa noudetun ilmentymän kentän arvoa ja synkronoida sen tietokantaan kutsumalla Entitymanagerin huuhtelumenetelmää tai toimittamalla nykyisen tapahtuman. Sinun ei tarvitse huolehtia tilasta tapauksissa liittyvät haettu esiintymiä joko. Tosiasia on, että ensimmäistä kertaa käytät liittyvä instanssi se tulee hallitaan automaattisesti. Tässä on yksinkertainen esimerkki osoittaa, miten kaikki tämä toimii käytännössä:
... 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/>"); ...
huomaa, että et vedota jatkuva menetelmä haettu PurchaseOrder instance, eikä siihen liittyvä OrderLineItem instance muokataan täällä. Tästä huolimatta tilauksen ensimmäiseen rivikohtaan tehdyt muutokset pysyvät tietokannassa tapahtuman toteutuessa. Tämä johtuu siitä, että sekä haetut entiteettiasteikot että niiden assosiaatiot liitetään automaattisesti nykyiseen pysyvyyskontekstiin. Kuten aiemmin mainittiin, edellisiä hallitaan, kun ne haetaan, ja jälkimmäiset liitetään kontekstiin, kun käytät niitä.
joissakin tilanteissa haluat ehkä, että assosiaatiot liitetään asiayhteyteen kyselyn suorittamisen yhteydessä, eikä ensimmäisen käytön yhteydessä. Tässä kohtaa NOUTOLIITTYMÄ tulee tarpeeseen. Sano, haluat saada kaikki tilaukset kuuluvat tietylle asiakkaalle, kun haetaan, että asiakkaan tapauksessa. Tämä lähestymistapa takaa, että käsittelet asiakastilauksia, jotka ovat käytettävissä kyselyn suoritushetkellä. Jos esimerkiksi uusi tilaus lisätään toiseen kontekstiin ja synkronoidaan tietokantaan ennen kuin käytät ensimmäistä kertaa tilausluetteloa, joka liittyy haettuun asiakkaan ilmentymään, et näe tätä muutosta ennen kuin päivität asiakkaan ilmentymän tilan tietokannasta. Seuraavassa pätkässä käytät join-kyselyä, joka palauttaa Asiakkaan ilmentymän, jonka cust_id on 1, ja hakee ostotilauksen ilmentymät, jotka liittyvät haettavaan asiakkaan ilmentymään.
... 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(); ...
koska ostotilausyksikkö ei ole osa eksplisiittistä kyselytulosta, tässä haettuun asiakkaan ilmentymään liittyvät Ostotilausyhteisö-ilmentymät myös noudetaan ja liitetään nykyiseen pysyvyyskontekstiin kyselyn suorittamisen yhteydessä.
natiivien SQL-kyselyjen hyödyntäminen
on mielenkiintoista huomata, että et ole rajoittunut JPQL: ään määriteltäessä kyselyjä, jotka sitten suoritetaan kyselyn API: lla. Saatat yllättyä kuullessasi, että EntityManager API tarjoaa menetelmiä, joilla voidaan luoda kyselytapauksia natiivien SQL-lauseiden suorittamiseen. Tärkeintä ymmärtää noin native SQL kyselyt luotu EntityManager menetelmiä on, että ne, kuten JPQL kyselyt, palata entiteetti esiintymiä, eikä tietokanta taulukko tietueita. Tässä on yksinkertainen esimerkki dynaamisesta SQL-kyselystä:
... 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 kehittyy edelleen, eikä sillä ole monia näistä tärkeistä ominaisuuksista saatavilla SQL: ssä. Määrittelevässä jpql liittyy aikaisempaan osioon, näit esimerkin JPQL: n epätäydellisyydestä: sinun piti tehdä paljon työtä itse, koska JPQL: n summa-aggregaattifunktio ei voi ottaa aritmeettista lauseketta parametriksi. Sen sijaan SQL: n SUMMAFUNKTIOLLA ei ole tällaista rajoitusta. Niin, tämä on hyvä esimerkki, jossa korvaa jpql natiivi SQL voisi olla tehokas. Seuraava koodi havainnollistaa, miten voit yksinkertaistaa asioita tässä nimenomaisessa esimerkissä valitsemalla natiivi SQL yli 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/>"); ...
yllä oleva esimerkki havainnollistaa muun muassa, että argumentteja voi sitoa natiivikyselyn parametreihin. Erityisesti voit sitoa argumentteja sijaintiparametreihin samalla tavalla kuin jos olisit tekemisissä JPQL-kyselyn kanssa.
natiivikyselyiden suurin haitta on tulosidonnan monimutkaisuus. Esimerkissä kysely tuottaa yhden yksinkertaisen tyypin tuloksen, jolloin tämä ongelma vältetään. Käytännössä joudutaan kuitenkin usein käsittelemään monitahoista tulosjoukkoa. Tässä tapauksessa sinun on ilmoitettava entiteetti, johon voit kartoittaa alkuperäisen kyselysi, tai määriteltävä monimutkainen tulosjoukko, joka on yhdistetty useisiin entiteetteihin tai kokonaisuuksien ja skalaaritulosten sekoitukseen.
tallennettujen menetelmien käyttäminen
toinen natiivikyselyiden haitta on se, että Java-koodisi tulee suoraan riippuvaiseksi taustalla olevasta tietokantarakenteesta. Jos muutat tätä taustalla olevaa rakennetta, sinun on säädettävä servleteissä ja/tai muissa sovelluskomponenteissa olevia natiivikyselyjä, jotka on käännettävä ja sijoitettava uudelleen sen jälkeen. Tämän ongelman kiertämiseksi, kun käytät edelleen natiivikyselyjä, saatat hyödyntää tallennettuja menettelyjä, siirtämällä monimutkaisia SQL-kyselyjä tietokantaan tallennettuihin ja suoritettuihin ohjelmiin ja sitten kutsumalla tallennettuja ohjelmia sen sijaan, että soittaisit suoraan taustalla oleviin taulukoihin. Tämä tarkoittaa käytännössä sitä, että tallennetut menettelyt voivat säästää vaivan käsitellä taustalla olevia taulukoita suoraan kyselyistä, jotka on koodattu kovalla Java-koodilla. Etuna tässä lähestymistavassa on, että useimmissa tapauksissa sinun ei tarvitse muokata Java-koodia seurata muutoksia taustalla tietokannan rakenne. Sen sijaan vain tallennetut menettelyt on vahvistettava.
kun palaat edellisessä osiossa käsiteltyyn esimerkkiin, voit siirtää siinä käytetyn monimutkaisen liittymiskyselyn tallennetuksi funktioksi, joka luodaan seuraavasti:
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; /
tämä yksinkertaistaa Java-koodissa käytettyä natiivikyselyä ja poistaa riippuvuuden taustalla olevista taulukoista:
... 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/>"); ...
päätelmä
kuten olet oppinut tässä artikkelissa, JPQL on tehokas työkalu, kun se tulee päästä relaatiotietoja sisällä Java sovelluksia hyödyntäen Java pysyvyys. Jpql: llä, toisin kuin SQL/JDBC: llä, määrittelet kyselyt yli JPA-entiteettien, jotka on yhdistetty taustalla oleviin tietokantataulukoihin, sen sijaan että kyselisit näitä taulukoita suoraan, jolloin kyseessä on abstraktiokerros, joka piilottaa tietokannan tiedot business logic-kerroksesta. Olet myös oppinut, että JPQL ei ole ainoa vaihtoehto, kun se tulee luoda kyselyitä yli JPA entities—joissakin tilanteissa käyttäen natiivi SQL kyselyt on helpompaa.