Introductie tot Jinq met Spring

1. Inleiding

Jinq biedt een intuïtieve en handige benadering voor het doorzoeken van databases in Java. In deze tutorial gaan we verkennen hoe u een Spring-project configureert om Jinq te gebruiken en enkele van de functies geïllustreerd met eenvoudige voorbeelden.

2. Maven afhankelijkheden

We moeten de Jinq-afhankelijkheid toevoegen in het pom.xml het dossier:

 org.jinq jinq-jpa 1.8.22 

Voor Spring voegen we de Spring ORM-afhankelijkheid toe aan het pom.xml het dossier:

 org.springframework spring-orm 5.2.5.RELEASE 

Ten slotte gebruiken we voor het testen een H2 in-memory database, dus laten we deze afhankelijkheid ook toevoegen, samen met spring-boot-starter-data-jpa naar het pom.xml-bestand:

 com.h2database h2 1.4.200 org.springframework.boot spring-boot-starter-data-jpa 2.2.6.RELEASE 

3. Jinq begrijpen

Jinq helpt ons om eenvoudigere en beter leesbare databasequery's te schrijven door een vloeiende API bloot te stellen die intern is gebaseerd op de Java Stream API.

Laten we een voorbeeld bekijken waarin we auto's op model filteren:

jinqDataProvider.streamAll (entityManager, Car.class) .where (c -> c.getModel (). equals (model)) .toList ();

Jinq vertaalt het bovenstaande codefragment op een efficiënte manier naar een SQL-query, dus de laatste vraag in dit voorbeeld zou zijn:

selecteer c. * van auto c waar c.model =?

Omdat we geen platte tekst gebruiken voor het schrijven van vragen en in plaats daarvan een typeveilige API gebruiken, is deze aanpak minder foutgevoelig.

Bovendien streeft Jinq ernaar om snellere ontwikkeling mogelijk te maken door algemene, gemakkelijk te lezen uitdrukkingen te gebruiken.

Niettemin heeft het enkele beperkingen in het aantal typen en bewerkingen dat we kunnen gebruiken, zoals we hierna zullen zien.

3.1. Beperkingen

Jinq ondersteunt alleen de basistypen in JPA en een concrete lijst met SQL-functies. Het werkt door de lambda-bewerkingen te vertalen naar een native SQL-query door alle objecten en methoden in een JPA-gegevenstype en een SQL-functie in kaart te brengen.

Daarom kunnen we niet verwachten dat de tool elk aangepast type of alle methoden van een type vertaalt.

3.2. Ondersteunde gegevenstypen

Laten we eens kijken naar de ondersteunde gegevenstypen en ondersteunde methoden:

  • Draadis gelijk aan (), vergelijk met() alleen methoden
  • Primitieve gegevenstypen - rekenkundige bewerkingen
  • Enums en aangepaste klassen - ondersteunt alleen == en! = bewerkingen
  • java.util.Collection - bevat ()
  • Datum API - is gelijk aan (), voordat(), na() alleen methoden

Opmerking: als we de conversie van een Java-object naar een database-object willen aanpassen, moeten we onze concrete implementatie van een AttributeConverter in Jinq.

4. Jinq integreren met de lente

Jinq heeft een EntityManager instantie om de persistentiecontext op te halen. In deze tutorial introduceren we een eenvoudige benadering met Spring om Jinq te laten werken met de EntityManager geleverd door Hibernate.

4.1. Repository-interface

Spring gebruikt het concept van repositories om entiteiten te beheren. Laten we naar onze kijken CarRepository interface waar we een methode hebben om een Auto voor een bepaald model:

openbare interface CarRepository {Optioneel findByModel (String-model); }

4.2. Abstracte basisopslagplaats

De volgende, we hebben een basisrepository nodig om alle Jinq-mogelijkheden te bieden:

openbare abstracte klasse BaseJinqRepositoryImpl {@Autowired privé JinqJPAStreamProvider jinqDataProvider; @PersistenceContext private EntityManager entityManager; beschermd abstract Klasse entityType (); openbare JPAJinqStream stream () {return streamOf (entityType ()); } beschermde JPAJinqStream streamOf (Class clazz) {return jinqDataProvider.streamAll (entityManager, clazz); }}

4.3. Implementeren van de repository

Nu hebben we voor Jinq alleen een EntityManager instantie en de entiteitstypeklasse.

Laten we eens kijken naar de Auto repository-implementatie met behulp van onze Jinq-basisrepository die we zojuist hebben gedefinieerd:

@Repository public class CarRepositoryImpl breidt BaseJinqRepositoryImpl uit en implementeert CarRepository {@Override public Optioneel findByModel (String model) {return stream () .where (c -> c.getModel (). Equals (model)) .findFirst (); } @Override beschermde Klasse entityType () {retourneer Car.class; }}

4.4. Bedrading van de JinqJPAStreamProvider

Om de JinqJPAStreamProvider bijvoorbeeld, we zullen voeg de configuratie van de Jinq-provider toe:

