Introduksjon til Jooq med våren

1. Oversikt

Denne artikkelen vil introdusere Jooq Object Oriented Querying - Jooq - og en enkel måte å sette den opp i samarbeid med Spring Framework.

De fleste Java-applikasjoner har en slags SQL-utholdenhet og får tilgang til det laget ved hjelp av verktøy på høyere nivå som JPA. Og selv om det er nyttig, trenger du i noen tilfeller virkelig et finere, mer nyansert verktøy for å komme til dataene dine eller for å utnytte alt den underliggende DB har å tilby.

Jooq unngår noen typiske ORM-mønstre og genererer kode som lar oss bygge typesafe spørsmål, og få full kontroll over den genererte SQL via et rent og kraftig flytende API.

Denne artikkelen fokuserer på Spring MVC. Vår artikkel Spring Boot Support for jOOQ beskriver hvordan du bruker jOOQ i Spring Boot.

2. Maven-avhengigheter

Følgende avhengigheter er nødvendige for å kjøre koden i denne opplæringen.

2.1. jOOQ

 org.jooq jooq 3.2.14 

2.2. Vår

Det er flere våravhengigheter som kreves for vårt eksempel; Men for å gjøre ting enkle, trenger vi bare å eksplisitt ta med to av dem i POM-filen:

 org.springframework spring-context 5.2.2.RELEASE org.springframework spring-jdbc 5.2.2.RELEASE 

2.3. Database

For å gjøre ting enkelt for vårt eksempel, bruker vi den innebygde H2-databasen:

 com.h2database h2 1.4.191 

3. Kodegenerering

3.1. Databasestruktur

La oss introdusere databasestrukturen vi skal jobbe med gjennom denne artikkelen. Anta at vi trenger å lage en database for et forlag for å lagre informasjon om bøkene og forfatterne de administrerer, der en forfatter kan skrive mange bøker og en bok kan være medskrevet av mange forfattere.

For å gjøre det enkelt, genererer vi bare tre tabeller: bok for bøker, forfatter for forfattere, og en annen tabell kalt forfatter_bok å representere det mange-for-mange forholdet mellom forfattere og bøker. De forfatter tabellen har tre kolonner: id, fornavn, og etternavn. De bok tabellen inneholder bare en tittel kolonnen og id primærnøkkel.

Følgende SQL-spørsmål, lagret i intro_schema.sql ressursfil, vil bli kjørt mot databasen vi allerede har satt opp før for å lage de nødvendige tabellene og fylle dem ut med eksempeldata:

DROP TABLE IF EXISTS author_book, author, book; OPPRETT TABELLforfatter (id INT IKKE NULL HOVEDNØKE, fornavn VARCHAR (50), etternavn VARCHAR (50) IKKE NULL); OPPRETT TABELLbok (id INT IKKE NULL PRIMÆR NØKKEL, tittel VARCHAR (100) IKKE NULL); OPPRETT TABELL author_book (author_id INT NOT NULL, book_id INT NOT NULL, PRIMARY KEY (author_id, book_id), CONSTRAINT fk_ab_author FOREIGN KEY (author_id) REFERENCES author (id) ON UPDATE CASCADE ON SLETE CASCADE, CONSTRAINT f_book (id)); INSERT I forfatterVERDIER (1, 'Kathy', 'Sierra'), (2, 'Bert', 'Bates'), (3, 'Bryan', 'Basham'); INSERT INTO book VALUES (1, 'Head First Java'), (2, 'Head First Servlets and JSP'), (3, 'OCA / OCP Java SE 7 Programmer'); INSERT IN author_book VERDIER (1, 1), (1, 3), (2, 1);

3.2. Egenskaper Maven Plugin

Vi vil bruke tre forskjellige Maven-plugins for å generere Jooq-koden. Den første av disse er Properties Maven-plugin.

Dette pluginet brukes til å lese konfigurasjonsdata fra en ressursfil. Det er ikke nødvendig siden dataene kan legges direkte til POM, men det er lurt å administrere egenskapene eksternt.

I denne delen vil vi definere egenskaper for databaseforbindelser, inkludert JDBC-driverklasse, database-URL, brukernavn og passord, i en fil som heter intro_config.properties. Eksternalisering av disse egenskapene gjør det enkelt å bytte database eller bare endre konfigurasjonsdataene.

