Jooq Adoc
Jooq Adoc
Marco Behler
2019-12-15
:revdate: 2019-12-15
:page-layout: layout-guides
:linkattrs:
:page-image: "/images/guides/undraw_hacker_mindset_gjwq.png"
:page-description: You can use this guide to understand what jOOQ is, how to get
started with it quickly and how jOOQ integrates with or compares to libraries like
Spring and Hibernate.
:page-published: true
:page-tags: ["jooq", "java sql", "java databases"]
:page-commento_id: guides-jooq-short
== What is jOOQ?
This is of course just the very high-level overview. If you want to know in more
detail how jOOQ works: Read on.
== jOOQ: Crash-Course
With Hibernate, you usually start writing your Java classes first and then let
Hibernate or a tool like Liquibase or Flyway generate corresponding database
tables.
With jOOQ, you start with your database. Your database and tables must already
exist and you then use jOOQ's code generator to generate Java classes for you.
The generation process for all three options is always the same. Whereas in the
standalone case you need to trigger generation manually, with Maven and Gradle
plugins code generation will happen automatically as part of the build process.
In any case, you need a configuration file for your jOOQ's code generator which is,
by default, called library.xml. It looks like this:
[source,xml]
----
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration xmlns="http://www.jooq.org/xsd/jooq-codegen-3.12.0.xsd">
<jdbc> // <1>
<driver>com.mysql.cj.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306/library</url>
<user>root</user>
<password></password>
</jdbc>
<generator>
<database> // <2>
<!-- database specific options: What tables to schema/tables to include or
exclujde ? What type of database? etc-->
<includes>.*</includes>
</database>
<target> // <3>
<!-- The destination package of your generated classes (within the
destination directory) -->
<packageName>my.startup</packageName>
As you can see, the library.xml config file roughly boils down to:
Example:
Imagine your database has a *USERS table*. Running the code generator with the
generator config will create (among many others) two classes:
[ditaa,jooq-code-generation-1,png]
----
+-------------------------------+ +-----------------------------------+
| Database | | Java/Jooq Class |
|-------------------------------| |-----------------------------------|
| | | |
| Table: Users (schema) | -------> | my.startup.Users |
| | | |
+-------------------------------+ +-----------------------------------+
+-------------------------------+ +-----------------------------------+
| Database | | Java/Jooq Class |
|-------------------------------| |-----------------------------------|
| | | |
| Table: Users (columns) | -------> | my.startup.UsersRecord |
| | | |
+-------------------------------+ +-----------------------------------+
----
Let's see how we can execute SQL queries with these generated classes:
jOOQ has two classes, DSL and DSLContext (Domain Specific Language) that developers
need to use to start writing SQL queries. You could also call these DSL classes
_SQL Builder_ classes, as they let you, well, build SQL queries.
As jOOQ is just a wrapper around JDBC, Java database basics apply and you need to
get a database connection for jOOQ to work. You either open one yourself, or ask
your connection pool to give you one.
[source,java]
----
import java.sql.SQLException;
import static my.startup.Tables.*;
}
}
}
----
That's it.
This short guide cannot give you a comprehensive explanation on every possible jOOQ
query, so let's have a look at some simple, common queries to get a basic
understanding.
Do keep in mind, that jOOQ queries read almost exactly like the corresponding SQL
query, so without strong SQL skills you will run into problems.
To execute a simple _select * from USERS where id = :id_ you would execute this
query:
[source,java]
----
UsersRecord record = dslContext.selectFrom(USERS)
.where(USERS.ID.eq(id))
.fetchAny();
// do something with record.getEmail()
----
With just a tiny change you would turn the query into _select * from USERS where id
in (:ids)_:
[source,java]
----
Result<UsersRecord> userRecords = dslContext.selectFrom(USERS)
.where(USERS.ID.in(ids))
.fetch();
// (for-loop over userRecords)
----
Let's have a look at joining two tables, like: _select * from USERS u inner join
PAYMENTS p on u.id = p.user_id_:
[source,java]
----
Result<?> result = dslContext.select()
.from(USERS.join(PAYMENTS)
.on(PAYMENTS.USER_ID.eq(USERS.ID)))
.fetch();
// (for-loop over join records)
----
Finally, deletes (_delete from USERS where id = 1_) or updates (_update USERS set
email = :email, username = :username where id = 1_) look like this:
[source,java]
----
dslContext.delete(USERS)
.where(USERS.ID.eq(1))
.execute();
----
[source,java]
----
dslContext.update(USERS)
.set(USERS.USERNAME, "John Rambo")
.set(USERS.EMAIL, "[email protected]")
.where(USERS.ID.eq(1))
.execute();
----
mb_ad::jooq_course[]
=== Summary
Now you might understand why jOOQ calls itself _typesafe_ database library. It lets
you write Java code that looks like SQL. With the benefit that your generated DSL
knows that e.g. user ids have to be numbers, user names have to be strings etc.
Spring Boot comes with a jOOQ autoconfiguration, which means it sets up the
DSLContext and integrates jOOQ with Spring's transaction handling for you - without
you having to do anything apart from adding the following dependency to your Spring
Boot project.
[source,xml]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jooq</artifactId>
<version>2.2.2.RELEASE</version> <!-- or your appropriate Spring boot version
-->
</dependency>
----
[source,java]
----
@Service
public class UserService {
@Autowired
private DSLContext dslContext; // <1>
@Transactional // <2>
public boolean registerUser(String email) {
UsersRecord existingRecord = dslContext.selectFrom(USERS)
.where(USERS.EMAIL.eq(email))
.fetchAny();
if (existingRecord != null) {
return false;
}
<1> Spring Boot creates the DSLContext automatically for you (dependent on a
datasource).
<2> jOOQ will take part in the Spring transaction demarcated by @Transactional.
As already mentioned at the beginning of this guide, both are quite different.
https://hibernate.org/[Hibernate] takes a Java-first approach, whereas you
(usually) write your Java classes and mappings first. Then you think about your
database (tables).
jOOQ on the other hand is database or SQL first, it needs an existing database
schema to work with and generate its Java classes from.
There is however nothing stopping you from using *both libraries* in the *same
project*. For a quick intro on how to do that, see this https://thoughts-on-
java.org/hibernate-jooq-a-match-made-in-heaven/[excellent post from Thorben
Janssen].
[source,xml]
----
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.UserMapper">
<select id="selectUser" resultType="User">
select * from Users where id = #{id}
</select>
</mapper>
----
[source,java]
----
// sqlsessionfactory is a myBatis specific entry point
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUser(1);
}
----
[source,java]
----
// 'Q' classes are generated and let you access tables, columns etc
QUser user = QUser.user;
User john = queryFactory.selectFrom(user)
.where(user.id.eq(1))
.fetchOne();
----
Note however, that the QueryDSL has been quite stagnant for a while and is
currently undergoing a project takeover.
== Fin
This guide is just a quick crash-course on what jOOQ can do for you and your
project and how it compares to other choices.