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! :)