De lese-prosjekt-egenskaper Målet med dette pluginet bør være bundet til en tidlig fase slik at konfigurasjonsdataene kan utarbeides for bruk av andre plugins. I dette tilfellet er det bundet til initialisere fase:

 org.codehaus.mojo egenskaper-maven-plugin 1.0.0 initialiser lese-prosjekt-egenskaper src / main / resources / intro_config.properties 

3.3. SQL Maven-plugin

SQL Maven-pluginet brukes til å utføre SQL-setninger for å opprette og fylle ut databasetabeller. Det vil gjøre bruk av egenskapene som er hentet fra intro_config.properties fil av Properties Maven-pluginet og ta SQL-setningene fra intro_schema.sql ressurs.

SQL Maven-plugin er konfigurert som nedenfor:

 org.codehaus.mojo sql-maven-plugin 1.5 initialiser utfør $ {db.driver} $ {db.url} $ {db.username} $ {db.password} src / main / resources / intro_schema.sql com.h2database h2 1.4.191 

Merk at dette pluginet må plasseres senere enn Properties Maven-pluginet i POM-filen, siden deres kjøringsmål begge er bundet til samme fase, og Maven vil utføre dem i den rekkefølgen de er oppført.

3.4. jOOQ Codegen Plugin

Jooq Codegen Plugin genererer Java-kode fra en databasetabellstruktur. Det er generere målet skal være bundet til generere kilder fase for å sikre riktig rekkefølge for utførelsen. Plugin-metadataene ser slik ut:

 org.jooq jooq-codegen-maven $ {org.jooq.version} genererer-kilder genererer $ {db.driver} $ {db.url} $ {db.username} $ {db.password} com.baeldung.jooq. introduksjon.db src / main / java 

3.5. Genererer kode

For å fullføre prosessen med kildekodegenerering, må vi kjøre Maven generere kilder fase. I Eclipse kan vi gjøre dette ved å høyreklikke på prosjektet og velge Løp så –>Maven genererer kilder. Etter at kommandoen er fullført, vil kildefiler som tilsvarer forfatter, bok, forfatter_bok tabeller (og flere andre for å støtte klasser) genereres.

La oss grave i tabellklasser for å se hva Jooq produserte. Hver klasse har et statisk felt med samme navn som klassen, bortsett fra at alle bokstaver i navnet er store. Følgende er kodebiter hentet fra definisjonene til de genererte klassene:

De Forfatter klasse:

