Uvod u Jooq s proljećem

1. Pregled

Ovaj će članak predstaviti Jooq objektno orijentirano postavljanje upita - Jooq - i jednostavan način postavljanja u suradnji s Spring Frameworkom.

Većina Java aplikacija ima neku vrstu postojanosti SQL-a i pristupa tom sloju uz pomoć alata više razine poput JPA. I dok je to korisno, u nekim vam je slučajevima doista potreban finiji, nijansirani alat da biste došli do vaših podataka ili da biste zapravo iskoristili sve što DB nudi.

Jooq izbjegava neke tipične ORM obrasce i generira kôd koji nam omogućuje izradu sigurnih upita i potpunu kontrolu nad generiranim SQL-om putem čistog i moćnog tečnog API-ja.

Ovaj se članak usredotočuje na proljetni MVC. Naš članak Podrška za proljetno pokretanje za jOOQ opisuje kako koristiti jOOQ u proljetnom pokretanju.

2. Ovisnosti Mavena

Sljedeće su zavisnosti potrebne za pokretanje koda u ovom vodiču.

2.1. jOOQ

 org.jooq jooq 3.2.14 

2.2. Proljeće

Za naš primjer potrebno je nekoliko proljetnih ovisnosti; međutim, da bismo pojednostavili stvari, samo ih moramo izričito uključiti u POM datoteku:

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

2.3. Baza podataka

Da bismo olakšali stvari našem primjeru, poslužit ćemo se ugrađenom bazom podataka H2:

 com.h2data baza podataka h2 1.4.191 

3. Generiranje koda

3.1. Struktura baze podataka

Uvedimo strukturu baze podataka s kojom ćemo raditi u ovom članku. Pretpostavimo da trebamo stvoriti bazu podataka za izdavača za pohranu podataka o knjigama i autorima kojima oni upravljaju, gdje autor može napisati mnogo knjiga, a knjigu mogu napisati i mnogi autori.

Da bismo to pojednostavili, generirat ćemo samo tri tablice: knjiga za knjige, Autor za autore, a nazvana je još jedna tablica knjiga autora predstavljati odnos mnogi-prema-mnogima između autora i knjiga. The Autor tablica ima tri stupca: iskaznica, ime, i prezime. The knjiga tablica sadrži samo a titula stupac i iskaznica Osnovni ključ.

Sljedeći SQL upiti, pohranjeni u intro_schema.sql datoteka resursa, izvršit će se prema bazi podataka koju smo već postavili kako bismo stvorili potrebne tablice i popunili ih uzorcima podataka:

TABELA ZA KAPANJE AKO POSTOJI author_book, author, book; CREATE TABLE autor (id INT NIJE NULL PRIMARNI KLJUČ, ime_VARCHAR (50), prezime VARCHAR (50) NOT NULL); STVARI TABLICU knjiga (id INT NIJE NULL PRIMARNI KLJUČ, naslov VARCHAR (100) NOT NULL); STVARI TABLICU author_book (author_id INT NOT NULL, book_id INT NOT NULL, PRIMARY KEY (author_id, book_id), CONSTRAINT fk_ab_author STRANI KLJUČ (author_id) LITERATURA author (id) ON UPDATE CASCADE ON DELETE CASCEF (iskaznica) ); UMESTI U VRIJEDNOSTI autora (1, 'Kathy', 'Sierra'), (2, 'Bert', 'Bates'), (3, 'Bryan', 'Basham'); UMETNITE U VRIJEDNOSTI knjige (1, 'Head First Java'), (2, 'Head First Servlets and JSP'), (3, 'OCA / OCP Java SE 7 Programmer'); INSERT INTO author_book VRIJEDNOSTI (1, 1), (1, 3), (2, 1);

3.2. Svojstva Maven dodatak

Za generiranje Jooq koda upotrijebit ćemo tri različita Maven dodatka. Prvi od njih je dodatak Properties Maven.

