Introductie tot Jooq met Spring

1. Overzicht

Dit artikel introduceert Jooq Object Oriented Querying - Jooq - en een eenvoudige manier om het op te zetten in samenwerking met het Spring Framework.

De meeste Java-applicaties hebben een soort SQL-persistentie en hebben toegang tot die laag met behulp van tools op een hoger niveau, zoals JPA. En hoewel dat handig is, heb je in sommige gevallen echt een fijnere, meer genuanceerde tool nodig om bij je gegevens te komen of om daadwerkelijk te profiteren van alles wat de onderliggende database te bieden heeft.

Jooq vermijdt een aantal typische ORM-patronen en genereert code waarmee we typeveilige queries kunnen bouwen en volledige controle krijgen over de gegenereerde SQL via een schone en krachtige, vloeiende API.

Dit artikel richt zich op Spring MVC. In ons artikel Spring Boot Support for jOOQ wordt beschreven hoe u jOOQ in Spring Boot kunt gebruiken.

2. Maven afhankelijkheden

De volgende afhankelijkheden zijn nodig om de code in deze zelfstudie uit te voeren.

2.1. jOOQ

 org.jooq jooq 3.2.14 

2.2. Voorjaar

Er zijn verschillende afhankelijkheden van de lente vereist voor ons voorbeeld; Om het echter eenvoudig te maken, hoeven we er slechts twee expliciet in het POM-bestand op te nemen:

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

2.3. Database

Om het ons voorbeeld gemakkelijk te maken, zullen we gebruik maken van de H2 embedded database:

 com.h2database h2 1.4.191 

3. Code genereren

3.1. Databasestructuur

Laten we de databasestructuur introduceren waarmee we in dit artikel zullen werken. Stel dat we een database moeten maken voor een uitgever om informatie op te slaan over de boeken en auteurs die ze beheren, waar een auteur veel boeken kan schrijven en een boek door veel auteurs kan worden geschreven.

Om het simpel te maken, zullen we slechts drie tabellen genereren: de boek voor boeken, schrijver voor auteurs, en een andere tafel genaamd author_book om de veel-op-veel-relatie tussen auteurs en boeken weer te geven. De schrijver tabel heeft drie kolommen: ID kaart, Voornaam, en achternaam. De boek tabel bevat alleen een titel kolom en de ID kaart hoofdsleutel.

De volgende SQL-query's, opgeslagen in het intro_schema.sql resource-bestand, wordt uitgevoerd op basis van de database die we al eerder hebben opgezet om de benodigde tabellen te maken en deze te vullen met voorbeeldgegevens:

DROP TABLE INDIEN BESTAAT author_book, author, book; CREATE TABLE auteur (id INT NOT NULL PRIMARY KEY, voornaam VARCHAR (50), achternaam VARCHAR (50) NOT NULL); CREATE TABLE book (id INT NOT NULL PRIMARY KEY, title VARCHAR (100) NOT NULL); CREATE TABLE author_book (author_id INT NOT NULL, book_id INT NOT NULL, PRIMARY KEY (author_id, book_id), CONSTRAINT fk_ab_author BUITENLANDSE SLEUTEL (author_id) REFERENTIES auteur (id) BIJ UPDATE CASCADE BIJ VERWIJDEREN CASCADE, CONSTRAINT fk_boek FORE-boek (ID kaart) ); INVOEGEN IN auteur WAARDEN (1, 'Kathy', 'Sierra'), (2, 'Bert', 'Bates'), (3, 'Bryan', 'Basham'); INVOEGEN IN boek WAARDEN (1, 'Head First Java'), (2, 'Head First Servlets and JSP'), (3, 'OCA / OCP Java SE 7 Programmer'); INSERT INTO author_book VALUES (1, 1), (1, 3), (2, 1);

3.2. Eigenschappen Maven Plugin

We zullen drie verschillende Maven-plug-ins gebruiken om de Jooq-code te genereren. De eerste hiervan is de Properties Maven-plug-in.

Deze plug-in wordt gebruikt om configuratiegegevens uit een bronbestand te lezen. Het is niet vereist omdat de gegevens rechtstreeks aan de POM kunnen worden toegevoegd, maar het is een goed idee om de eigenschappen extern te beheren.