@Configuration openbare klasse JinqProviderConfiguration {@Bean @Autowired JinqJPAStreamProvider jinqProvider (EntityManagerFactory emf) {retourneer nieuwe JinqJPAStreamProvider (emf); }}

4.5. Configureren van de veerapplicatie

De laatste stap is om configureer onze Spring-applicatie met behulp van Hibernate en onze Jinq-configuratie. Zie onze application.properties bestand, waarin we een H2-instantie in het geheugen gebruiken als de database:

spring.datasource.url = jdbc: h2: ~ / jinq spring.datasource.username = sa spring.datasource.password = spring.jpa.hibernate.ddl-auto = create-drop

5. Querygids

Jinq biedt veel intuïtieve opties om de uiteindelijke SQL-query mee aan te passen selecteer, waar,sluit zich aan en meer. Merk op dat deze dezelfde beperkingen hebben die we hierboven al hebben geïntroduceerd.

5.1. Waar

De waar clausule maakt het mogelijk om meerdere filters toe te passen op een gegevensverzameling.

In het volgende voorbeeld willen we auto's filteren op model en beschrijving:

stream () .where (c -> c.getModel (). equals (model) && c.getDescription (). bevat (desc)) .toList ();

En dit is de SQL die Jinq vertaalt:

selecteer c.model, c.description van auto c waar c.model =? en zoek (?, c.description)> 0

5.2. Selecteer

Als we slechts een paar kolommen / velden uit de database willen halen, moeten we de selecteer clausule.

Om meerdere waarden in kaart te brengen, biedt Jinq een aantal Tuple klassen met maximaal acht waarden:

stream () .select (c -> nieuwe Tuple3 (c.getModel (), c.getYear (), c.getEngine ())) .toList ()

En de vertaalde SQL:

selecteer c.model, c.jaar, c. motor uit auto c

5.3. Doet mee

Jinq is in staat een-op-een en veel-op-een relaties op te lossen als de entiteiten correct zijn gekoppeld.

Als we bijvoorbeeld de entiteit van de fabrikant toevoegen aan Auto:

@Entity (name = "CAR") openbare klasse Auto {// ... @OneToOne @JoinColumn (naam = "naam") openbaar Fabrikant getManufacturer () {retour fabrikant; }}

En de Fabrikant entiteit met de lijst van Autos:

@Entity (name = "FABRIKANT") openbare klasse Fabrikant {// ... @OneToMany (mappedBy = "model") openbare lijst getCars () {retourauto's; }}

We kunnen nu het Fabrikant voor een bepaald model:

Optioneel fabrikant = stream () .where (c -> c.getModel (). Equals (model)) .select (c -> c.getManufacturer ()) .findFirst ();

Zoals verwacht, Jinq zal een SQL-clausule voor inner join gebruiken in dit scenario:

selecteer m.name, m.city van auto c inner join fabrikant m op c.name = m.name waar c.model =?

Voor het geval we meer controle nodig hebben over het toetreden clausules om meer complexe relaties over de entiteiten te implementeren, zoals een veel-op-veel-relatie, kunnen we de toetreden methode:

Lijst list = streamOf (Manufacturer.class) .join (m -> JinqStream.from (m.getCars ())) .toList ()

Ten slotte kunnen we een SQL-component left outer join gebruiken door de leftOuterJoin methode in plaats van de toetreden methode.

5.4. Aggregaties

Alle voorbeelden die we tot nu toe hebben geïntroduceerd, gebruiken ofwel de toList of de findFirst methoden - om het eindresultaat van onze zoekopdracht in Jinq te retourneren.

Naast deze methoden, we hebben ook toegang tot andere methoden om resultaten te verzamelen.

Laten we bijvoorbeeld de tellen methode om het totale aantal auto's voor een concreet model in onze database te krijgen:

long total = stream () .where (c -> c.getModel (). equals (model)) .count ()

En de laatste SQL gebruikt de tellen SQL-methode zoals verwacht:

selecteer count (c.model) van auto c waar c.model =?

Jinq biedt ook aggregatiemethoden zoals som, gemiddelde, min, max, en de mogelijkheid om verschillende aggregaties te combineren.

5.5. Paginering

Als we gegevens in batches willen lezen, kunnen we de limiet en overspringen methoden.

Laten we een voorbeeld bekijken waarin we de eerste 10 auto's willen overslaan en slechts 20 items willen krijgen:

stream () .skip (10) .limit (20) .toList ()

En de gegenereerde SQL is:

selecteer c. * van auto c limiet? compensatie?

6. Conclusie

Daar gaan we. In dit artikel hebben we een benadering gezien voor het opzetten van een Spring-applicatie met Jinq met Hibernate (minimaal).

We hebben ook kort ingegaan op de voordelen van Jinq en enkele van de belangrijkste kenmerken ervan.

Zoals altijd zijn de bronnen te vinden op GitHub.