Ovaj dodatak koristi se za čitanje podataka o konfiguraciji iz datoteke resursa. Nije potrebno jer se podaci mogu izravno dodati u POM, ali dobro je upravljati svojstvima izvana.

U ovom ćemo odjeljku definirati svojstva za veze baze podataka, uključujući klasu JDBC upravljačkog programa, URL baze podataka, korisničko ime i lozinku u datoteci s imenom intro_config.properties. Eksternalizacija ovih svojstava olakšava prebacivanje baze podataka ili samo promjenu podataka o konfiguraciji.

The čitaj-svojstva projekta Cilj ovog dodatka trebao bi biti vezan za ranu fazu kako bi se podaci o konfiguraciji mogli pripremiti za upotrebu drugih dodataka. U ovom je slučaju vezan za inicijalizirati faza:

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

3.3. Dodatak za SQL Maven

Dodatak SQL Maven koristi se za izvršavanje SQL izraza za stvaranje i popunjavanje tablica baze podataka. Koristit će svojstva koja su izdvojena iz intro_config.properties datoteku pomoću dodataka Properties Maven i preuzmite SQL izraze iz intro_schema.sql resurs.

Dodatak SQL Maven konfiguriran je na sljedeći način:

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

Imajte na umu da se ovaj dodatak mora smjestiti kasnije od dodatka Properties Maven u POM datoteku, jer su njihovi ciljevi izvršavanja povezani u istu fazu, a Maven će ih izvršiti redoslijedom kojim su navedeni.

3.4. jOOQ Codegen dodatak

Dodatak Jooq Codegen generira Java kôd iz strukture tablice baze podataka. Njegova generirati cilj bi trebao biti vezan za generirati-izvori faza kako bi se osigurao ispravan redoslijed izvršenja. Metapodaci dodatka izgledaju ovako:

 org.jooq jooq-codegen-maven $ {org.jooq.version} generiraj-izvori generiraj $ {db.driver} $ {db.url} $ {db.username} $ {db.password} com.baeldung.jooq. uvod.db src / main / java 

3.5. Generiranje koda

Da bismo završili postupak generiranja izvornog koda, moramo pokrenuti Maven generirati-izvori faza. U Eclipseu to možemo učiniti desnim klikom na projekt i odabirom Trčati kao –>Maven generira-izvore. Nakon dovršenja naredbe izvorne datoteke koje odgovaraju datoteci Autor, knjiga, knjiga autora generiraju se tablice (i nekoliko drugih za prateću nastavu).

Krenimo u klase tablica da vidimo što je Jooq proizveo. Svaka klasa ima statičko polje istog imena kao i klasa, osim što su sva slova u imenu napisana velikim slovom. Slijede isječci koda preuzeti iz definicija generiranih klasa:

The Autor razred:

javni razred Autor proširuje TableImpl {javni statični konačni Autor AUTOR = novi autor (); // ostali članovi razreda}

The Knjiga razred:

public class Book proširuje TableImpl {public static final Book BOOK = new Book (); // ostali članovi razreda}

The Autor knjiga razred:

javni razred AuthorBook proširuje TableImpl {javni statički konačni AuthorBook AUTHOR_BOOK = novi AuthorBook (); // ostali članovi razreda}

Primjerci na koje se pozivaju ta statička polja poslužit će kao objekti za pristup podacima koji predstavljaju odgovarajuće tablice pri radu s drugim slojevima u projektu.

4. Proljetna konfiguracija

4.1. Prevođenje izuzetaka jOOQ u proljeće

Da bi izuzeci izbačeni iz Jooq izvršavanja bili u skladu s Spring podrškom za pristup bazi podataka, moramo ih prevesti u podvrste DataAccessException razred.

Definirajmo provedbu ExecuteListener sučelje za pretvaranje iznimaka:

javna klasa ExceptionTranslator proširuje DefaultExecuteListener {izuzetak javne praznine (kontekst ExecuteContext) {SQLDialect dijalekt = context.configuration (). dialect (); SQLExceptionTranslator translator = novi SQLErrorCodeSQLExceptionTranslator (dijalekt.ime ()); context.exception (translator .translate ("Pristup bazi podataka pomoću Jooq-a", context.sql (), context.sqlException ())); }}

Ovu će klasu koristiti kontekst aplikacije Spring.

4.2. Konfiguriranje proljeća

Ovaj odjeljak proći će korake za definiranje a Kontekst postojanosti koji sadrži metapodatke i grah koji će se koristiti u kontekstu aplikacije Spring.

Krenimo primjenom potrebnih napomena na razred:

  • @Konfiguracija: Neka razred bude prepoznat kao spremnik za grah
  • @ComponentScan: Konfigurirajte smjernice za skeniranje, uključujući vrijednost mogućnost deklariranja niza imena paketa za traženje komponenata. U ovom vodiču paket koji se traži je paket koji generira dodatak Jooq Codegen Maven
  • @EnableTransactionManagement: Omogućite da transakcijama upravlja Spring
  • @PropertySource: Navedite mjesta datoteka svojstava koja se učitavaju. Vrijednost u ovom članku upućuje na datoteku koja sadrži konfiguracijske podatke i dijalekt baze podataka, a to je ista datoteka spomenuta u pododjeljku 4.1.
@Configuration @ComponentScan ({"com.baeldung.Jooq.introduction.db.public_.tables"}) @EnableTransactionManagement @PropertySource ("classpath: intro_config.properties") javna klasa PersistenceContext {// Ostale deklaracije}

Zatim upotrijebite Okoliš objekt za dobivanje podataka o konfiguraciji, koji se zatim koristi za konfiguriranje Izvor podataka grah:

@Autowired privatno okruženje okoliša; @Bean public DataSource dataSource () {JdbcDataSource dataSource = new JdbcDataSource (); dataSource.setUrl (environment.getRequiredProperty ("db.url")); dataSource.setUser (environment.getRequiredProperty ("db.username")); dataSource.setPassword (environment.getRequiredProperty ("db.password"));
 vratiti dataSource; }

Sada definiramo nekoliko graha za rad s operacijama pristupa bazi podataka:

@Bean public TransactionAwareDataSourceProxyactionAwareDataSource () {return new TransactionAwareDataSourceProxy (dataSource ()); } @Bean public DataSourceTransactionManageractionManager () {return new DataSourceTransactionManager (dataSource ()); } @Bean public DataSourceConnectionProvider connectionProvider () {return new DataSourceConnectionProvider (actionAwareDataSource ()); } @Bean public ExceptionTranslator exceptionTransformer () {return new ExceptionTranslator (); } @Bean public DefaultDSLContext dsl () {vratiti novi DefaultDSLContext (konfiguracija ()); }

Na kraju, pružamo Jooq Konfiguracija implementaciju i proglasite ga proljetnim grahom koji će koristiti DSLContext razred:

@Bean public DefaultConfiguration configuration () {DefaultConfiguration JooqConfiguration = new DefaultConfiguration (); jooqConfiguration.set (connectionProvider ()); jooqConfiguration.set (novi DefaultExecuteListenerProvider (iznimkaTransformer ())); Niz sqlDialectName = environment.getRequiredProperty ("jooq.sql.dialect"); Dijalekt SQLDialect = SQLDialect.valueOf (sqlDialectName); jooqConfiguration.set (dijalekt); vrati jooqConfiguration; }

5. Korištenje jOOQ-a s proljećem

Ovaj odjeljak prikazuje upotrebu Jooq-a u uobičajenim upitima za pristup bazi podataka. Postoje dva testa, jedan za urezivanje i jedan za vraćanje, za svaku vrstu operacije "pisanja", uključujući umetanje, ažuriranje i brisanje podataka. Korištenje operacije "čitanja" ilustrirano je prilikom odabira podataka za provjeru upita "pisanje".

Započet ćemo proglašavanjem automatske veze DSLContext objekt i instance Jooq generiranih klasa koje će koristiti sve metode ispitivanja:

@Autowired private DSLContext dsl; Autor autor = Autor.AUTHOR; Knjiga knjiga = Book.BOOK; AuthorBook authorBook = AutorKnjiga.AUTHOR_BOOK;

5.1. Umetanje podataka

Prvi korak je umetanje podataka u tablice:

dsl.insertInto (author) .set (author.ID, 4) .set (author.FIRST_NAME, "Herbert") .set (author.LAST_NAME, "Schildt") .execute (); dsl.insertInto (book) .set (book.ID, 4) .set (book.TITLE, "Vodič za početnike") .execute (); dsl.insertInto (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 4) .execute ();

A ODABERI upit za izdvajanje podataka:

Proizlaziti rezultat = dsl .select (author.ID, author.LAST_NAME, DSL.count ()) .from (author) .join (authorBook) .on (author.ID.equal (authorBook.AUTHOR_ID)) .join (book). na (authorBook.BOOK_ID.equal (book.ID)) .groupBy (author.LAST_NAME) .fetch ();

Gornji upit daje sljedeći izlaz:

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

Rezultat potvrđuje Tvrditi API:

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

Kada se neuspjeh dogodi zbog neispravnog upita, izbacuje se iznimka i transakcija se vraća. U sljedećem primjeru, UMETNUTI upit krši ograničenje stranog ključa, što rezultira iznimkom:

@Test (očekuje se = DataAccessException.class) javna praznina givenInvalidData_whenInserting_thenFail () {dsl.insertInto (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 5) .execute (); }

5.2. Ažuriranje podataka

Ajmo sada ažurirati postojeće podatke:

dsl.update (author) .set (author.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 ();

Dobiti potrebne podatke:

Proizlaziti result = dsl .select (author.ID, author.LAST_NAME, book.TITLE) .from (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 ();

Izlaz bi trebao biti:

+ ---- + --------- + ---------------------------------- + | ID | LAST_NAME | TITLE | + ---- + --------- + ---------------------------------- + | 3 | Baeldung | Izgradnja vašeg REST API-ja s Springom | + ---- + --------- + ---------------------------------- +

Sljedeći test provjerit će je li Jooq radio kako se očekivalo:

assertEquals (1, result.size ()); assertEquals (Integer.valueOf (3), result.getValue (0, author.ID)); assertEquals ("Baeldung", result.getValue (0, autor.LAST_NAME)); assertEquals ("Izgradnja vašeg REST API-ja s Springom", result.getValue (0, book.TITLE));

U slučaju kvara, izbacuje se iznimka i transakcija se vraća, što potvrđujemo testom:

@Test (očekuje se = DataAccessException.class) javna praznina givenInvalidData_whenUpdating_thenFail () {dsl.update (authorBook) .set (authorBook.AUTHOR_ID, 4) .set (authorBook.BOOK_ID, 5) .execute (); }

5.3. Brisanje podataka

Sljedeća metoda briše neke podatke:

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

Evo upita za čitanje pogođene tablice:

Proizlaziti rezultat = dsl .select (author.ID, author.FIRST_NAME, author.LAST_NAME) .from (author) .fetch ();

Izlaz upita:

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

Sljedeći test potvrđuje brisanje:

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

S druge strane, ako je upit nevaljan, izbacit će iznimku i transakcija će se vratiti. Sljedeći test će dokazati da:

@Test (očekuje se = DataAccessException.class) javna praznina givenInvalidData_whenDeleting_thenFail () {dsl.delete (book) .where (book.ID.equal (1)) .execute (); }

6. Zaključak

Ovaj je vodič predstavio osnove Jooq-a, Java knjižnice za rad s bazama podataka. Obuhvatio je korake za generiranje izvornog koda iz strukture baze podataka i kako stupiti u interakciju s tom bazom podataka pomoću novostvorenih klasa.

Implementacija svih ovih primjera i isječaka koda može se naći u projektu GitHub.