In deze sectie zullen we eigenschappen voor databaseverbindingen definiëren, inclusief de JDBC-stuurprogrammaklasse, database-URL, gebruikersnaam en wachtwoord, in een bestand met de naam intro_config.properties. Door deze eigenschappen naar buiten te brengen, is het gemakkelijk om van database te wisselen of gewoon de configuratiegegevens te wijzigen.

De read-project-eigenschappen Het doel van deze plug-in moet worden gebonden aan een vroege fase, zodat de configuratiegegevens kunnen worden voorbereid voor gebruik door andere plug-ins. In dit geval is het gebonden aan de initialiseren fase:

 org.codehaus.mojo properties-maven-plugin 1.0.0 initialiseer read-project-properties src / main / resources / intro_config.properties 

3.3. SQL Maven-plug-in

De SQL Maven-plug-in wordt gebruikt om SQL-instructies uit te voeren om databasetabellen te maken en te vullen. Het maakt gebruik van de eigenschappen die zijn geëxtraheerd uit het intro_config.properties bestand door de Properties Maven-plug-in en neem de SQL-instructies van de intro_schema.sql bron.

De SQL Maven-plug-in is als volgt geconfigureerd:

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

Merk op dat deze plug-in later dan de Properties Maven-plug-in in het POM-bestand moet worden geplaatst, aangezien hun uitvoeringsdoelen beide aan dezelfde fase zijn gebonden en Maven ze zal uitvoeren in de volgorde waarin ze worden vermeld.

3.4. jOOQ Codegen-plug-in

De Jooq Codegen Plugin genereert Java-code vanuit een databasetabelstructuur. Haar genereren doel moet worden gebonden aan de genereren-bronnen fase om de juiste volgorde van uitvoering te garanderen. De metadata van de plug-in zien er als volgt uit:

 org.jooq jooq-codegen-maven $ {org.jooq.version} genereren-bronnen genereren $ {db.driver} $ {db.url} $ {db.username} $ {db.password} com.baeldung.jooq. introductie.db src / main / java 

3.5. Code genereren

Om het proces van het genereren van broncode af te ronden, moeten we de Maven draaien genereren-bronnen fase. In Eclipse kunnen we dit doen door met de rechtermuisknop op het project te klikken en te kiezen Rennen als –>Maven genereren-bronnen. Nadat de opdracht is voltooid, worden bronbestanden die overeenkomen met het schrijver, boek, author_book tabellen (en verschillende andere voor ondersteunende klassen) worden gegenereerd.

Laten we eens kijken naar de tafelklassen om te zien wat Jooq heeft geproduceerd. Elke klasse heeft een statisch veld met dezelfde naam als de klasse, behalve dat alle letters in de naam een ​​hoofdletter zijn. Hieronder volgen codefragmenten uit de definities van de gegenereerde klassen:

De Schrijver klasse:

public class Author breidt TableImpl uit {public static final Author AUTHOR = new Author (); // andere klasleden}

De Boek klasse:

public class Book breidt TableImpl uit {public static final Book BOOK = new Book (); // andere klasleden}

De AuthorBook klasse:

openbare klasse AuthorBook breidt TableImpl uit {openbare statische laatste AuthorBook AUTHOR_BOOK = nieuwe AuthorBook (); // andere klasleden}

De instanties waarnaar door die statische velden wordt verwezen, dienen als gegevenstoegangsobjecten om de corresponderende tabellen weer te geven bij het werken met andere lagen in een project.

4. Veerconfiguratie

4.1. Uitzonderingen van jOOQ vertalen naar de lente

Om uitzonderingen die worden gegenereerd door Jooq-uitvoering consistent te maken met Spring-ondersteuning voor databasetoegang, moeten we ze vertalen naar subtypen van de DataAccessException klasse.

Laten we een implementatie van het ExecuteListener interface om uitzonderingen te converteren:

openbare klasse ExceptionTranslator breidt DefaultExecuteListener uit {openbare ongeldige uitzondering (ExecuteContext-context) {SQLDialect dialect = context.configuration (). dialect (); SQLExceptionTranslator vertaler = nieuwe SQLErrorCodeSQLExceptionTranslator (dialect.name ()); context.exception (vertaler .translate ("Toegang tot database met Jooq", context.sql (), context.sqlException ())); }}

Deze klasse wordt gebruikt door de Spring-toepassingscontext.

4.2. Spring configureren

In dit gedeelte worden de stappen doorlopen om een PersistenceContext die metadata en bonen bevat die moeten worden gebruikt in de Spring-toepassingscontext.

Laten we beginnen door de nodige annotaties toe te passen op de klas:

  • @Configuratie: Zorg ervoor dat de klas wordt herkend als een container voor bonen
  • @BuienRadarNL: Scanrichtlijnen configureren, inclusief de waarde optie om een ​​reeks pakketnamen te declareren om naar componenten te zoeken. In deze tutorial is het pakket dat moet worden doorzocht het pakket dat is gegenereerd door de Jooq Codegen Maven-plug-in
  • @EnableTransactionManagement: Maak het mogelijk dat transacties worden beheerd door Spring
  • @PropertySource: Geef de locaties aan van de eigenschappenbestanden die moeten worden geladen. De waarde in dit artikel verwijst naar het bestand met configuratiegegevens en het dialect van de database, wat toevallig hetzelfde bestand is als vermeld in paragraaf 4.1.
@Configuration @ComponentScan ({"com.baeldung.Jooq.introduction.db.public_.tables"}) @EnableTransactionManagement @PropertySource ("classpath: intro_config.properties") public class PersistenceContext {// Andere declaraties}

Gebruik vervolgens een Milieu object om de configuratiegegevens op te halen, die vervolgens worden gebruikt om het Databron Boon:

@Autowired private omgeving omgeving; @Bean openbare DataSource dataSource () {JdbcDataSource dataSource = nieuwe JdbcDataSource (); dataSource.setUrl (environment.getRequiredProperty ("db.url")); dataSource.setUser (environment.getRequiredProperty ("db.username")); dataSource.setPassword (environment.getRequiredProperty ("db.password"));
 retourneer dataSource; }

Nu definiëren we verschillende bonen om te werken met databasetoegangsbewerkingen:

@Bean openbare TransactionAwareDataSourceProxy transactionAwareDataSource () {retourneer nieuwe TransactionAwareDataSourceProxy (dataSource ()); } @Bean openbare DataSourceTransactionManager transactionManager () {retourneer nieuwe DataSourceTransactionManager (dataSource ()); } @Bean openbare DataSourceConnectionProvider connectionProvider () {retourneer nieuwe DataSourceConnectionProvider (transactionAwareDataSource ()); } @Bean openbare ExceptionTranslator exceptionTransformer () {retourneer nieuwe ExceptionTranslator (); } @Bean public DefaultDSLContext dsl () {retourneer nieuwe DefaultDSLContext (configuration ()); }

Ten slotte bieden we een Jooq Configuratie implementatie en verklaar het als een Spring bean voor gebruik door de DSLContext klasse:

@Bean openbaar DefaultConfiguration-configuratie () {DefaultConfiguration JooqConfiguration = nieuwe DefaultConfiguration (); jooqConfiguration.set (connectionProvider ()); jooqConfiguration.set (nieuwe DefaultExecuteListenerProvider (exceptionTransformer ())); String sqlDialectName = environment.getRequiredProperty ("jooq.sql.dialect"); SQLDialect dialect = SQLDialect.valueOf (sqlDialectName); jooqConfiguration.set (dialect); retourneer jooqConfiguration; }

5. jOOQ gebruiken met veer

Deze sectie demonstreert het gebruik van Jooq in algemene databasetoegangsvragen. Er zijn twee tests, één voor vastleggen en één voor terugdraaien, voor elk type “schrijf” -bewerking, inclusief het invoegen, bijwerken en verwijderen van gegevens. Het gebruik van de "lees" -bewerking wordt geïllustreerd bij het selecteren van gegevens om de "schrijf" -query's te verifiëren.

We beginnen met het declareren van een auto-wired DSLContext object en instanties van door Jooq gegenereerde klassen die door alle testmethoden kunnen worden gebruikt:

@Autowired privé DSLContext dsl; Auteur auteur = Auteur.AUTHOR; Boek boek = Boek.BOOK; AuthorBook authorBook = AuthorBook.AUTHOR_BOOK;

5.1. Gegevens invoegen

De eerste stap is om gegevens in tabellen in te voegen:

dsl.insertInto (auteur) .set (author.ID, 4) .set (author.FIRST_NAME, "Herbert") .set (author.LAST_NAME, "Schildt") .execute (); dsl.insertInto (boek) .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 ();

EEN SELECTEER vraag om gegevens te extraheren:

Resultaat result = dsl .select (author.ID, author.LAST_NAME, DSL.count ()) .from (author) .join (authorBook) .on (author.ID.equal (authorBook.AUTHOR_ID)) .join (boek). op (authorBook.BOOK_ID.equal (book.ID)) .groupBy (author.LAST_NAME) .fetch ();

De bovenstaande query levert de volgende uitvoer op:

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

Het resultaat wordt bevestigd door de Beweren API:

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

Wanneer er een fout optreedt als gevolg van een ongeldige query, wordt er een uitzondering gegenereerd en wordt de transactie teruggedraaid. In het volgende voorbeeld is het INVOEGEN query schendt een beperking van een externe sleutel, wat resulteert in een uitzondering:

@Test (verwacht = DataAccessException.class) openbare ongeldige gegevenInvalidData_whenInserting_thenFail () {dsl.insertInto (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 5) .execute (); }

5.2. Gegevens bijwerken

Laten we nu de bestaande gegevens bijwerken:

dsl.update (auteur) .set (author.LAST_NAME, "Baeldung") .where (author.ID.equal (3)) .execute (); dsl.update (boek) .set (book.TITLE, "Bouwen aan uw REST API met Spring") .where (book.ID.equal (3)) .execute (); dsl.insertInto (authorBook) .set (authorBook.AUTHOR_ID, 3) .set (authorBook.BOOK_ID, 3) .execute ();

Verkrijg de nodige gegevens:

Resultaat result = dsl .select (author.ID, author.LAST_NAME, book.TITLE) .from (author) .join (authorBook) .on (author.ID.equal (authorBook.AUTHOR_ID)) .join (boek) .on ( authorBook.BOOK_ID.equal (book.ID)) .where (author.ID.equal (3)) .fetch ();

De output zou moeten zijn:

+ ---- + --------- + ---------------------------------- + | ID | LAST_NAME | TITLE | + ---- + --------- + ---------------------------------- + | 3 | Baeldung | Bouwen aan uw REST API met Spring | + ---- + --------- + ---------------------------------- +

De volgende test zal verifiëren dat Jooq werkte zoals verwacht:

assertEquals (1, result.size ()); assertEquals (Integer.valueOf (3), result.getValue (0, author.ID)); assertEquals ("Baeldung", result.getValue (0, author.LAST_NAME)); assertEquals ("Bouwen aan uw REST API met Spring", result.getValue (0, book.TITLE));

In het geval van een mislukking, wordt er een uitzondering gegenereerd en wordt de transactie teruggedraaid, wat we bevestigen met een test:

@Test (verwacht = DataAccessException.class) openbare ongeldige gegevenInvalidData_whenUpdating_thenFail () {dsl.update (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 5) .execute (); }

5.3. Gegevens verwijderen

Met de volgende methode worden enkele gegevens verwijderd:

dsl.delete (auteur) .where (author.ID.lt (3)) .execute ();

Hier is de vraag om de betreffende tabel te lezen:

Resultaat result = dsl .select (auteur.ID, auteur.FIRST_NAME, auteur.LAST_NAME) .from (auteur) .fetch ();

De query-uitvoer:

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

De volgende test verifieert de verwijdering:

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

Aan de andere kant, als een query ongeldig is, genereert deze een uitzondering en wordt de transactie teruggedraaid. De volgende test zal bewijzen dat:

@Test (verwacht = DataAccessException.class) openbare ongeldige gegevenInvalidData_whenDeleting_thenFail () {dsl.delete (boek) .where (boek.ID.equal (1)) .execute (); }

6. Conclusie

Deze tutorial introduceerde de basis van Jooq, een Java-bibliotheek voor het werken met databases. Het behandelde de stappen om broncode te genereren uit een databasestructuur en hoe je met die database kunt communiceren met behulp van de nieuw gemaakte klassen.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in een GitHub-project.


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