jOOQ on YugabyteDB

franckpachot

Franck Pachot

Posted on July 19, 2021

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]$
Enter fullscreen mode Exit fullscreen mode

Libraries

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

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:YugabyteDB@yb1.pachot.net:5433/yb_demo_northwind
Enter fullscreen mode Exit fullscreen mode

(I leave it opened publicly so that you can copy-paste all the code, play with it but don't break it please)

Code Generator

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>
Enter fullscreen mode Exit fullscreen mode

which I save in a northwind.xml file, and run:

[opc@C jooq]$ java org.jooq.codegen.GenerationTool northwind.xml
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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".

Query with jOOQ

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
Enter fullscreen mode Exit fullscreen mode

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);
 }

}
Enter fullscreen mode Exit fullscreen mode

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/

šŸ’– šŸ’Ŗ šŸ™… šŸš©
franckpachot
Franck Pachot

Posted on July 19, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

jOOQ on YugabyteDB
java jOOQ on YugabyteDB

July 19, 2021