28
jOOQ on YugabyteDB
I know jOOQ for a while, and I've recommended it many times to database developers because it overcomes two of the major problems of SQL:
- SQL queries being embedded as character strings, without compile-time validation
- SQL syntax being dependent on the database engine
But, as I'm not a Java developer, I actually never used it myself. This was in my to-do for a long time, so here is my first jOOQ program 😎 The occasion is there: verify that it works with YugabyteDB. Even without a specific dialect, I expect it to work seamlessly, because YugabyteDB is using the same query layer as PostgreSQL.
I'm running on a 4 vCPU (Arm) Oracle Cloud Developer Image which is free and contains all developer tools. I'll use GraalVM for my JDK:
[opc@C jooq]$ sudo update-alternatives --config java <<<6
There are 6 programs which provide 'java'.
Selection Command
-----------------------------------------------
1 java-1.8.0-openjdk.aarch64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el8_4.aarch64/jre/bin/java)
2 java-11-openjdk.aarch64 (/usr/lib/jvm/java-11-openjdk-11.0.11.0.9-2.el8_4.aarch64/bin/java)
3 /usr/java/jdk1.8.0_291-aarch64/bin/java
* 4 /usr/java/jdk-16.0.1.0.1/bin/java
5 /usr/java/jdk-11.0.11.0.1/bin/java
+ 6 /usr/lib64/graalvm/graalvm21-ee-java11/bin/java
[opc@C jooq]$ java --version
java 11.0.11 2021-04-20 LTS
Java(TM) SE Runtime Environment GraalVM EE 21.1.0 (build 11.0.11+9-LTS-jvmci-21.1-b05)
Java HotSpot(TM) 64-Bit Server VM GraalVM EE 21.1.0 (build 11.0.11+9-LTS-jvmci-21.1-b05, mixed mode, sharing)
[opc@C jooq]$
I'll not build a Maven project for this simple test. Just get the libraries I need in a directory and build my classpath from there:
mkdir -p /var/tmp/jooq && (
cd /var/tmp/jooq
wget -qc "https://www.jooq.org/download/license-accepted?type=oss&file=jOOQ-3.15.1.zip"
wget -qc https://repo1.maven.org/maven2/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar
wget -qc https://jdbc.postgresql.org/download/postgresql-42.2.23.jar
for i in *.zip ; do unzip -qo $i ; done
) && export CLASSPATH=.:$(find /var/tmp/jooq -name "*.jar" | paste -sd:)
At this point CLASSPATH includes my current directory and the .jar downloaded above. I have downloaded the latest jOOQ (which is free for Open Source databases, and both PostgreSQL and YugabyteDB are free in this context).
I have added JAXB APIs because of the following:
java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
And I've added PostgreSQL JDBC driver as I'll connect to YugabyteDB.
I have a YugabyteDB database with the Northwind demo schema:
psql postgres://franck:[email protected]:5433/yb_demo_northwind
(I leave it opened publicly so that you can copy-paste all the code, play with it but don't break it please)
Here is my configuration for code generation:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration xmlns="http://www.jooq.org/xsd/jooq-codegen-3.14.0.xsd">
<jdbc>
<driver>org.postgresql.Driver</driver>
<url>jdbc:postgresql://yb1.pachot.net:5433/yb_demo_northwind</url>
<user>franck</user>
<password></password>
</jdbc>
<generator>
<name>org.jooq.codegen.JavaGenerator</name>
<database>
<name>org.jooq.meta.postgres.PostgresDatabase</name>
<inputSchema>public</inputSchema>
<includes>.*</includes>
<excludes></excludes>
</database>
<target>
<packageName>northwind.generated</packageName>
<directory>.</directory>
</target>
</generator>
</configuration>
which I save in a northwind.xml
file, and run:
[opc@C jooq]$ java org.jooq.codegen.GenerationTool northwind.xml
The result is Java classes to access my schema:
[opc@C jooq]$ tree northwind
northwind
└── generated
├── DefaultCatalog.java
├── Keys.java
├── Public.java
├── tables
│  ├── Categories.java
│  ├── CustomerCustomerDemo.java
│  ├── CustomerDemographics.java
│  ├── Customers.java
│  ├── Employees.java
│  ├── EmployeeTerritories.java
│  ├── OrderDetails.java
│  ├── Orders.java
│  ├── Products.java
│  ├── records
│  │  ├── CategoriesRecord.java
│  │  ├── CustomerCustomerDemoRecord.java
│  │  ├── CustomerDemographicsRecord.java
│  │  ├── CustomersRecord.java
│  │  ├── EmployeesRecord.java
│  │  ├── EmployeeTerritoriesRecord.java
│  │  ├── OrderDetailsRecord.java
│  │  ├── OrdersRecord.java
│  │  ├── ProductsRecord.java
│  │  ├── RegionRecord.java
│  │  ├── ShippersRecord.java
│  │  ├── SuppliersRecord.java
│  │  ├── TerritoriesRecord.java
│  │  └── UsStatesRecord.java
│  ├── Region.java
│  ├── Shippers.java
│  ├── Suppliers.java
│  ├── Territories.java
│  └── UsStates.java
└── Tables.java
I'll not go into the details, I just followed some bits of jOOQ documentation and used https://www.jooq.org/translate to write some SQL and see the translation into the jOOQ "output dialect".
So my goal was to run a simple query with SELECT ... FROM ... JOIN ... WHERE ... ORDER BY in order to list some orders per products, with the following output:
Jul 19, 2021 2:27:05 PM org.jooq.tools.JooqLogger info
INFO:
jOOQ tip of the day: While you don't have to use jOOQ's code generator, there are *lots* of awesome features you're missing out on if you're not using it!
Connected to version version:
11.2-YB-2.6.0.0-b0
dialect detected:
POSTGRES
Order Product Quantity Date
10403 Chocolade 70 1997-01-03
10704 Chocolade 24 1997-10-14
10453 Chocolade 15 1997-02-21
10507 Chocolade 15 1997-04-15
10814 Chocolade 8 1998-01-05
10604 Chocolade 6 1997-07-18
Here is my code:
// JDBC imports
import java.sql.*;
// jOOQ imports
import org.jooq.*;
import org.jooq.impl.DSL;
// generated code for the schema
import static northwind.generated.Tables.*;
import northwind.generated.tables.*;
public class Northwind {
public static void main( String[] args ) {
// connection to my database
try (Connection conn = DriverManager.getConnection(
"jdbc:postgresql://yb1.pachot.net:5433/yb_demo_northwind", "franck", "Yugabyte"
)) {
// jOOQ context
DSLContext create = DSL.using(conn);
System.out.println( "Connected to version version:\n "
+conn.getMetaData().getDatabaseProductVersion()
+ "\n dialect detected:\n "
+create.dialect()
);
// Declaring SQL query aliases
Products p = PRODUCTS.as("p");
Orders o = ORDERS.as("o");
OrderDetails d = ORDER_DETAILS.as("d");
// Here is the SQL query
Result<Record> result = create
.select()
.from(p)
.join(d).on(p.PRODUCT_ID.eq(d.PRODUCT_ID))
.join(o).on(d.ORDER_ID.eq(o.ORDER_ID))
.where(p.PRODUCT_NAME.eq("Chocolade"))
.orderBy(d.QUANTITY.desc())
.fetch();
// print the header
System.out.println(
String.format("%6s %-40s %-6s %-20s"
,"Order"
,"Product"
,"Quantity"
,"Date"
)
);
System.out.println();
// print the rows fetched
for(Record r:result) {
System.out.println(
String.format("%6d %-40s %6d %-20s"
,r.getValue(d.ORDER_ID)
,r.getValue(p.PRODUCT_NAME)
,r.getValue(d.QUANTITY)
,r.getValue(o.ORDER_DATE)
)
);
}
}
catch (Exception e) { e.printStackTrace(); System.exit(255); }
System.exit(0);
}
}
This is really nice: all the power of the SQL declarative language natively embedded in the Java procedural language. Want to test? Just copy paste in Northwind.java
and run javac Northwind.java && java Northwind
with the CLASSPATH above (current directory where I have this Northwind.class and the generated directory, as well as the downloaded JARs.
My connection string here goes on my database on a very limited free VM. Want to try it with your databases? have a look at our cloud with a free tier: https://www.yugabyte.com/cloud/ (currently in beta). You can also install it where you want: https://docs.yugabyte.com/latest/quick-start/
28