Persistência de Dados em Java com jOOQ (alternativa ao Hibernate, MyBatis, JDBC)
Olá pessoal,
Começando uma série de posts aqui no blog para falar sobre o jOOQ, um framework java de persistência de dados que pode ser usado como uma alternativa ao Hibernate, MyBatis, JDBC, SpringData, etc.
No post de hoje vou apresentar esse framework a vocês, prós e contras e como fazer o setup de um projeto HelloWorld que vamos usar como base nos próximos posts.
O jOOQ ganhou bastante espaço nos últimos tempos, principalmente em conferências na gringa. Um dos motivos é essa frase que encontramos logo na página inicial do framework:
jOOQ gera código Java a partir de seu banco de dados e permite que você construa consultas SQL typesafe através de sua API fluente.
Ou seja, ao contrário de outros frameworks, onde você configura a partir de classes Java, o jOOQ é focado totalmente no SQL, fazendo a configuração das classes para você. Vocês irão ver que apenas de setup inicial, quase não vamos escrever código.
O jOOQ é open source, e essa versão oferece suporte a banco de dados que também são open source. Se você quiser usar o jOOQ com algum banco de dados comercial, é preciso pagar (justo; e do ponto de vista corporativo, a licença tem um preço muito bom - afinal, desenvolvedor também precisa pagar contas né? rs).
Para fazer setup do nosso projeto, vamos precisar:
- IDE (vou usar o Eclipse)
- jars do jOOQ
- jar do conector do banco de dados (vou usar o MySQL)
- Banco de dados de exemplo (vou usar o World do MySQL)
Também é possível usar o Maven para criar o projeto (vou criar o projeto com os jars mesmo, caso alguém queria executar o exemplo e ainda não use Maven).
Os passos que vamos executar nesse tutorial são:
- Criação do banco de dados de exemplo
- Criação do projeto Java
- Geração das classes com o jOOQ
- Criação de uma classe Main para testar a conexão com o banco de dados
- Codificar uma query usando jOOQ DSL
- Iterar os resultados
1 - Criação do banco de dados de exemplo
Vou usar um banco de dados pronto para esse exemplo. É o banco World disponibilizado pelo MySQL mesmo - pode fazer download de qualquer versão abaixo - eu fiz do InnoDB.
Faça o Download e instal no seu MySQL. Basta executar os scripts que vem dentro do zip. O meu banco ficou assim:
2 - Criação do projeto Java
Crie um projeto Java (Java Project mesmo, o mais simples) - estou usando o Eclipse - e crie uma pasta chamada lib para colocar os jars - e não esqueça de adicionar os jars ao build path do projeto:
Maven
Se preferir, pode gerar o projeto maven e usar o seguintes artefatos no seu pom.xml:
<dependency> <groupId>org.jooq</groupId> <artifactId>jooq</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>org.jooq</groupId> <artifactId>jooq-meta</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>org.jooq</groupId> <artifactId>jooq-codegen</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.28</version> </dependency>
3 - Geração das classes com o jOOQ
O jOOQ é totalmente focado no SQL. Por este motivo, não criamos classes Java e mapeamos ao banco de dados como geralmente fazemos quando utilizamos Hibernate, JPA, MyBatis, etc.
É o jOOQ que gera o código java que precisamos para "mapear" o banco de dados. E é bem simples. Basta criar um arquivo XML para isso que fica assim - arquivo library.xml que é criado dentro da pasta src do projeto:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <configuration xmlns="http://www.jooq.org/xsd/jooq-codegen-3.2.0.xsd"> <!-- Configure the database connection here --> <jdbc> <driver>com.mysql.jdbc.Driver</driver> <url>jdbc:mysql://localhost:3306/world</url> <user>root</user> <password>root</password> </jdbc> <generator> <!-- The default code generator. You can override this one, to generate your own code style Defaults to org.jooq.util.DefaultGenerator --> <name>org.jooq.util.DefaultGenerator</name> <database> <!-- The database type. The format here is: org.util.[database].[database]Database --> <name>org.jooq.util.mysql.MySQLDatabase</name> <!-- The database schema (or in the absence of schema support, in your RDBMS this can be the owner, user, database name) to be generated --> <inputSchema>world</inputSchema> <!-- All elements that are generated from your schema (A Java regular expression. Use the pipe to separate several expressions) Watch out for case-sensitivity. Depending on your database, this might be important! --> <includes>.*</includes> <!-- All elements that are excluded from your schema (A Java regular expression. Use the pipe to separate several expressions). Excludes match before includes --> <excludes></excludes> </database> <target> <!-- The destination package of your generated classes (within the destination directory) --> <packageName>com.loiane.mysql.generatedclasses</packageName> <!-- The destination directory of your generated classes --> <directory>/Users/loiane/Documents/workspace/jOOQ-examples/src</directory> </target> </generator> </configuration>
- Nas linhas 4-9 temos os dados para fazer a conexão com o banco de dados.
- Na linha 19 temos qual é o banco de dados que vamos nos conectar. No nosso caso é o MySQL.
- Na linha 23 temos o nome do schema que queremos nos conectar.
- Na linha 38 qual é o pacote onde queremos que as classes sejam geradas.
- E na linha 41 qual é o caminho completo da pasta src do projeto.
Para gerar as classes é possível executar por linha de comando, mas vamos fazer no eclipse mesmo. Para isso, selecione o projeto e vá em Run Configurations:
Selecione o projeto e a classe principal como mostra abaixo:
E adicione o library.xml como parâmetro:
Clique em Run - e veja o output no console. Será algo similar a isso:
Jan 23, 2014 11:15:43 AM org.jooq.tools.JooqLogger info INFO: Initialising properties : /library.xml Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: License parameters Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Thank you for using jOOQ and jOOQ's code generator Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Database parameters Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: dialect : MYSQL Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: target dir : /Users/loiane/Documents/workspace/jOOQ-examples/src Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: target package : com.loiane.mysql.generatedclasses Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: includes : [.*] Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: excludes : [] Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: includeExcludeColumns : false Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: DefaultGenerator parameters Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: strategy : class org.jooq.util.DefaultGeneratorStrategy Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: deprecated : true Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: generated annotation : true Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: JPA annotations : false Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: validation annotations : false Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: instance fields : true Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: records : true Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: pojos : false Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: immutable pojos : false Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: interfaces : false Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: daos : false Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: relations : true Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: global references : true Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Generation remarks Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Emptying : /Users/loiane/Documents/workspace/jOOQ-examples/src/com/loiane/mysql/generatedclasses Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Generating schemata : Total: 1 Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Generating schema : World.java Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: ---------------------------------------------------------- Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Sequences fetched : 0 (0 included, 0 excluded) Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Tables fetched : 3 (3 included, 0 excluded) Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: UDTs fetched : 0 (0 included, 0 excluded) Jan 23, 2014 11:15:44 AM org.jooq.tools.JooqLogger info INFO: Generating tables Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating table : City.java [input=City, output=City, pk=KEY_City_PRIMARY] Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: ARRAYs fetched : 0 (0 included, 0 excluded) Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Enums fetched : 2 (2 included, 0 excluded) Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating table : Country.java [input=Country, output=Country, pk=KEY_Country_PRIMARY] Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating table : Countrylanguage.java [input=CountryLanguage, output=CountryLanguage, pk=KEY_CountryLanguage_PRIMARY] Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Tables generated : Total: 1.862s Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating table references Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Table refs generated : Total: 1.864s, +1.34ms Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating Keys Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Keys generated : Total: 1.868s, +4.122ms Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating records Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating record : CityRecord.java Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating record : CountryRecord.java Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating record : CountrylanguageRecord.java Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Table records generated : Total: 1.903s, +35.639ms Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating ENUMs Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating ENUM : CountryContinent.java Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Generating ENUM : CountrylanguageIsofficial.java Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Enums generated : Total: 1.907s, +3.571ms Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Routines fetched : 0 (0 included, 0 excluded) Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: Packages fetched : 0 (0 included, 0 excluded) Jan 23, 2014 11:15:46 AM org.jooq.tools.JooqLogger info INFO: GENERATION FINISHED! : Total: 1.911s, +3.693ms
E pronto! Faça o refresh do projeto e verá que as classes foram geradas!
No XML!
Se você assim como eu não é muito fã de XML, também é possível fazer a geração das classes acima sem usar XML!
Para isso, vamos criar uma classe com um método main (ponto de entrada):
package com.loiane.config; import org.jooq.util.GenerationTool; import org.jooq.util.jaxb.Configuration; import org.jooq.util.jaxb.Database; import org.jooq.util.jaxb.Generator; import org.jooq.util.jaxb.Jdbc; import org.jooq.util.jaxb.Target; public class JOOQConfig { public static void main(String[] args) throws Exception { Configuration configuration = new Configuration() .withJdbc(new Jdbc() .withDriver("com.mysql.jdbc.Driver") .withUrl("jdbc:mysql://localhost:3306/world") .withUser("root") .withPassword("root")) .withGenerator(new Generator() .withName("org.jooq.util.DefaultGenerator") .withDatabase(new Database() .withName("org.jooq.util.mysql.MySQLDatabase") .withIncludes(".*") .withExcludes("") .withInputSchema("world")) .withTarget(new Target() .withPackageName("com.loiane.mysql.generatedclasses") .withDirectory("/Users/loiane/Documents/workspace/jOOQ-examples/src"))); GenerationTool.main(configuration); } }
E se executarmos essa classe, o resultado será o mesmo que usando o library.xml:
4 - Criação de uma classe Main para testar a conexão com o banco de dados
O próximo passo agora é criar uma classe para testarmos a conexão. Para deixar mais simples, não vou usar JUnit ou alguma biblioteca de testes. Vou criar uma classe bem simples para testar:
package com.loiane.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class TestConnection { public static void main(String[] args) { Connection conn = null; String userName = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/world"; try { Class.forName("com.mysql.jdbc.Driver").newInstance(); conn = DriverManager.getConnection(url, userName, password); } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (SQLException ignore) { } } } } }
5 - Codificar uma query usando jOOQ DSL
O próximo passo é buscar os dados no banco. Para isso vamos usar o jOOQ DSL:
package com.loiane.test; import static com.loiane.mysql.generatedclasses.tables.Country.COUNTRY; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import org.jooq.DSLContext; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; import org.jooq.impl.DSL; public class TestConnection { public static void main(String[] args) { Connection conn = null; String userName = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/world"; try { Class.forName("com.mysql.jdbc.Driver").newInstance(); conn = DriverManager.getConnection(url, userName, password); //buscando dados no banco DSLContext create = DSL.using(conn, SQLDialect.MYSQL); Result<Record> result = create.select().from(COUNTRY).fetch(); } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (SQLException ignore) { } } } } }
6 - Iterar os resultados
E por último, vamos fazer um loop para ver se o resultado é o mesmo esperado:
package com.loiane.test; import static com.loiane.mysql.generatedclasses.tables.Country.COUNTRY; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import org.jooq.DSLContext; import org.jooq.Record; import org.jooq.Result; import org.jooq.SQLDialect; import org.jooq.impl.DSL; public class TestConnection { public static void main(String[] args) { Connection conn = null; String userName = "root"; String password = "root"; String url = "jdbc:mysql://localhost:3306/world"; try { Class.forName("com.mysql.jdbc.Driver").newInstance(); conn = DriverManager.getConnection(url, userName, password); //buscando dados no banco DSLContext create = DSL.using(conn, SQLDialect.MYSQL); Result<Record> result = create.select().from(COUNTRY).fetch(); //iterando os resultados for (Record r : result) { String code = r.getValue(COUNTRY.CODE); String name = r.getValue(COUNTRY.NAME); System.out.println("Codigo: " + code + " name: " + name); } } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); } catch (SQLException ignore) { } } } } }
E se executarmos a classe acima o resultado será:
Codigo: ABW name: Aruba Codigo: AFG name: Afghanistan Codigo: AGO name: Angola Codigo: AIA name: Anguilla Codigo: ALB name: Albania Codigo: AND name: Andorra Codigo: ANT name: Netherlands Antilles Codigo: ARE name: United Arab Emirates Codigo: ARG name: Argentina Codigo: ARM name: Armenia Codigo: ASM name: American Samoa Codigo: ATA name: Antarctica Codigo: ATF name: French Southern territories Codigo: ATG name: Antigua and Barbuda Codigo: AUS name: Australia Codigo: AUT name: Austria Codigo: AZE name: Azerbaijan ...
Prontinho!
Download do Código Fonte
Código completo disponível em: https://github.com/loiane/jOOQ-examples
Referência: http://www.jooq.org/doc/3.2/manual-single-page/
Até a próxima! :)