offentlig klasse Forfatter utvider TableImpl {offentlig statisk slutt Forfatter FORfatter = ny forfatter (); // andre klassemedlemmer}

De Bok klasse:

public class Book utvider TableImpl {public static final Bok BOOK = ny bok (); // andre klassemedlemmer}

De Forfatterbok klasse:

public class AuthorBook utvider TableImpl {public static final AuthorBook AUTHOR_BOOK = new AuthorBook (); // andre klassemedlemmer}

Forekomster som de statiske feltene refererer til, vil tjene som dataadgangsobjekter for å representere de tilsvarende tabellene når du arbeider med andre lag i et prosjekt.

4. Vårkonfigurasjon

4.1. Oversettelse av jOOQ-unntak fra våren

For å gjøre unntak fra Jooq-utførelse i samsvar med vårstøtte for databasetilgang, må vi oversette dem til undertyper av DataAccessException klasse.

La oss definere en implementering av ExecuteListener grensesnitt for å konvertere unntak:

offentlig klasse ExceptionTranslator utvider DefaultExecuteListener {unntak for offentlig tomrom (ExecuteContext context) {SQLDialect dialect = context.configuration (). dialect (); SQLExceptionTranslator translator = ny SQLErrorCodeSQLExceptionTranslator (dialect.name ()); context.exception (oversetter .translate ("Få tilgang til database ved hjelp av Jooq", context.sql (), context.sqlException ())); }}

Denne klassen vil bli brukt av vårens applikasjonskontekst.

4.2. Konfigurerer våren

Denne delen vil gå gjennom trinn for å definere en PersistenceContext som inneholder metadata og bønner som skal brukes i vårapplikasjonssammenheng.

La oss komme i gang med å bruke nødvendige kommentarer til klassen:

  • @Konfigurasjon: Gjør klassen til å bli anerkjent som en beholder for bønner
  • @ComponentScan: Konfigurer skannedirektiver, inkludert verdi muligheten til å erklære en rekke pakkenavn for å søke etter komponenter. I denne opplæringen er pakken som skal søkes, den som genereres av Jooq Codegen Maven-plugin
  • @EnableTransactionManagement: Gjør det mulig for transaksjoner å bli administrert av Spring
  • @PropertySource: Angi stedene til eiendomsfilene som skal lastes inn. Verdien i denne artikkelen peker på filen som inneholder konfigurasjonsdata og dialekt av databasen, som tilfeldigvis er den samme filen som er nevnt i underavsnitt 4.1.
@Configuration @ComponentScan ({"com.baeldung.Jooq.introduction.db.public_.tables"}) @EnableTransactionManagement @PropertySource ("classpath: intro_config.properties") offentlig klasse PersistenceContext {// Andre erklæringer}

Bruk deretter en Miljø objekt for å få konfigurasjonsdataene, som deretter brukes til å konfigurere Datakilde bønne:

@Autowired private Environment miljø; @Bean public DataSource dataSource () {JdbcDataSource dataSource = new JdbcDataSource (); dataSource.setUrl (miljø.getRequiredProperty ("db.url")); dataSource.setUser (environment.getRequiredProperty ("db.username")); dataSource.setPassword (miljø.getRequiredProperty ("db.password"));
 returner datakilde; }

Nå definerer vi flere bønner for å jobbe med databasetilgangsoperasjoner:

@Bean offentlig TransactionAwareDataSourceProxy-transaksjonAwareDataSource () {returner ny TransactionAwareDataSourceProxy (dataSource ()); } @Bean public DataSourceTransactionManager transactionManager () {returner ny DataSourceTransactionManager (dataSource ()); } @Bean offentlig DataSourceConnectionProvider connectionProvider () {returner ny DataSourceConnectionProvider (transactionAwareDataSource ()); } @Bean offentlig ExceptionTranslator exceptionTransformer () {returner ny ExceptionTranslator (); } @Bean public DefaultDSLContext dsl () {return new DefaultDSLContext (configuration ()); }

Til slutt gir vi en Jooq Konfigurasjon implementering og erklære det som en vårbønne som skal brukes av DSLContext klasse:

@Bean offentlig DefaultConfiguration-konfigurasjon () {DefaultConfiguration JooqConfiguration = ny DefaultConfiguration (); jooqConfiguration.set (connectionProvider ()); jooqConfiguration.set (ny DefaultExecuteListenerProvider (exceptionTransformer ())); Streng sqlDialectName = environment.getRequiredProperty ("jooq.sql.dialect"); SQLDialect dialect = SQLDialect.valueOf (sqlDialectName); jooqConfiguration.set (dialekt); returner jooqConfiguration; }

5. Bruke jOOQ med våren

Denne delen demonstrerer bruken av Jooq i vanlige databasetilgangsspørsmål. Det er to tester, en for forpliktelse og en for tilbakestilling, for hver type "skriv" -operasjon, inkludert innsetting, oppdatering og sletting av data. Bruken av "lese" -operasjon illustreres når du velger data for å bekrefte "skriv" -spørsmålene.

Vi begynner med å erklære en automatisk kablet DSLContext objekt og forekomster av Jooq-genererte klasser som skal brukes av alle testmetoder:

@Autowired privat DSLContext dsl; Forfatterforfatter = Forfatter.AUTHOR; Bokbok = Bok.BOK; AuthorBook authorBook = AuthorBook.AUTHOR_BOOK;

5.1. Sette inn data

Det første trinnet er å sette inn data i tabeller:

dsl.insertInto (forfatter) .sett (forfatter.ID, 4) .sett (forfatter.FIRST_NAME, "Herbert") .sett (forfatter.LAST_NAME, "Schildt"). kjør (); dsl.insertInto (book) .set (book.ID, 4) .set (book.TITLE, "A Beginner's Guide") .execute (); dsl.insertInto (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 4) .execute ();

EN Å VELGE spørring for å hente ut data:

Resultat resultat = dsl .select (author.ID, author.LAST_NAME, DSL.count ()) .from (author) .join (authorBook) .on (author.ID.equal (authorBook.AUTHOR_ID)) .join (book). på (authorBook.BOOK_ID.equal (book.ID)) .groupBy (author.LAST_NAME) .fetch ();

Spørringen ovenfor gir følgende utdata:

+ ---- + --------- + ----- + | ID | LAST_NAME | antall | + ---- + --------- + ----- + | 1 | Sierra | 2 | | 2 | Bates | 1 | | 4 | Schildt | 1 | + ---- + --------- + ----- +

Resultatet blir bekreftet av Påstå API:

assertEquals (3, result.size ()); assertEquals ("Sierra", result.getValue (0, forfatter.LAST_NAME)); assertEquals (Integer.valueOf (2), result.getValue (0, DSL.count ())); assertEquals ("Schildt", result.getValue (2, forfatter.LAST_NAME)); assertEquals (Integer.valueOf (1), result.getValue (2, DSL.count ()));

Når en feil oppstår på grunn av en ugyldig spørring, kastes et unntak og transaksjonen ruller tilbake. I det følgende eksemplet, SETT INN spørring bryter med en begrensning for fremmed nøkkel, noe som resulterer i et unntak

@Test (forventet = DataAccessException.class) offentlig ugyldig givenInvalidData_whenInserting_thenFail () {dsl.insertInto (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 5) .execute (); }

5.2. Oppdaterer data

La oss nå oppdatere eksisterende data:

dsl.update (forfatter) .set (forfatter.LAST_NAME, "Baeldung") .where (author.ID.equal (3)) .execute (); dsl.update (book) .set (book.TITLE, "Building your REST API with Spring") .where (book.ID.equal (3)) .execute (); dsl.insertInto (authorBook) .set (authorBook.AUTHOR_ID, 3) .set (authorBook.BOOK_ID, 3) .execute ();

Få de nødvendige dataene:

Resultat resultat = dsl .select (author.ID, author.LAST_NAME, book.TITLE) .fra (author) .join (authorBook) .on (author.ID.equal (authorBook.AUTHOR_ID)) .join (book) .on ( authorBook.BOOK_ID.equal (book.ID)) .where (author.ID.equal (3)) .fetch ();

Resultatet skal være:

+ ---- + --------- + ---------------------------------- + | ID | LAST_NAME | TITEL | + ---- + --------- + ---------------------------------- + | 3 | Baeldung | Bygg din REST API med Spring | + ---- + --------- + ---------------------------------- +

Følgende test vil verifisere at Jooq fungerte som forventet:

assertEquals (1, result.size ()); assertEquals (Integer.valueOf (3), result.getValue (0, author.ID)); assertEquals ("Baeldung", result.getValue (0, forfatter.LAST_NAME)); assertEquals ("Bygge din REST API med Spring", result.getValue (0, book.TITLE));

I tilfelle en feil, kastes et unntak og transaksjonen ruller tilbake, noe vi bekrefter med en test:

@Test (forventet = DataAccessException.class) offentlig ugyldig givenInvalidData_whenUpdating_thenFail () {dsl.update (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 5) .execute (); }

5.3. Slette data

Følgende metode sletter noen data:

dsl.delete (forfatter) .where (forfatter.ID.lt (3)). utføre ();

Her er spørsmålet om å lese den berørte tabellen:

Resultat resultat = dsl .select (author.ID, author.FIRST_NAME, author.LAST_NAME) .fra (author) .fetch ();

Søkeutgangen:

+ ---- + ---------- + --------- + | ID | FIRST_NAME | LAST_NAME | + ---- + ---------- + --------- + | 3 | Bryan | Basham | + ---- + ---------- + --------- +

Følgende test verifiserer slettingen:

assertEquals (1, result.size ()); assertEquals ("Bryan", result.getValue (0, forfatter.FIRST_NAME)); assertEquals ("Basham", result.getValue (0, forfatter.LAST_NAME));

På den annen side, hvis et spørsmål er ugyldig, vil det kaste et unntak og transaksjonen ruller tilbake. Følgende test vil bevise at:

@Test (forventet = DataAccessException.class) offentlig tomrom gittInvalidData_whenDeleting_thenFail () {dsl.delete (bok) .where (book.ID.equal (1)) .execute (); }

6. Konklusjon

Denne opplæringen introduserte det grunnleggende om Jooq, et Java-bibliotek for arbeid med databaser. Det dekket trinnene for å generere kildekode fra en databasestruktur og hvordan du kan samhandle med den databasen ved hjelp av de nylig opprettet klassene.

Implementeringen av alle disse eksemplene og kodebiter kan bli funnet i et GitHub-prosjekt.


$config[zx-auto] not found$config[zx-overlay] not found