From def7a5525595eae68659fb111f485eb5a35e68f3 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Fri, 5 Aug 2016 11:42:39 +0200 Subject: [PATCH] [#2684] Rename org.jooq.scala package to org.jooq.scalaextensions because of potential collisions with the scala package --- .../resources/org/jooq/web/manual-3.9.xml | 16421 ++++++++++++++++ .../jooq/scalaextensions/Conversions.scala | 820 + 2 files changed, 17241 insertions(+) create mode 100644 jOOQ-manual/src/main/resources/org/jooq/web/manual-3.9.xml create mode 100644 jOOQ-scala/src/main/scala/org/jooq/scalaextensions/Conversions.scala diff --git a/jOOQ-manual/src/main/resources/org/jooq/web/manual-3.9.xml b/jOOQ-manual/src/main/resources/org/jooq/web/manual-3.9.xml new file mode 100644 index 0000000000..ab3956cb36 --- /dev/null +++ b/jOOQ-manual/src/main/resources/org/jooq/web/manual-3.9.xml @@ -0,0 +1,16421 @@ + + + +
+ The jOOQ User Manual. Multiple Pages + +

Overview

+

This manual is divided into six main sections:

+ +
    +
  • + +

    + This section will get you started with jOOQ quickly. It contains + simple explanations about what jOOQ is, what jOOQ isn't and how + to set it up for the first time +

    +
  • +
  • + +

    + This section explains all about the jOOQ syntax used for building + queries through the query DSL and the query model API. It explains + the central factories, the supported SQL statements and various other syntax elements +

    +
  • +
  • + +

    + This section explains how to configure and use the built-in source code + generator +

    +
  • +
  • + +

    + This section will get you through the specifics of what can be done + with jOOQ at runtime, in order to execute queries, perform CRUD + operations, import and export data, and hook into the jOOQ execution + lifecycle for debugging +

    +
  • +
  • + +

    + This section is dedicated to tools that ship with jOOQ, such as the + jOOQ's JDBC mocking feature +

    +
  • +
  • + +

    + This section is a reference for elements in this manual +

    +
  • +
+
+ + +
+ Preface + +

jOOQ's reason for being - compared to JPA

+

+ Java and SQL have come a long way. SQL is an "old", yet established and well-understood technology. Java is a legacy too, although its platform JVM allows for many new and contemporary languages built on top of it. Yet, after all these years, libraries dealing with the interface between SQL and Java have come and gone, leaving JPA to be a standard that is accepted only with doubts, short of any surviving options. +

+

+ So far, there had been only few database abstraction frameworks or libraries, that truly respected SQL as a first class citizen among languages. Most frameworks, including the industry standards JPA, EJB, Hibernate, JDO, Criteria Query, and many others try to hide SQL itself, minimising its scope to things called JPQL, HQL, JDOQL and various other inferior query languages +

+

+ jOOQ has come to fill this gap. +

+ +

jOOQ's reason for being - compared to LINQ

+

+ Other platforms incorporate ideas such as LINQ (with LINQ-to-SQL), or Scala's SLICK, or also Java's QueryDSL to better integrate querying as a concept into their respective language. By querying, they understand querying of arbitrary targets, such as SQL, XML, Collections and other heterogeneous data stores. jOOQ claims that this is going the wrong way too. +

+

+ In more advanced querying use-cases (more than simple CRUD and the occasional JOIN), people will want to profit from the expressivity of SQL. Due to the relational nature of SQL, this is quite different from what object-oriented and partially functional languages such as C#, Scala, or Java can offer. +

+

+ It is very hard to formally express and validate joins and the ad-hoc table expression types they create. It gets even harder when you want support for more advanced table expressions, such as pivot tables, unnested cursors, or just arbitrary projections from derived tables. With a very strong object-oriented typing model, these features will probably stay out of scope. +

+

+ In essence, the decision of creating an API that looks like SQL or one that looks like C#, Scala, Java is a definite decision in favour of one or the other platform. While it will be easier to evolve SLICK in similar ways as LINQ (or QueryDSL in the Java world), SQL feature scope that clearly communicates its underlying intent will be very hard to add, later on (e.g. how would you model Oracle's partitioned outer join syntax? How would you model ANSI/ISO SQL:1999 grouping sets? How can you support scalar subquery caching? etc...). +

+

+ jOOQ has come to fill this gap. +

+ +

jOOQ's reason for being - compared to SQL / JDBC

+

+ So why not just use SQL? +

+

+ SQL can be written as plain text and passed through the JDBC API. Over the years, people have become wary of this approach for many reasons: +

+
    +
  • No typesafety
  • +
  • No syntax safety
  • +
  • No bind value index safety
  • +
  • Verbose SQL String concatenation
  • +
  • Boring bind value indexing techniques
  • +
  • Verbose resource and exception handling in JDBC
  • +
  • A very "stateful", not very object-oriented JDBC API, which is hard to use
  • +
+

+ For these many reasons, other frameworks have tried to abstract JDBC away in the past in one way or another. Unfortunately, many have completely abstracted SQL away as well +

+

+ jOOQ has come to fill this gap. +

+ +

jOOQ is different

+

+ SQL was never meant to be abstracted. To be confined in the narrow boundaries of heavy mappers, hiding the beauty and simplicity of relational data. SQL was never meant to be object-oriented. SQL was never meant to be anything other than... SQL! +

+
+
+ + + +
+ Getting started with jOOQ + +

+ These chapters contain a quick overview of how to get started with this manual and with jOOQ. While the subsequent chapters contain a lot of reference information, this chapter here just wraps up the essentials. +

+
+ + +
+ How to read this manual + +

+ This section helps you correctly interpret this manual in the context of jOOQ. +

+ +

Code blocks

+

+ The following are code blocks: +

+ + + + + + +]]> + + + +

+ These are useful to provide examples in code. Often, with jOOQ, it is even more useful to compare SQL code with its corresponding Java/jOOQ code. When this is done, the blocks are aligned side-by-side, with SQL usually being on the left, and an equivalent jOOQ DSL query in Java usually being on the right: +

+ + + + + + +

Code block contents

+

+ The contents of code blocks follow conventions, too. If nothing else is mentioned next to any given code block, then the following can be assumed: +

+ + + + + + +

+ Your naming may differ, of course. For instance, you could name the "create" instance "db", instead. +

+ +

Execution

+ +

+ When you're coding PL/SQL, T-SQL or some other procedural SQL language, SQL statements are always executed immediately at the semi-colon. This is not the case in jOOQ, because as an internal DSL, jOOQ can never be sure that your statement is complete until you call fetch() or execute(). The manual tries to apply fetch() and execute() as thoroughly as possible. If not, it is implied: +

+ + + + + + +

Degree (arity)

+

+ jOOQ records (and many other API elements) have a degree N between 1 and {max-row-degree}. The variable degree of an API element is denoted as [N], e.g. Row[N] or Record[N]. The term "degree" is preferred over arity, as "degree" is the term used in the SQL standard, whereas "arity" is used more often in mathematics and relational theory. +

+ +

Settings

+

+ jOOQ allows to override runtime behaviour using . If nothing is specified, the default runtime settings are assumed. +

+ +

Sample database

+

+ jOOQ query examples run against the sample database. See the manual's section about to learn more about the sample database. +

+
+
+ +
+ The sample database used in this manual + +

+ For the examples in this manual, the same database will always be referred to. It essentially consists of these entities created using the Oracle dialect +

+CREATE TABLE language ( + id NUMBER(7) NOT NULL PRIMARY KEY, + cd CHAR(2) NOT NULL, + description VARCHAR2(50) +); + +CREATE TABLE author ( + id NUMBER(7) NOT NULL PRIMARY KEY, + first_name VARCHAR2(50), + last_name VARCHAR2(50) NOT NULL, + date_of_birth DATE, + year_of_birth NUMBER(7), + distinguished NUMBER(1) +); + +CREATE TABLE book ( + id NUMBER(7) NOT NULL PRIMARY KEY, + author_id NUMBER(7) NOT NULL, + title VARCHAR2(400) NOT NULL, + published_in NUMBER(7) NOT NULL, + language_id NUMBER(7) NOT NULL, + + CONSTRAINT fk_book_author FOREIGN KEY (author_id) REFERENCES author(id), + CONSTRAINT fk_book_language FOREIGN KEY (language_id) REFERENCES language(id) +); + +CREATE TABLE book_store ( + name VARCHAR2(400) NOT NULL UNIQUE +); + +CREATE TABLE book_to_book_store ( + name VARCHAR2(400) NOT NULL, + book_id INTEGER NOT NULL, + stock INTEGER, + + PRIMARY KEY(name, book_id), + CONSTRAINT fk_b2bs_book_store FOREIGN KEY (name) REFERENCES book_store (name) ON DELETE CASCADE, + CONSTRAINT fk_b2bs_book FOREIGN KEY (book_id) REFERENCES book (id) ON DELETE CASCADE +); +

+ More entities, types (e.g. UDT's, ARRAY types, ENUM types, etc), stored procedures and packages are introduced for specific examples +

+

+ In addition to the above, you may assume the following sample data: +

+ +
+
+ +
+ Different use cases for jOOQ + +

+ jOOQ has originally been created as a library for complete abstraction of JDBC and all database interaction. Various best practices that are frequently encountered in pre-existing software products are applied to this library. This includes: +

+
    +
  • Typesafe database object referencing through generated schema, table, column, record, procedure, type, dao, pojo artefacts (see the chapter about )
  • +
  • Typesafe SQL construction / SQL building through a complete querying DSL API modelling SQL as a domain specific language in Java (see the chapter about )
  • +
  • Convenient query execution through an improved API for result fetching (see the chapters about )
  • +
  • SQL dialect abstraction and SQL clause emulation to improve cross-database compatibility and to enable missing features in simpler databases (see the chapter about )
  • +
  • SQL logging and debugging using jOOQ as an integral part of your development process (see the chapters about )
  • +
+

+ Effectively, jOOQ was originally designed to replace any other database abstraction framework short of the ones handling connection pooling (and more sophisticated ) +

+ +

Use jOOQ the way you prefer

+

+ ... but open source is community-driven. And the community has shown various ways of using jOOQ that diverge from its original intent. Some use cases encountered are: +

+
    +
  • Using Hibernate for 70% of the queries (i.e. ) and jOOQ for the remaining 30% where SQL is really needed
  • +
  • Using jOOQ for SQL building and JDBC for SQL execution
  • +
  • Using jOOQ for SQL building and Spring Data for SQL execution
  • +
  • Using jOOQ without the to build the basis of a framework for dynamic SQL execution.
  • +
+ +

+ The following sections explain about various use cases for using jOOQ in your application. +

+
+ + +
+ jOOQ as a SQL builder + +

+ This is the most simple of all use cases, allowing for construction of valid SQL for any database. In this use case, you will not use and probably not even . Instead, you'll use to wrap strings, literals and other user-defined objects into an object-oriented, type-safe AST modelling your SQL statements. An example is given here: +

+ + + +

+ The SQL string built with the jOOQ query DSL can then be executed using JDBC directly, using Spring's JdbcTemplate, using Apache DbUtils and many other tools (note that since jOOQ uses PreparedStatement by default, this will generate a bind variable for "1948". ). +

+

+ If you wish to use jOOQ only as a SQL builder, the following sections of the manual will be of interest to you: +

+
    +
  • : This section contains a lot of information about creating SQL statements using the jOOQ API
  • +
  • : This section contains information useful in particular to those that want to supply , , etc. as plain SQL to jOOQ, rather than through generated artefacts
  • +
+
+
+ +
+ jOOQ as a SQL builder with code generation + +

+ In addition to using jOOQ as a , you can also use jOOQ's code generation features in order to compile your SQL statements using a Java compiler against an actual database schema. This adds a lot of power and expressiveness to just simply constructing SQL using the query DSL and custom strings and literals, as you can be sure that all database artefacts actually exist in the database, and that their type is correct. An example is given here: +

+ + + +

+ The SQL string that you can generate as such can then be executed using JDBC directly, using Spring's JdbcTemplate, using Apache DbUtils and many other tools. +

+

+ If you wish to use jOOQ only as a SQL builder with code generation, the following sections of the manual will be of interest to you: +

+
    +
  • : This section contains a lot of information about creating SQL statements using the jOOQ API
  • +
  • : This section contains the necessary information to run jOOQ's code generator against your developer database
  • +
+
+
+ +
+ jOOQ as a SQL executor + +

+ Instead of any tool mentioned in the previous chapters, you can also use jOOQ directly to execute your jOOQ-generated SQL statements. This will add a lot of convenience on top of the previously discussed API for typesafe SQL construction, when you can re-use the information from generated classes to fetch records and custom data types. An example is given here: +

+ +> result = +create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .from(BOOK) + .join(AUTHOR) + .on(BOOK.AUTHOR_ID.equal(AUTHOR.ID)) + .where(BOOK.PUBLISHED_IN.equal(1948)) + .fetch();]]> + +

+ By having jOOQ execute your SQL, the jOOQ query DSL becomes truly embedded SQL. +

+ +

+ jOOQ doesn't stop here, though! You can execute any SQL with jOOQ. In other words, you can use any other SQL building tool and run the SQL statements with jOOQ. An example is given here: +

+ + result = create.fetch(sql); + +// Or execute that SQL with JDBC, fetching the ResultSet with jOOQ: +ResultSet rs = connection.createStatement().executeQuery(sql); +Result result = create.fetch(rs);]]> + +

+ If you wish to use jOOQ as a SQL executor with (or without) code generation, the following sections of the manual will be of interest to you: +

+
    +
  • : This section contains a lot of information about creating SQL statements using the jOOQ API
  • +
  • : This section contains the necessary information to run jOOQ's code generator against your developer database
  • +
  • : This section contains a lot of information about executing SQL statements using the jOOQ API
  • +
  • : This section contains some useful information about the various ways of fetching data with jOOQ
  • +
+
+
+ +
+ jOOQ for CRUD + +

+ This is probably the most complete use-case for jOOQ: Use all of jOOQ's features. Apart from jOOQ's fluent API for query construction, jOOQ can also help you execute everyday CRUD operations. An example is given here: +

+ + 5) { + + // Mark the author as a "distinguished" author + author.setDistinguished(1); + author.store(); + } +}]]> + +

+ If you wish to use all of jOOQ's features, the following sections of the manual will be of interest to you (including all sub-sections): +

+
    +
  • : This section contains a lot of information about creating SQL statements using the jOOQ API
  • +
  • : This section contains the necessary information to run jOOQ's code generator against your developer database
  • +
  • : This section contains a lot of information about executing SQL statements using the jOOQ API
  • +
+
+
+ +
+ jOOQ for PROs + +

+ jOOQ isn't just a library that helps you and SQL against your . jOOQ ships with a lot of tools. Here are some of the most important tools shipped with jOOQ: +

+
    +
  • : jOOQ allows you to hook your custom execute listeners into jOOQ's SQL statement execution lifecycle in order to centrally coordinate any arbitrary operation performed on SQL being executed. Use this for logging, identity generation, SQL tracing, performance measurements, etc.
  • +
  • : jOOQ has a standard DEBUG logger built-in, for logging and tracing all your executed SQL statements and fetched result sets
  • +
  • : jOOQ supports stored procedures and functions of your favourite database. All routines and user-defined types are generated and can be included in jOOQ's SQL building API as function references.
  • +
  • : Batch execution is important when executing a big load of SQL statements. jOOQ simplifies these operations compared to JDBC
  • +
  • and : jOOQ ships with an API to easily export/import data in various formats
  • +
+

+ If you're a power user of your favourite, feature-rich database, jOOQ will help you access all of your database's vendor-specific features, such as OLAP features, stored procedures, user-defined types, vendor-specific SQL, functions, etc. Examples are given throughout this manual. +

+
+
+
+
+ +
+ Tutorials + +

+ Don't have time to read the full manual? Here are a couple of tutorials that will get you into the most essential parts of jOOQ as quick as possible. +

+
+ + +
+ jOOQ in 7 easy steps + +

+ This manual section is intended for new users, to help them get a running application with jOOQ, quickly. +

+
+ + +
+ Step 1: Preparation + +

+ If you haven't already downloaded it, download jOOQ:
+ http://www.jooq.org/download +

+ +

+ Alternatively, you can create a Maven dependency to download jOOQ artefacts: +

+ +

Open Source Edition

+ + + org.jooq + jooq + {jooq-version} + + + org.jooq + jooq-meta + {jooq-version} + + + org.jooq + jooq-codegen + {jooq-version} +]]> + +

Commercial Editions (Java 8+)

+ + + org.jooq.pro + jooq + {jooq-version} + + + org.jooq.pro + jooq-meta + {jooq-version} + + + org.jooq.pro + jooq-codegen + {jooq-version} +]]> + +

Commercial Editions (Java 6+)

+ + + org.jooq.pro-java-6 + jooq + {jooq-version} + + + org.jooq.pro-java-6 + jooq-meta + {jooq-version} + + + org.jooq.pro-java-6 + jooq-codegen + {jooq-version} +]]> + +

Commercial Editions (Free Trial)

+ + + org.jooq + jooq + {jooq-version} + + + org.jooq + jooq-meta + {jooq-version} + + + org.jooq + jooq-codegen + {jooq-version} +]]> + +

+ Note that only the jOOQ Open Source Edition is available from Maven Central. If you're using the jOOQ Professional Edition or the jOOQ Enterprise Edition, you will have to manually install jOOQ in your local Nexus, or in your local Maven cache. For more information, please refer to the licensing pages. +

+ +

+ Please refer to the manual's section about to learn how to use jOOQ's code generator with Maven. +

+ +

+ For this example, we'll be using MySQL. If you haven't already downloaded MySQL Connector/J, download it here:
+ http://dev.mysql.com/downloads/connector/j/ +

+ +

+ If you don't have a MySQL instance up and running yet, get XAMPP now! XAMPP is a simple installation bundle for Apache, MySQL, PHP and Perl +

+
+
+ +
+ Step 2: Your database + +

+ We're going to create a database called "library" and a corresponding "author" table. Connect to MySQL via your command line client and type the following: +

+ +CREATE DATABASE `library`; + +USE `library`; + +CREATE TABLE `author` ( + `id` int NOT NULL, + `first_name` varchar(255) DEFAULT NULL, + `last_name` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +); + +
+
+ +
+ Step 3: Code generation + +

+ In this step, we're going to use jOOQ's command line tools to generate classes that map to the Author table we just created. More detailed information about how to set up the jOOQ code generator can be found here:
+ +

+ +

+ The easiest way to generate a schema is to copy the jOOQ jar files (there should be 3) and the MySQL Connector jar file to a temporary directory. Then, create a library.xml that looks like this: +

+ + + + + + com.mysql.jdbc.Driver + jdbc:mysql://localhost:3306/library + root + + + + + + org.jooq.util.JavaGenerator + + + + org.jooq.util.mysql.MySQLDatabase + + + library + + + .* + + + + + + + + test.generated + + + C:/workspace/MySQLTest/src/main/java + + +]]> +

+ Replace the username with whatever user has the appropriate privileges to query the database meta data. You'll also want to look at the other values and replace as necessary. Here are the two interesting properties: +

+

+ generator.target.package - set this to the parent package you want to create for the generated classes. The setting of test.generated will cause the test.generated.Author and test.generated.AuthorRecord to be created +

+

+ generator.target.directory - the directory to output to. +

+ +

+ Once you have the JAR files and library.xml in your temp directory, type this on a Windows machine: +

+ +java -classpath jooq-{jooq-version}.jar;jooq-meta-{jooq-version}.jar;jooq-codegen-{jooq-version}.jar;mysql-connector-java-5.1.18-bin.jar;. + org.jooq.util.GenerationTool library.xml + + +

+ ... or type this on a UNIX / Linux / Mac system (colons instead of semi-colons): +

+ +java -classpath jooq-{jooq-version}.jar:jooq-meta-{jooq-version}.jar:jooq-codegen-{jooq-version}.jar:mysql-connector-java-5.1.18-bin.jar:. + org.jooq.util.GenerationTool library.xml + + +

+ Note: jOOQ will try loading the library.xml from your classpath. This is also why there is a trailing period (.) on the classpath. If the file cannot be found on the classpath, jOOQ will look on the file system from the current working directory. +

+ +

+ Replace the filenames with your actual filenames. In this example, jOOQ {jooq-version} is being used. If everything has worked, you should see this in your console output: +

+ +Nov 1, 2011 7:25:06 PM org.jooq.impl.JooqLogger info +INFO: Initialising properties : /library.xml +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Database parameters +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: ---------------------------------------------------------- +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: dialect : MYSQL +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: schema : library +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: target dir : C:/workspace/MySQLTest/src +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: target package : test.generated +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: ---------------------------------------------------------- +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Emptying : C:/workspace/MySQLTest/src/test/generated +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Generating classes in : C:/workspace/MySQLTest/src/test/generated +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Generating schema : Library.java +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Schema generated : Total: 122.18ms +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Sequences fetched : 0 (0 included, 0 excluded) +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Tables fetched : 5 (5 included, 0 excluded) +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Generating tables : C:/workspace/MySQLTest/src/test/generated/tables +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: ARRAYs fetched : 0 (0 included, 0 excluded) +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Enums fetched : 0 (0 included, 0 excluded) +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: UDTs fetched : 0 (0 included, 0 excluded) +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Generating table : Author.java +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Tables generated : Total: 680.464ms, +558.284ms +Nov 1, 2011 7:25:07 PM org.jooq.impl.JooqLogger info +INFO: Generating Keys : C:/workspace/MySQLTest/src/test/generated/tables +Nov 1, 2011 7:25:08 PM org.jooq.impl.JooqLogger info +INFO: Keys generated : Total: 718.621ms, +38.157ms +Nov 1, 2011 7:25:08 PM org.jooq.impl.JooqLogger info +INFO: Generating records : C:/workspace/MySQLTest/src/test/generated/tables/records +Nov 1, 2011 7:25:08 PM org.jooq.impl.JooqLogger info +INFO: Generating record : AuthorRecord.java +Nov 1, 2011 7:25:08 PM org.jooq.impl.JooqLogger info +INFO: Table records generated : Total: 782.545ms, +63.924ms +Nov 1, 2011 7:25:08 PM org.jooq.impl.JooqLogger info +INFO: Routines fetched : 0 (0 included, 0 excluded) +Nov 1, 2011 7:25:08 PM org.jooq.impl.JooqLogger info +INFO: Packages fetched : 0 (0 included, 0 excluded) +Nov 1, 2011 7:25:08 PM org.jooq.impl.JooqLogger info +INFO: GENERATION FINISHED! : Total: 791.688ms, +9.143ms + + +
+
+ +
+ Step 4: Connect to your database + +

+ Let's just write a vanilla main class in the project containing the generated classes: +

+ + + +

+ This is pretty standard code for establishing a MySQL connection. +

+
+
+ +
+ Step 5: Querying + +

+ Let's add a simple query constructed with jOOQ's query DSL: +

+ + result = create.select().from(AUTHOR).fetch();]]> + +

+ First get an instance of DSLContext so we can write a simple SELECT query. We pass an instance of the MySQL connection to DSL. Note that the DSLContext doesn't close the connection. We'll have to do that ourselves. +

+

+ We then use jOOQ's query DSL to return an instance of Result. We'll be using this result in the next step. +

+
+
+ +
+ Step 6: Iterating + +

+ After the line where we retrieve the results, let's iterate over the results and print out the data: +

+ + + +

+ The full program should now look like this: +

+ + result = create.select().from(AUTHOR).fetch(); + + for (Record r : result) { + Integer id = r.getValue(AUTHOR.ID); + String firstName = r.getValue(AUTHOR.FIRST_NAME); + String lastName = r.getValue(AUTHOR.LAST_NAME); + + System.out.println("ID: " + id + " first name: " + firstName + " last name: " + lastName); + } + } + + // For the sake of this tutorial, let's keep exception handling simple + catch (Exception e) { + e.printStackTrace(); + } + } +}]]> +
+
+ +
+ Step 7: Explore! + +

+ jOOQ has grown to be a comprehensive SQL library. For more information, please consider the documentation:
+ http://www.jooq.org/learn +

+

+ ... explore the Javadoc:
+ http://www.jooq.org/javadoc/latest/ +

+

+ ... or join the news group:
+ https://groups.google.com/forum/#!forum/jooq-user +

+

+ This tutorial is the courtesy of Ikai Lan. See the original source here:
+ http://ikaisays.com/2011/11/01/getting-started-with-jooq-a-tutorial/ +

+
+
+
+
+ +
+ Using jOOQ in modern IDEs + +

Feel free to contribute a tutorial!

+
+
+ +
+ Using jOOQ with Spring and Apache DBCP + +

+ jOOQ and Spring are easy to integrate. In this example, we shall integrate: +

+ + +

Before you copy the manual examples, consider also these further resources:

+ + +

Add the required Maven dependencies

+

+ For this example, we'll create the following Maven dependencies +

+ + + + 3.2.3.RELEASE + + + + + + + + org.jooq + jooq + {jooq-version} + + + org.apache.commons + commons-dbcp2 + 2.0 + + + com.h2database + h2 + 1.3.168 + + + + + log4j + log4j + 1.2.16 + + + org.slf4j + slf4j-log4j12 + 1.7.5 + + + + + org.springframework + spring-context + ${org.springframework.version} + + + org.springframework + spring-jdbc + ${org.springframework.version} + + + + + junit + junit + 4.11 + jar + test + + + org.springframework + spring-test + ${org.springframework.version} + test + + +]]> + +

+ Note that only the jOOQ Open Source Edition is available from Maven Central. If you're using the jOOQ Professional Edition or the jOOQ Enterprise Edition, you will have to manually install jOOQ in your local Nexus, or in your local Maven cache. For more information, please refer to the licensing pages. +

+ +

Create a minimal Spring configuration file

+

+ The above dependencies are configured together using a Spring Beans configuration: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + H2 + + + + + + + + + + + + +]]> + +

Run a query using the above configuration:

+

+ With the above configuration, you should be ready to run queries pretty quickly. For instance, in an integration-test, you could use Spring to run JUnit: +

+ +> result = + create.select(a.FIRST_NAME, a.LAST_NAME, countDistinct(s.NAME)) + .from(a) + .join(b).on(b.AUTHOR_ID.equal(a.ID)) + .join(t).on(t.BOOK_ID.equal(b.ID)) + .join(s).on(t.BOOK_STORE_NAME.equal(s.NAME)) + .groupBy(a.FIRST_NAME, a.LAST_NAME) + .orderBy(countDistinct(s.NAME).desc()) + .fetch(); + + assertEquals(2, result.size()); + assertEquals("Paulo", result.getValue(0, a.FIRST_NAME)); + assertEquals("George", result.getValue(1, a.FIRST_NAME)); + + assertEquals("Coelho", result.getValue(0, a.LAST_NAME)); + assertEquals("Orwell", result.getValue(1, a.LAST_NAME)); + + assertEquals(Integer.valueOf(3), result.getValue(0, countDistinct(s.NAME))); + assertEquals(Integer.valueOf(2), result.getValue(1, countDistinct(s.NAME))); + } +}]]> + +

Run a queries in an explicit transaction:

+

+ The following example shows how you can use Spring's TransactionManager to explicitly handle transactions: +

+ + + +

Run queries using declarative transactions

+

+ Spring-TX has very powerful means to handle transactions declaratively, using the @Transactional annotation. The BookService that we had defined in the previous Spring configuration can be seen here: +

+ + + * The implementation of this method has a bug, which causes this method to + * fail and roll back the transaction. + */ + @Transactional + void create(int id, int authorId, String title); + +}]]> + +

+ And here is how we interact with it: +

+ + +

Run queries using jOOQ's transaction API

+

+ jOOQ has its own programmatic transaction API that can be used with Spring transactions by implementing the jOOQ SPI and passing that to your jOOQ . More details about this transaction API can be found in the . +

+ +

+ You can try the above example yourself by downloading it from GitHub. +

+
+
+ +
+ Using jOOQ with Flyway + +

+ Flyway - Database Migrations Made EasyWhen performing database migrations, we at Data Geekery recommend using jOOQ with Flyway - Database Migrations Made Easy. In this chapter, we're going to look into a simple way to get started with the two frameworks. +

+ +

Philosophy

+ +

+ There are a variety of ways how jOOQ and Flyway could interact with each other in various development setups. In this tutorial we're going to show just one variant of such framework team play - a variant that we find particularly compelling for most use cases. +

+ +

+ The general philosophy behind the following approach can be summarised as this: +

+ +
    +
  • 1. Database increment
  • +
  • 2. Database migration
  • +
  • 3. Code re-generation
  • +
  • 4. Development
  • +
+ +

+ The four steps above can be repeated time and again, every time you need to modify something in your database. More concretely, let's consider: +

+ +
    +
  • 1. Database increment - You need a new column in your database, so you write the necessary DDL in a Flyway script
  • +
  • 2. Database migration - This Flyway script is now part of your deliverable, which you can share with all developers who can migrate their databases with it, the next time they check out your change
  • +
  • 3. Code re-generation - Once the database is migrated, you regenerate all jOOQ artefacts (see ), locally
  • +
  • 4. Development - You continue developing your business logic, writing code against the udpated, generated database schema
  • +
+ +

Maven Project Configuration - Properties

+ +

+ The following properties are defined in our pom.xml, to be able to reuse them between plugin configurations: +

+ + + jdbc:h2:~/flyway-test + sa + +]]> +

0. Maven Project Configuration - Dependencies

+ +

+ While jOOQ and Flyway could be used in standalone migration scripts, in this tutorial, we'll be using Maven for the standard project setup. You will also find the source code of this tutorial on GitHub at https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-flyway-example, and the full pom.xml file here. +

+ +

+ These are the dependencies that we're using in our Maven configuration: +

+ + + + + org.jooq + jooq + {jooq-version} + + + com.h2database + h2 + 1.4.177 + + + + + log4j + log4j + 1.2.16 + + + org.slf4j + slf4j-log4j12 + 1.7.5 + + + + + junit + junit + 4.11 + test + +]]> + +

0. Maven Project Configuration - Plugins

+ +

+ After the dependencies, let's simply add the Flyway and jOOQ Maven plugins like so. The Flyway plugin: +

+ + + + org.flywaydb + flyway-maven-plugin + 3.0 + + + + + generate-sources + + migrate + + + + + + + ${db.url} + ${db.username} + + filesystem:src/main/resources/db/migration + + + +]]> + +

+ The above Flyway Maven plugin configuration will read and execute all database migration scripts from src/main/resources/db/migration prior to compiling Java source code. While the official Flyway documentation suggests that migrations be done in the compile phase, the jOOQ code generator relies on such migrations having been done prior to code generation. +

+ +

+ After the Flyway plugin, we'll add the jOOQ Maven Plugin. For more details, please refer to the . +

+ + + + + org.jooq + jooq-codegen-maven + ${org.jooq.version} + + + + + generate-sources + + generate + + + + + + + + ${db.url} + ${db.username} + + + + .* + FLYWAY_TEST + + + org.jooq.example.flyway.db.h2 + target/generated-sources/jooq-h2 + + + + +]]> + +

+ This configuration will now read the FLYWAY_TEST schema and reverse-engineer it into the target/generated-sources/jooq-h2 directory, and within that, into the org.jooq.example.flyway.db.h2 package. +

+ +

1. Database increments

+ +

+ Now, when we start developing our database. For that, we'll create database increment scripts, which we put into the src/main/resources/db/migration directory, as previously configured for the Flyway plugin. We'll add these files: +

+ +
    +
  • V1__initialise_database.sql
  • +
  • V2__create_author_table.sql
  • +
  • V3__create_book_table_and_records.sql
  • +
+ +

+ These three scripts model our schema versions 1-3 (note the capital V!). Here are the scripts' contents +

+ + + + + + + +

2. Database migration and 3. Code regeneration

+ +

+ The above three scripts are picked up by Flyway and executed in the order of the versions. This can be seen very simply by executing: +

+ +mvn clean install + +

+ And then observing the log output from Flyway... +

+ +> +[INFO] Migrating schema "PUBLIC" to version 1 +[INFO] Migrating schema "PUBLIC" to version 2 +[INFO] Migrating schema "PUBLIC" to version 3 +[INFO] Successfully applied 3 migrations to schema "PUBLIC" (execution time 00:00.073s).]]> + +

+ ... and from jOOQ on the console: +

+ + + + +

4. Development

+ +

+ Note that all of the previous steps are executed automatically, every time someone adds new migration scripts to the Maven module. For instance, a team member might have committed a new migration script, you check it out, rebuild and get the latest jOOQ-generated sources for your own development or integration-test database. +

+

+ Now, that these steps are done, you can proceed writing your database queries. Imagine the following test case +

+ + result = + DSL.using(c) + .select( + AUTHOR.FIRST_NAME, + AUTHOR.LAST_NAME, + BOOK.ID, + BOOK.TITLE + ) + .from(AUTHOR) + .join(BOOK) + .on(AUTHOR.ID.eq(BOOK.AUTHOR_ID)) + .orderBy(BOOK.ID.asc()) + .fetch(); + + assertEquals(4, result.size()); + assertEquals(asList(1, 2, 3, 4), result.getValues(BOOK.ID)); + } + } +} +]]> + +

Reiterate

+

+ The power of this approach becomes clear once you start performing database modifications this way. Let's assume that the French guy on our team prefers to have things his way: +

+ + + + +

+ They check it in, you check out the new database migration script, run +

+ +mvn clean install + +

+ And then observing the log output: +

+ + + +

+ So far so good, but later on: +

+ + + +

+ When we go back to our Java integration test, we can immediately see that the TITLE column is still being referenced, but it no longer exists: +

+ + result = + DSL.using(c) + .select( + AUTHOR.FIRST_NAME, + AUTHOR.LAST_NAME, + BOOK.ID, + BOOK.TITLE + // ^^^^^ This column no longer exists. We'll have to rename it to LE_TITRE + ) + .from(AUTHOR) + .join(BOOK) + .on(AUTHOR.ID.eq(BOOK.AUTHOR_ID)) + .orderBy(BOOK.ID.asc()) + .fetch(); + + assertEquals(4, result.size()); + assertEquals(asList(1, 2, 3, 4), result.getValues(BOOK.ID)); + } + } +} +]]> + +

Conclusion

+

+ This tutorial shows very easily how you can build a rock-solid development process using Flyway and jOOQ to prevent SQL-related errors very early in your development lifecycle - immediately at compile time, rather than in production! +

+ +

+ Please, visit the Flyway website for more information about Flyway. +

+
+
+ +
+ Using jOOQ with JAX-RS + +

+ In some use-cases, having a lean, single-tier server-side architecture is desirable. Typically, such architectures expose a RESTful API implementing client code and the UI using something like AngularJS. +

+ +

+ In Java, the standard API for RESTful applications is JAX-RS, which is part of JEE 7, along with a standard JSON implementation. But you can use JAX-RS also outside of a JEE container. The following example shows how to set up a simple license server using these technologies: +

+
    +
  • Maven for building and running
  • +
  • Jetty as a lightweight Servlet implementation
  • +
  • Jersey, the JAX-RS (JSR 311 & JSR 339) reference implementation
  • +
  • jOOQ as a data access layer
  • +
+

+ For the example, we'll use a PostgreSQL database. +

+ +

Creating the license server database

+ +

+ We'll keep the example simple and use a LICENSE table to store all license keys and associated information, whereas a LOG_VERIFY table is used to log access to the license server. Here's the DDL: +

+ + + +

+ To make things a bit more interesting (and secure), we'll also push license key generation into the database, by generating it from a stored function as such: +

+ + + +

+ The actual algorithm might be using a secret salt to hash the function arguments. For the sake of a tutorial, a constant string will suffice. +

+ +

Setting up the project

+ +

+ We're going to be setting up the +

+ + + + 4.0.0 + + org.jooq + jooq-webservices + war + 1.0 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.0.2 + + 1.7 + 1.7 + + + + + org.mortbay.jetty + maven-jetty-plugin + 6.1.26 + + manual + stop + 9966 + + + + + + org.jooq + jooq-codegen-maven + {jooq-version} + + + + + + + + + com.sun.jersey + jersey-server + 1.0.2 + + + com.sun.jersey + jersey-json + 1.0.2 + + + com.sun.jersey.contribs + jersey-spring + 1.0.2 + + + javax.servlet + servlet-api + 2.5 + + + + + org.jooq + jooq + {jooq-version} + + + org.postgresql + postgresql + 9.2-1003-jdbc4 + + + log4j + log4j + 1.2.16 + + +]]> + +

+ With the above setup, we're now pretty ready to start developing our license service as a JAX-RS service. +

+ +

The license service class

+ +

+ Once we've run the , we can write the following service class: +

+ +/license/generate generates and returns a new license key. + * + * @param mail The input email address of the licensee. + */ + @GET + @Produces("text/plain") + @Path("/generate") + public String generate( + final @QueryParam("mail") String mail + ) { + return run(new CtxRunnable() { + + @Override + public String run(DSLContext ctx) { + Timestamp licenseDate = new Timestamp(System.currentTimeMillis()); + + // Use the jOOQ query DSL API to generate a license key + return + ctx.insertInto(LICENSE) + .set(LICENSE.LICENSE_, generateKey(inline(licenseDate), inline(mail))) + .set(LICENSE.LICENSE_DATE, licenseDate) + .set(LICENSE.LICENSEE, mail) + .returning() + .fetchOne() + .getLicense(); + } + }); + } + + /** + * /license/verify checks if a given licensee has access to version using a license. + * + * @param request The servlet request from the JAX-RS context. + * @param mail The input email address of the licensee. + * @param license The license used by the licensee. + * @param version The product version being accessed. + */ + @GET + @Produces("text/plain") + @Path("/verify") + public String verify( + final @Context HttpServletRequest request, + final @QueryParam("mail") String mail, + final @QueryParam("license") String license, + final @QueryParam("version") String version + ) { + return run(new CtxRunnable() { + @Override + public String run(DSLContext ctx) { + String v = (version == null || version.equals("")) ? "" : version; + + // Use the jOOQ query DSL API to generate a log entry + return + ctx.insertInto(LOG_VERIFY) + .set(LOG_VERIFY.LICENSE, license) + .set(LOG_VERIFY.LICENSEE, mail) + .set(LOG_VERIFY.REQUEST_IP, request.getRemoteAddr()) + .set(LOG_VERIFY.MATCH, field( + selectCount() + .from(LICENSE) + .where(LICENSE.LICENSEE.eq(mail)) + .and(LICENSE.LICENSE_.eq(license)) + .and(val(v).likeRegex(LICENSE.VERSION)) + .asField().gt(0))) + .set(LOG_VERIFY.VERSION, v) + .returning(LOG_VERIFY.MATCH) + .fetchOne() + .getValue(LOG_VERIFY.MATCH, String.class); + } + }); + } + + // [...] +}]]> + +

+ The INSERT INTO LOG_VERIFY query is actually rather interesting. In plain SQL, it would look like this: +

+ + 0, + :version +) +RETURNING MATCH;]]> + +

+ Apart from the foregoing, the LicenseService also contains a couple of simple utilities: +

+ + + +

Configuring Spring and Jetty

+ +

+ All we need now is to configure Spring... +

+ + + + + +]]> + +

+ ... and Jetty ... +

+ + + + + + contextConfigLocation + classpath:applicationContext.xml + + + org.springframework.web.context.ContextLoaderListener + + + org.springframework.web.context.request.RequestContextListener + + + Jersey Spring Web Application + com.sun.jersey.spi.spring.container.servlet.SpringServlet + + + Jersey Spring Web Application + /* + +]]> + +

+ ... and we're done! We can now run the server with the following command: +

+ + + +

+ Or if you need a custom port: +

+ + + +

Using the license server

+ +

+ You can now use the license server at the following URLs +

+ + license-key + +http://localhost:8088/jooq-jax-rs-example/license/verify?mail=test@example.com&license=license-key&version=3.2.0 +-> true + +http://localhost:8088/jooq-jax-rs-example/license/verify?mail=test@example.com&license=wrong&version=3.2.0 +-> false]]> + +

+ Let's verify what happened, in the database: +

+ + + +

Downloading the complete example

+ +

+ The complete example can be downloaded for free and under the terms of the Apache Software License 2.0 from here:
+ https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-jax-rs-example +

+
+
+ +
+ A simple web application with jOOQ + +

Feel free to contribute a tutorial!

+
+
+
+
+ +
+ jOOQ and Java 8 + +

+ Java 8 has introduced a great set of enhancements, among which lambda expressions and the new Streams API. These new constructs align very well with jOOQ's fluent API as can be seen in the following examples: +

+ +

+ jOOQ and lambda expressions +

+

+ jOOQ's API is fully Java-8-ready, which basically means that it is a SAM (Single Abstract Method) type, which can be instanciated using a lambda expression. Consider this example: +

+ + new Schema( + rs.getValue("SCHEMA_NAME", String.class), + rs.getValue("IS_DEFAULT", boolean.class) + )) + + // ... and then profit from the new Collection methods + .forEach(System.out::println); +}]]> + +

+ The above example shows how jOOQ's method can receive a lambda expression that implements to map from jOOQ to your custom types. +

+ +

+ jOOQ and the Streams API +

+

+ jOOQ's type extends , which opens up access to a variety of new Java features in Java 8. The following example shows how easy it is to transform a jOOQ Result containing INFORMATION_SCHEMA meta data to produce DDL statements: +

+ + r.getValue(COLUMNS.TABLE_NAME), + LinkedHashMap::new, + mapping( + r -> new Column( + r.getValue(COLUMNS.COLUMN_NAME), + r.getValue(COLUMNS.TYPE_NAME) + ), + toList() + ) + )) + .forEach( + (table, columns) -> { + // Just emit a CREATE TABLE statement + System.out.println( + "CREATE TABLE " + table + " ("); + + // Map each "Column" type into a String + // containing the column specification, + // and join them using comma and + // newline. Done! + System.out.println( + columns.stream() + .map(col -> " " + col.name + + " " + col.type) + .collect(Collectors.joining(",\n")) + ); + + System.out.println(");"); + } + );]]> + +

+ The above example is explained more in depth in this blog post: http://blog.jooq.org/2014/04/11/java-8-friday-no-more-need-for-orms/. For more information about Java 8, consider these resources: +

+ + +
+
+ +
+ jOOQ and JavaFX + +

+ One of the major improvements of Java 8 is the introduction of JavaFX into the JavaSE. With jOOQ and , it is now very easy and idiomatic to transform SQL results into JavaFX XYChart.Series or other, related objects: +

+ +

+ Creating a bar chart from a jOOQ Result +

+

+ As we've seen in the previous , jOOQ integrates seamlessly with Java 8's Streams API. The fluent style can be maintained throughout the data transformation chain. +

+

+ In this example, we're going to use Open Data from the world bank to show a comparison of countries GDP and debts: +

+ + + +

+ Once this data is set up (e.g. in an H2 or PostgreSQL database), we'll run jOOQ's and implement the following code to display our chart: +

+ + bc = new BarChart(xAxis, yAxis); +bc.setTitle("Government Debt"); +bc.getData().addAll( + + // SQL data transformation, executed in the database + // ------------------------------------------------- + DSL.using(connection) + .select( + COUNTRIES.YEAR, + COUNTRIES.CODE, + COUNTRIES.GOVT_DEBT) + .from(COUNTRIES) + .join( + table( + select(COUNTRIES.CODE, avg(COUNTRIES.GOVT_DEBT).as("avg")) + .from(COUNTRIES) + .groupBy(COUNTRIES.CODE) + ).as("c1") + ) + .on(COUNTRIES.CODE.eq(field(name("c1", COUNTRIES.CODE.getName()), String.class))) + + // order countries by their average projected value + .orderBy( + field(name("avg")), + COUNTRIES.CODE, + COUNTRIES.YEAR) + + // The result produced by the above statement looks like this: + // +----+----+---------+ + // |year|code|govt_debt| + // +----+----+---------+ + // |2009|RU | 8.70| + // |2010|RU | 9.10| + // |2011|RU | 9.30| + // |2012|RU | 9.40| + // |2009|CA | 51.30| + // +----+----+---------+ + + // Java data transformation, executed in application memory + // -------------------------------------------------------- + + // Group results by year, keeping sort order in place + .fetchGroups(COUNTRIES.YEAR) + + // Stream>>> + .entrySet() + .stream() + + // Map each entry into a { Year -> Projected value } series + .map(entry -> new XYChart.Series<>( + entry.getKey().toString(), + observableArrayList( + + // Map each country record into a chart Data object + entry.getValue() + .map(country -> new XYChart.Data( + country.getValue(COUNTRIES.CODE), + country.getValue(COUNTRIES.GOVT_DEBT) + )) + ) + )) + .collect(toList()) +);]]> + +

+ The above example uses basic SQL-92 syntax where the countries are ordered using aggregate information from a , which is supported in all databases. If you're using a database that supports , e.g. PostgreSQL or any commercial database, you could have also written a simpler query like this:00 +

+ + + +

+ When executed, we'll get nice-looking bar charts like these: +
+ jOOQ and JavaFX Example +

+ +

+ The complete example can be downloaded and run from GitHub: +
+ https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-javafx-example +

+
+
+ +
+ jOOQ and Nashorn + +

+ With Java 8 and the new built-in JavaScript engine Nashorn, a whole new ecosystem of software can finally make easy use of jOOQ in server-side JavaScript. A very simple example can be seen here: +

+ + + +

+ More details about how to use jOOQ, JDBC, and SQL with Nashorn can be seen here. +

+
+
+ +
+ jOOQ and Scala + +

+ As any other library, jOOQ can be easily used in Scala, taking advantage of the many Scala language features such as for example: +

+
    +
  • Optional "." to dereference methods from expressions
  • +
  • Optional "(" and ")" to delimit method argument lists
  • +
  • Optional ";" at the end of a Scala statement
  • +
  • Type inference using "var" and "val" keywords
  • +
  • Lambda expressions and for-comprehension syntax for record iteration and data type conversion
  • +
+ +

+ But jOOQ also leverages other useful Scala features, such as +

+
    +
  • implicit defs for operator overloading
  • +
  • Scala Macros (soon to come)
  • +
+ +

+ All of the above heavily improve jOOQ's querying DSL API experience for Scala developers. +

+ +

+ A short example jOOQ application in Scala might look like this: +

+ + 2) // Using the olerloaded "<>" operator + or (BOOK.TITLE in ("O Alquimista", "Brida")) // Neat IN predicate expression + fetch // + ) { // + println(r) // + } // + } // +}]]> + +

+ For more details about jOOQ's Scala integration, please refer to the manual's section about . +

+
+
+ + +
+ jOOQ and Groovy + +

+ As any other library, jOOQ can be easily used in Groovy, taking advantage of the many Groovy language features such as for example: +

+
    +
  • Optional ";" at the end of a Groovy statement
  • +
  • Type inference for local variables
  • +
+ +

+ A short example jOOQ application in Groovy might look like this: +

+ + println( + "${r.getValue(a.FIRST_NAME)} " + + "${r.getValue(a.LAST_NAME)} " + + "has written ${r.getValue(b.TITLE)}" + ) + } as RecordHandler)]]> + +

+ Note that while Groovy supports some means of operator overloading, we think that these means should be avoided in a jOOQ integration. For instance, a + b in Groovy maps to a formal a.plus(b) method invocation, and jOOQ provides the required synonyms in its API to help you write such expressions. Nonetheless, Groovy only offers little typesafety, and as such, operator overloading can lead to many runtime issues. +

+

+ Another caveat of Groovy operator overloading is the fact that operators such as == or >= map to a.equals(b), a.compareTo(b) == 0, a.compareTo(b) >= 0 respectively. This behaviour does not make sense in a fluent API such as jOOQ. +

+
+
+ + +
+ jOOQ and Kotlin + +

+ As any other library, jOOQ can be easily used in Kotlin, taking advantage of the many Kotlin language features such as for example: +

+
    +
  • Optional ";" at the end of a Kotlin statement
  • +
  • Type inference for local variables
  • +
+ +

+ A short example jOOQ application in Kotlin might look like this: +

+ +) { + + val properties = Properties(); + properties.load(Properties::class.java.getResourceAsStream("/config.properties")); + + DSL.using( + properties.getProperty("db.url"), + properties.getProperty("db.username"), + properties.getProperty("db.password") + + // The below "use" method is an adapted version of the one in kotlin.io + // See also: https://github.com/JetBrains/kotlin/pull/807 + ).use { + val ctx = it + val a = AUTHOR + val b = BOOK + + ctx.select(a.FIRST_NAME, a.LAST_NAME, b.TITLE) + .from(a) + .join(b).on(a.ID.eq(b.AUTHOR_ID)) + .orderBy(1, 2, 3) + .forEach { + println("${it[b.TITLE]} by ${it[a.FIRST_NAME]} ${it[a.LAST_NAME]}") + } + } +}]]> + +

+ Note that Kotlin supports some means of operator overloading. For instance, a + b in Kotlin maps to a formal a.plus(b) method invocation, and jOOQ provides the required synonyms in its API to help you write such expressions. +

+ +

+ One particularly nice language feature is the fact that [square brackets] allow for accessing any object's contents via get() and set() methods. Instead of using the above value1(), value2(), and value3() methods, we could also iterate as such: +

+ + + +

+ A caveat of Kotlin operator overloading is the fact that operators such as == or >= map to a.equals(b), a.compareTo(b) == 0, a.compareTo(b) >= 0 respectively. This behaviour does not make sense in a fluent API such as jOOQ. +

+
+
+ +
+ jOOQ and NoSQL + +

+ jOOQ users often get excited about jOOQ's intuitive API and would then wish for NoSQL support. +

+

+ There are a variety of NoSQL databases that implement some sort of proprietary query language. Some of these query languages even look like SQL. Examples are JCR-SQL2, CQL (Cassandra Query Language), Cypher (Neo4j's Query Language), SOQL (Salesforce Query Language) and many more. +

+

+ Mapping the jOOQ API onto these alternative query languages would be a very poor fit and a leaky abstraction. We believe in the power and expressivity of the SQL standard and its various dialects. Databases that extend this standard too much, or implement it not thoroughly enough are often not suitable targets for jOOQ. It would be better to build a new, dedicated API for just that one particular query language. +

+

+ jOOQ is about SQL, and about SQL alone. Read more about our visions in the . +

+
+
+ +
+ Dependencies + +

+ Dependencies are a big hassle in modern software. Many libraries depend on other, non-JDK library parts that come in different, incompatible versions, potentially causing trouble in your runtime environment. jOOQ has no external dependencies on any third-party libraries. +

+

+ However, the above rule has some exceptions: +

+
    +
  • are referenced as "optional dependencies". jOOQ tries to find slf4j or log4j on the classpath. If it fails, it will use the
  • +
  • Oracle ojdbc types used for array creation are loaded using reflection. The same applies to Postgres PG* types.
  • +
  • Small libraries with compatible licenses are incorporated into jOOQ. These include jOOR, jOOU, parts of OpenCSV, json simple, parts of commons-lang
  • +
  • javax.persistence and javax.validation will be needed if you activate the relevant
  • +
+
+
+ +
+ Build your own + +

+ In order to build jOOQ (Open Source Edition) yourself, please download the sources from https://github.com/jOOQ/jOOQ and use Maven to build jOOQ, preferably in Eclipse. The jOOQ Open Source Edition requires Java 8+ to compile and run. The commercial jOOQ Editions require Java 8+ or Java 6+ to compile and run, depending on the distribution. +

+

+ Some useful hints to build jOOQ yourself: +

+
    +
  • Get the latest version of Git or EGit
  • +
  • Get the latest version of Maven or M2E
  • +
  • Check out the jOOQ sources from https://github.com/jOOQ/jOOQ
  • +
  • Optionally, import Maven artefacts into an Eclipse workspace using the following command (see the maven-eclipse-plugin documentation for details): +
      +
    • mvn eclipse:eclipse
    • +
    +
  • +
  • Build the jooq-parent artefact by using any of these commands: +
      +
    • mvn clean package
      create .jar files in ${project.build.directory}
    • +
    • mvn clean install
      install the .jar files in your local repository (e.g. ~/.m2)
    • +
    • mvn clean {goal} -Dmaven.test.skip=true
      don't run unit tests when building artefacts
    • +
    +
  • +
+
+
+ +
+ jOOQ and backwards-compatibility + +

+ jOOQ follows the rules of semantic versioning according to http://semver.org quite strictly. Those rules impose a versioning scheme [X].[Y].[Z] that can be summarised as follows: +

+
    +
  • If a patch release includes bugfixes, performance improvements and API-irrelevant new features, [Z] is incremented by one.
  • +
  • If a minor release includes backwards-compatible, API-relevant new features, [Y] is incremented by one and [Z] is reset to zero.
  • +
  • If a major release includes backwards-incompatible, API-relevant new features, [X] is incremented by one and [Y], [Z] are reset to zero.
  • +
+ +

jOOQ's understanding of backwards-compatibility

+

+ Backwards-compatibility is important to jOOQ. You've chosen jOOQ as a strategic SQL engine and you don't want your SQL to break. That is why there is at most one major release per year, which changes only those parts of jOOQ's API and functionality, which were agreed upon on the user group. During the year, only minor releases are shipped, adding new features in a backwards-compatible way +

+

+ However, there are some elements of API evolution that would be considered backwards-incompatible in other APIs, but not in jOOQ. As discussed later on in the section about , much of jOOQ's API is indeed an internal domain-specific language implemented mostly using Java interfaces. Adding language elements to these interfaces means any of these actions: +

+
    +
  • Adding methods to the interface
  • +
  • Overloading methods for convenience
  • +
  • Changing the type hierarchy of interfaces
  • +
+ +

+ It becomes obvious that it would be impossible to add new language elements (e.g. new , new ) to the API without breaking any client code that actually implements those interfaces. Hence, the following rule should be observed: +

+ +

+ jOOQ's DSL interfaces should not be implemented by client code! Extend only those extension points that are explicitly documented as "extendable" (e.g. ) +

+ +

jOOQ-codegen and jOOQ-meta

+

+ While a reasonable amount of care is spent to maintain these two modules under the rules of semantic versioning, it may well be that minor releases introduce backwards-incompatible changes. This will be announced in the respective release notes and should be the exception. +

+
+
+
+
+ +
+ SQL building + +

+ SQL is a declarative language that is hard to integrate into procedural, object-oriented, functional or any other type of programming languages. jOOQ's philosophy is to give SQL the credit it deserves and integrate SQL itself as an "internal domain specific language" directly into Java. +

+

+ With this philosophy in mind, SQL building is the main feature of jOOQ. All other features (such as and ) are mere convenience built on top of jOOQ's SQL building capabilities. +

+

+ This section explains all about the various syntax elements involved with jOOQ's SQL building capabilities. For a complete overview of all syntax elements, please refer to the manual's sections about as well as +

+
+ + + + + + +
+ The query DSL type + +

+ jOOQ exposes a lot of interfaces and hides most implementation facts from client code. The reasons for this are: +

+
    +
  • Interface-driven design. This allows for modelling queries in a fluent API most efficiently
  • +
  • Reduction of complexity for client code.
  • +
  • API guarantee. You only depend on the exposed interfaces, not concrete (potentially dialect-specific) implementations.
  • +
+

+ The class is the main class from where you will create all jOOQ objects. It serves as a static factory for , (or "fields"), and many other . +

+ +

The static query DSL API

+

+ With jOOQ 2.0, static factory methods have been introduced in order to make client code look more like SQL. Ideally, when working with jOOQ, you will simply static import all methods from the DSL class: +

+ +import static org.jooq.impl.DSL.*; + +

+ Note, that when working with Eclipse, you could also add the DSL to your favourites. This will allow to access functions even more fluently: +

+concat(trim(FIRST_NAME), trim(LAST_NAME)); + +// ... which is in fact the same as: +DSL.concat(DSL.trim(FIRST_NAME), DSL.trim(LAST_NAME)); +
+ + +
+ DSL subclasses + +

+ There are a couple of subclasses for the general query DSL. Each SQL dialect has its own dialect-specific DSL. For instance, if you're only using the MySQL dialect, you can choose to reference the MySQLDSL instead of the standard DSL: +

+

+ The advantage of referencing a dialect-specific DSL lies in the fact that you have access to more proprietary RDMBS functionality. This may include: +

+
    +
  • MySQL's encryption functions
  • +
  • PL/SQL constructs, pgplsql, or any other dialect's ROUTINE-language (maybe in the future)
  • +
+
+
+
+
+ + + + + + + + + + +
+ The DSLContext class + +

+ DSLContext references a , an object that configures jOOQ's behaviour when executing queries (see for more details). Unlike the static DSL, the DSLContext allow for creating that are already "configured" and ready for execution. +

+ +

Fluent creation of a DSLContext object

+

+ The DSLContext object can be created fluently from the : +

+ + + +

+ If you do not have a reference to a pre-existing Configuration object (e.g. created from ), the various overloaded DSL.using() methods will create one for you. +

+ +

Contents of a Configuration object

+

+ A Configuration can be supplied with these objects: +

+
    +
  • : The dialect of your database. This may be any of the currently supported database types (see for more details)
  • +
  • : An optional runtime configuration (see for more details)
  • +
  • : An optional reference to a provider class that can provide execute listeners to jOOQ (see for more details)
  • +
  • : An optional reference to a provider class that can provide record mappers to jOOQ (see for more details)
  • +
  • + Any of these: +
      +
    • : An optional JDBC Connection that will be re-used for the whole lifecycle of your Configuration (see for more details). For simplicity, this is the use-case referenced from this manual, most of the time.
    • +
    • : An optional JDBC DataSource that will be re-used for the whole lifecycle of your Configuration. If you prefer using DataSources over Connections, jOOQ will internally fetch new Connections from your DataSource, conveniently closing them again after query execution. This is particularly useful in J2EE or Spring contexts (see for more details)
    • +
    • : A custom abstraction that is used by jOOQ to "acquire" and "release" connections. jOOQ will internally "acquire" new Connections from your ConnectionProvider, conveniently "releasing" them again after query execution. (see for more details)
    • +
    +
  • +
+ +

+ Wrapping a Configuration object, a DSLContext can construct , for later . An example is given here: +

+ select = create.selectOne(); + +// Using the internally referenced Configuration, the select statement can now be executed: +Result result = select.fetch();]]> + +

+ Note that you do not need to keep a reference to a DSLContext. You may as well inline your local variable, and fluently execute a SQL statement as such: +

+ + result = +DSL.using(connection, dialect) + .select() + .from(BOOK) + .where(BOOK.TITLE.like("Animal%")) + .fetch();]]> +
+ + +
+ SQL Dialect + +

+ While jOOQ tries to represent the SQL standard as much as possible, many features are vendor-specific to a given database and to its "SQL dialect". jOOQ models this using the enum type. +

+

+ The SQL dialect is one of the main attributes of a . Queries created from DSLContexts will assume dialect-specific behaviour when and . +

+

+ Some parts of the jOOQ API are officially supported only by a given subset of the supported SQL dialects. For instance, the , which is supported by the Oracle and CUBRID databases, is annotated with a annotation, as such: +

+CONNECT BY clause to the query + */ +@Support({ SQLDialect.CUBRID, SQLDialect.}) +SelectConnectByConditionStep connectBy(Condition condition);]]> + +

+ jOOQ API methods which are not annotated with the annotation, or which are annotated with the Support annotation, but without any SQL dialects can be safely used in all SQL dialects. An example for this is the factory method: +

+ select(Field... fields);]]> + +

jOOQ's SQL clause emulation capabilities

+

+ The aforementioned Support annotation does not only designate, which databases natively support a feature. It also indicates that a feature is emulated by jOOQ for some databases lacking this feature. An example of this is the , a predicate syntax defined by SQL:1999 and implemented only by H2, HSQLDB, and Postgres: +

+ + + +

+ Nevertheless, the IS DISTINCT FROM predicate is supported by jOOQ in all dialects, as its semantics can be expressed with an equivalent . For more details, see the manual's section about the . +

+ +

jOOQ and the Oracle SQL dialect

+

+ Oracle SQL is much more expressive than many other SQL dialects. It features many unique keywords, clauses and functions that are out of scope for the SQL standard. Some examples for this are +

+
    +
  • The , for hierarchical queries
  • +
  • The keyword for creating PIVOT tables
  • +
  • , as described in the section about
  • +
  • Advanced analytical functions as described in the section about
  • +
+ +

+ jOOQ has a historic affinity to Oracle's SQL extensions. If something is supported in Oracle SQL, it has a high probability of making it into the jOOQ API +

+
+
+ +
+ SQL Dialect Family + +

+ In jOOQ 3.1, the notion of a SQLDialect.family() was introduced, in order to group several similar into a common family. An example for this is SQL Server, which is supported by jOOQ in various versions: +

+ +
    +
  • : The "version-less" SQL Server version. This always maps to the latest supported version of SQL Server
  • +
  • : The SQL Server version 2012
  • +
  • : The SQL Server version 2008
  • +
+ +

+ In the above list, SQLSERVER is both a dialect and a family of three dialects. This distinction is used internally by jOOQ to distinguish whether to use the clause (SQL Server 2012), or whether to emulate it using ROW_NUMBER() OVER() (SQL Server 2008). +

+
+
+ +
+ Connection vs. DataSource + +

Interact with JDBC Connections

+

+ While you can use jOOQ for only, you can also run queries against a JDBC . Internally, jOOQ creates or objects from such a Connection, in order to execute statements. The normal operation mode is to provide a with a JDBC Connection, whose lifecycle you will control yourself. This means that jOOQ will not actively close connections, rollback or commit transactions. +

+

+ Note, in this case, jOOQ will internally use a , which you can reference directly if you prefer that. The DefaultConnectionProvider exposes various transaction-control methods, such as commit(), rollback(), etc. +

+ +

Interact with JDBC DataSources

+

+ If you're in a J2EE or Spring context, however, you may wish to use a instead. Connections obtained from such a DataSource will be closed after query execution by jOOQ. The semantics of such a close operation should be the returning of the connection into a connection pool, not the actual closing of the underlying connection. Typically, this makes sense in an environment using distributed JTA transactions. An example of using DataSources with jOOQ can be seen in the tutorial section about . +

+

+ Note, in this case, jOOQ will internally use a , which you can reference directly if you prefer that. +

+ +

Inject custom behaviour

+

+ If your specific environment works differently from any of the above approaches, you can inject your own custom implementation of a ConnectionProvider into jOOQ. This is the API contract you have to fulfil: +

+ + + +

+ Note that acquire() should always return the same Connection until this connection is returned via release() +

+
+
+ +
+ Custom data + +

+ In advanced use cases of integrating your application with jOOQ, you may want to put custom data into your , which you can then access from your... +

+ +
    +
  • +
  • +
+ +

+ Here is an example of how to use the custom data API. Let's assume that you have written an , that prevents INSERT statements, when a given flag is set to true: +

+ + +

+ See the manual's section about to learn more about how to implement an ExecuteListener. +

+ +

+ Now, the above listener can be added to your , but you will also need to pass the flag to the Configuration, in order for the listener to work: +

+ + + +

+ Using the data() methods, you can store and retrieve custom data in your Configurations. +

+
+
+ +
+ Custom ExecuteListeners + +

+ ExecuteListeners are a useful tool to... +

+ +
    +
  • implement custom logging
  • +
  • apply triggers written in Java
  • +
  • collect query execution statistics
  • +
+ +

+ ExecuteListeners are hooked into your by returning them from an : +

+ + + +

+ See the manual's section about to see examples of such listener implementations. +

+
+
+ +
+ Custom Settings + +

+ The jOOQ Configuration allows for some optional configuration elements to be used by advanced users. The class is a JAXB-annotated type, that can be provided to a Configuration in several ways: +

+
    +
  • In the DSLContext constructor (DSL.using()). This will override default settings below
  • +
  • in the constructor. This will override default settings below
  • +
  • From a location specified by a JVM parameter: -Dorg.jooq.settings
  • +
  • From the classpath at /jooq-settings.xml
  • +
  • From the settings defaults, as specified in http://www.jooq.org/xsd/jooq-runtime-{runtime-xsd-version}.xsd
  • +
+

Example

+

+ For example, if you want to indicate to jOOQ, that it should inline all bind variables, and execute static instead of binding its variables to , you can do so by creating the following DSLContext: +

+ +

+ Subsequent sections of the manual contain some more in-depth explanations about these settings and what is controlled by these settings: +

+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+ +

All Settings

+ +

+ This section of the manual explains all the available Settings flags as available from the XSD specification. +

+ + + + + false + + + ... + + + LOWER + + + UPPER + + + false + + + INDEXED + + + PREPARED_STATEMENT + + + true + + + false + + + true + + + false +]]> + + +

More details

+ +

+ Please refer to the jOOQ runtime configuration XSD for more details:
+ http://www.jooq.org/xsd/jooq-runtime-{runtime-xsd-version}.xsd +

+
+
+ +
+ Runtime schema and table mapping + +

Mapping your DEV schema to a productive environment

+

+ You may wish to design your database in a way that you have several instances of your schema. This is useful when you want to cleanly separate data belonging to several customers / organisation units / branches / users and put each of those entities' data in a separate database or schema. +

+ +

+ In our AUTHOR example this would mean that you provide a book reference database to several companies, such as My Book World and Books R Us. In that case, you'll probably have a schema setup like this: +

+
    +
  • DEV: Your development schema. This will be the schema that you base code generation upon, with jOOQ
  • +
  • MY_BOOK_WORLD: The schema instance for My Book World
  • +
  • BOOKS_R_US: The schema instance for Books R Us
  • +
+ +

Mapping DEV to MY_BOOK_WORLD with jOOQ

+

+ When a user from My Book World logs in, you want them to access the MY_BOOK_WORLD schema using classes generated from DEV. This can be achieved with the class, that you can equip your Configuration's with. Take the following example: +

+ +Settings settings = new Settings() + .withRenderMapping(new RenderMapping() + .withSchemata( + new MappedSchema().withInput("DEV") + .withOutput("MY_BOOK_WORLD"))); + +// Add the settings to the DSLContext +DSLContext create = DSL.using(connection, SQLDialect.ORACLE, settings); + +// Run queries with the "mapped" Configuration +create.selectFrom(AUTHOR).fetch(); + +

+ The query executed with a Configuration equipped with the above mapping will in fact produce this SQL statement: +

+ +SELECT * FROM MY_BOOK_WORLD.AUTHOR +

+ Even if AUTHOR was generated from DEV. +

+ +

Mapping several schemata

+

+ Your development database may not be restricted to hold only one DEV schema. You may also have a LOG schema and a MASTER schema. Let's say the MASTER schema is shared among all customers, but each customer has their own LOG schema instance. Then you can enhance your RenderMapping like this (e.g. using an XML configuration file): +

+ + + + + + DEV + MY_BOOK_WORLD + + + LOG + MY_BOOK_WORLD_LOG + + + +]]> + +

+ Note, you can load the above XML file like this: +

+ +Settings settings = JAXB.unmarshal(new File("jooq-runtime.xml"), Settings.class); + +

+ This will map generated classes from DEV to MY_BOOK_WORLD, from LOG to MY_BOOK_WORLD_LOG, but leave the MASTER schema alone. Whenever you want to change your mapping configuration, you will have to create a new Configuration. +

+ + +

Using a default schema

+

+ If you wish not to render any schema name at all, use the following Settings property for this: +

+ + +Settings settings = new Settings() + .withRenderSchema(false); + +// Add the settings to the Configuration +DSLContext create = DSL.using(connection, SQLDialect.ORACLE, settings); + +// Run queries that omit rendering schema names +create.selectFrom(AUTHOR).fetch(); + +

Mapping of tables

+

+ Not only schemata can be mapped, but also tables. If you are not the owner of the database your application connects to, you might need to install your schema with some sort of prefix to every table. In our examples, this might mean that you will have to map DEV.AUTHOR to something MY_BOOK_WORLD.MY_APP__AUTHOR, where MY_APP__ is a prefix applied to all of your tables. This can be achieved by creating the following mapping: +

+ +Settings settings = new Settings() + .withRenderMapping(new RenderMapping() + .withSchemata( + new MappedSchema().withInput("DEV") + .withOutput("MY_BOOK_WORLD") + .withTables( + new MappedTable().withInput("AUTHOR") + .withOutput("MY_APP__AUTHOR")))); + +// Add the settings to the Configuration +DSLContext create = DSL.using(connection, SQLDialect.ORACLE, settings); + +// Run queries with the "mapped" configuration +create.selectFrom(AUTHOR).fetch(); + +

+ The query executed with a Configuration equipped with the above mapping will in fact produce this SQL statement: +

+ +SELECT * FROM MY_BOOK_WORLD.MY_APP__AUTHOR + +

+ Table mapping and schema mapping can be applied independently, by specifying several MappedSchema entries in the above configuration. jOOQ will process them in order of appearance and map at first match. Note that you can always omit a MappedSchema's output value, in case of which, only the table mapping is applied. If you omit a MappedSchema's input value, the table mapping is applied to all schemata! +

+ +

Using regular expressions

+

+ All of the above examples were using 1:1 constant name mappings where the input and output schema or table names are fixed by the configuration. With jOOQ 3.8, regular expression can be used as well for mapping, for example: +

+ +Settings settings = new Settings() + .withRenderMapping(new RenderMapping() + .withSchemata( + new MappedSchema().withInputExpression(Pattern.compile("DEV_(.*)")) + .withOutput("PROD_$1") + .withTables( + new MappedTable().withInputExpression(Pattern.compile("DEV_(.*)")) + .withOutput("PROD_$1")))); + +// Add the settings to the Configuration +DSLContext create = DSL.using(connection, SQLDialect.ORACLE, settings); + +// Run queries with the "mapped" configuration +create.selectFrom(AUTHOR).fetch(); + +

+ The only difference to the constant version is that the input field is replaced by the inputExpression field of type , in case of which the meaning of the output field is a pattern replacement, not a constant replacement. +

+ + +

Hard-wiring mappings at code-generation time

+

+ Note that the manual's section about explains how you can hard-wire your schema mappings at code generation time +

+
+
+
+
+ +
+ SQL Statements (DML) + +

+ jOOQ currently supports 5 types of SQL statements. All of these statements are constructed from a DSLContext instance with an optional . If supplied with a Connection or DataSource, they can be executed. Depending on the , executed queries can return results. +

+
+ + +
+ jOOQ's DSL and model API + +

+ jOOQ ships with its own DSL (or Domain Specific Language) that emulates SQL in Java. This means, that you can write SQL statements almost as if Java natively supported it, just like .NET's C# does with LINQ to SQL. +

+

+ Here is an example to illustrate what that means: +

+ + 1920 + AND a.first_name = 'Paulo' + ORDER BY b.title]]> + result = +create.select() + .from(AUTHOR.as("a")) + .join(BOOK.as("b")).on(a.ID.equal(b.AUTHOR_ID)) + .where(a.YEAR_OF_BIRTH.greaterThan(1920) + .and(a.FIRST_NAME.equal("Paulo"))) + .orderBy(b.TITLE) + .fetch();]]> + +

+ We'll see how the aliasing works later in the section about +

+ +

jOOQ as an internal domain specific language in Java (a.k.a. the DSL API)

+

+ Many other frameworks have similar APIs with similar feature sets. Yet, what makes jOOQ special is its informal modelling a unified SQL dialect suitable for many vendor-specific dialects, and implementing that BNF notation as a hierarchy of interfaces in Java. This concept is extremely powerful, when with syntax completion. Not only can you code much faster, your SQL code will be compile-checked to a certain extent. An example of a DSL query equivalent to the previous one is given here: +

+ + result = create.select() + .from(AUTHOR) + .join(BOOK).on(BOOK.AUTHOR_ID.equal(AUTHOR.ID)) + .fetch();]]> + +

+ Unlike other, simpler frameworks that use "fluent APIs" or "method chaining", jOOQ's BNF-based interface hierarchy will not allow bad query syntax. The following will not compile, for instance: +

+ result = create.select() + .join(BOOK).on(BOOK.AUTHOR_ID.equal(AUTHOR.ID)) + // ^^^^ "join" is not possible here + .from(AUTHOR) + .fetch(); + +Result result = create.select() + .from(AUTHOR) + .join(BOOK) + .fetch(); + // ^^^^^ "on" is missing here + +Result result = create.select(rowNumber()) + // ^^^^^^^^^ "over()" is missing here + .from(AUTHOR) + .fetch(); + +Result result = create.select() + .from(AUTHOR) + .where(AUTHOR.ID.in(select(BOOK.TITLE).from(BOOK))) + // ^^^^^^^^^^^^^^^^^^ + // AUTHOR.ID is of type Field but subselect returns Record1 + .fetch(); + +Result result = create.select() + .from(AUTHOR) + .where(AUTHOR.ID.in(select(BOOK.AUTHOR_ID, BOOK.ID).from(BOOK))) + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // AUTHOR.ID is of degree 1 but subselect returns Record2 + .fetch();]]> + +

History of SQL building and incremental query building (a.k.a. the model API)

+

+ Historically, jOOQ started out as an object-oriented SQL builder library like any other. This meant that all queries and their syntactic components were modeled as so-called , which delegate and to child components. This part of the API will be referred to as the model API (or non-DSL API), which is still maintained and used internally by jOOQ for incremental query building. An example of incremental query building is given here: +

+ + query = create.selectQuery(); +query.addFrom(AUTHOR); + +// Join books only under certain circumstances +if (join) { + query.addJoin(BOOK, BOOK.AUTHOR_ID.equal(AUTHOR.ID)); +} + +Result result = query.fetch();]]> + +

+ This query is equivalent to the one shown before using the DSL syntax. In fact, internally, the DSL API constructs precisely this SelectQuery object. Note, that you can always access the SelectQuery object to switch between DSL and model APIs: +

+ + select = create.select().from(AUTHOR); + +// Add the JOIN clause on the internal QueryObject representation +SelectQuery query = select.getQuery(); +query.addJoin(BOOK, BOOK.AUTHOR_ID.equal(AUTHOR.ID));]]> + +

Mutability

+

+ Note, that for historic reasons, the DSL API mixes mutable and immutable behaviour with respect to the internal representation of the being constructed. While creating , (such as functions) assumes immutable behaviour, creating does not. In other words, the following can be said: +

+ + s1 = select(); +SelectJoinStep s2 = s1.from(BOOK); +SelectJoinStep s3 = s1.from(AUTHOR); + +// The following can be said +s1 == s2; // The internal object is always the same +s2 == s3; // The internal object is always the same]]> + +

+ On the other hand, beware that you can always extract and modify from any QueryPart. +

+
+
+ +
+ The WITH clause + +

+ The SQL:1999 standard specifies the WITH clause to be an optional clause for the , in order to specify common table expressions (also: CTE). Many other databases (such as PostgreSQL, SQL Server) also allow for using common table expressions also in other DML clauses, such as the , , , or . +

+ +

+ When using common table expressions with jOOQ, there are essentially two approaches: +

+
    +
  • Declaring and assigning common table expressions explicitly to
  • +
  • Inlining common table expressions into a
  • +
+ +

Explicit common table expressions

+

+ The following example makes use of to construct common table expressions, which can then be supplied to a WITH clause or a FROM clause of a : +

+ + + + + + +

+ The above expression can be assigned to a variable in Java and then be used to create a full : +

+ + + +> t1 = + name("t1").fields("f1", "f2").as(select(val(1), val("a"))); +CommonTableExpression> t2 = + name("t2").fields("f3", "f4").as(select(val(2), val("b"))); + +Result result2 = +create.with(t1) + .with(t2) + .select( + t1.field("f1").add(t2.field("f3")).as("add"), + t1.field("f2").concat(t2.field("f4")).as("concat")) + .from(t1, t2) + .fetch(); +]]> + + +

+ Note that the type extends the commonly used type, and can thus be used wherever a table can be used. +

+ +

Inlined common table expressions

+ +

+ If you're just operating on , you may not need to keep intermediate references to such common table expressions. An example of such usage would be this: +

+ + + + + + +

Recursive common table expressions

+ +

+ The various SQL dialects do not agree on the use of RECURSIVE when writing recursive common table expressions. When using jOOQ, always use the or methods, and jOOQ will render the RECURSIVE keyword, if needed. +

+
+
+ +
+ The SELECT statement + +

+ When you don't just perform (i.e. SELECT * FROM your_table WHERE ID = ?), you're usually generating new record types using custom projections. With jOOQ, this is as intuitive, as if using SQL directly. A more or less complete example of the "standard" SQL syntax, plus some extensions, is provided by a query like this: +

+ +

SELECT from a complex table expression

+ + '2008-01-01' +GROUP BY AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME + HAVING COUNT(*) > 5 +ORDER BY AUTHOR.LAST_NAME ASC NULLS FIRST + LIMIT 2 + OFFSET 1 + FOR UPDATE]]> + + + +

+ Details about the various clauses of this query will be provided in subsequent sections. +

+ +

SELECT from single tables

+

+ A very similar, but limited API is available, if you want to select from single tables in order to retrieve . The decision, which type of select to create is already made at the very first step, when you create the SELECT statement with the DSL or DSLContext types: +

+ + SelectWhereStep selectFrom(Table table);]]> +

+ As you can see, there is no way to further restrict/project the selected fields. This just selects all known TableFields in the supplied Table, and it also binds <R extends Record> to your Table's associated Record. An example of such a Query would then be: +

+ + +

+ The "reduced" SELECT API is limited in the way that it skips DSL access to any of these clauses: +

+
    +
  • +
  • +
+

+ In most parts of this manual, it is assumed that you do not use the "reduced" SELECT API. For more information about the simple SELECT API, see the manual's section about . +

+
+ + +
+ The SELECT clause + +

+ The SELECT clause lets you project your own record types, referencing table fields, functions, arithmetic expressions, etc. The DSL type provides several methods for expressing a SELECT clause: +

+ + + + s1 = create.select(BOOK.ID, BOOK.TITLE); +Select s2 = create.select(BOOK.ID, trim(BOOK.TITLE));]]> + + +

+ Some commonly used projections can be easily created using convenience methods: +

+ + + + select1 = create.selectCount().fetch(); +Select select2 = create.selectZero().fetch(); +Select select2 = create.selectOne().fetch();]]> + + +

+ See more details about functions and expressions in the manual's section about +

+ +

The SELECT DISTINCT clause

+

+ The DISTINCT keyword can be included in the method name, constructing a SELECT clause +

+ + + + select1 = create.selectDistinct(BOOK.TITLE).fetch();]]> + + +

SELECT *

+

+ jOOQ does not explicitly support the asterisk operator in projections. However, you can omit the projection as in these examples: +

+ + + +

Typesafe projections with degree up to {max-row-degree}

+

+ Since jOOQ 3.0, and up to degree {max-row-degree} are now generically typesafe. This is reflected by an overloaded SELECT (and SELECT DISTINCT) API in both DSL and DSLContext. An extract from the DSL type: +

+ + select(Collection> fields); +public static SelectSelectStep select(Field... fields); + +// Typesafe select methods: +public static SelectSelectStep> select(Field field1); +public static SelectSelectStep> select(Field field1, Field field2); +public static SelectSelectStep> select(Field field1, Field field2, Field field3); +// [...]]]> + +

+ Since the generic R type is bound to some , the associated T type information can be used in various other contexts, e.g. the . Such a SELECT statement can be assigned typesafely: +

+ +> s1 = create.select(BOOK.ID, BOOK.TITLE); +Select> s2 = create.select(BOOK.ID, trim(BOOK.TITLE));]]> + +

+ For more information about typesafe record types with degree up to {max-row-degree}, see the manual's section about . +

+
+
+ +
+ The FROM clause + +

+ The SQL FROM clause allows for specifying any number of to select data from. The following are examples of how to form normal FROM clauses: +

+ + + + + + +

+ Read more about aliasing in the manual's section about . +

+ +

More advanced table expressions

+

+ Apart from simple tables, you can pass any arbitrary to the jOOQ FROM clause. This may include in Oracle: +

+ + + + + + +

+ Note, in order to access the DbmsXplan package, you can use the to generate Oracle's SYS schema. +

+ + +

Selecting FROM DUAL with jOOQ

+

+ In many SQL dialects, FROM is a mandatory clause, in some it isn't. jOOQ allows you to omit the FROM clause, returning just one record. An example: +

+ + + + + + +

+ Read more about dual or dummy tables in the manual's section about . The following are examples of how to form normal FROM clauses: +

+
+
+ +
+ The JOIN clause + +

+ jOOQ supports many different types of standard and non-standard SQL JOIN operations: +

+
    +
  • [ INNER ] JOIN
  • +
  • LEFT [ OUTER ] JOIN
  • +
  • RIGHT [ OUTER ] JOIN
  • +
  • FULL OUTER JOIN
  • +
  • LEFT SEMI JOIN
  • +
  • LEFT ANTI JOIN
  • +
  • CROSS JOIN
  • +
  • NATURAL JOIN
  • +
  • NATURAL LEFT [ OUTER ] JOIN
  • +
  • NATURAL RIGHT [ OUTER ] JOIN
  • +
+ +

+ Besides, jOOQ also supports +

+ +
    +
  • CROSS APPLY (T-SQL and Oracle 12c specific)
  • +
  • OUTER APPLY (T-SQL and Oracle 12c specific)
  • +
  • LATERAL derived tables (PostgreSQL and Oracle 12c)
  • +
  • partitioned outer join
  • +
+ +

+ All of these JOIN methods can be called on types, or directly after the FROM clause for convenience. The following example joins AUTHOR and BOOK +

+ + result = create.select() + .from(AUTHOR.join(BOOK) + .on(BOOK.AUTHOR_ID.equal(AUTHOR.ID))) + .fetch(); + +// Call "join" on the type returned by "from" +Result result = create.select() + .from(AUTHOR) + .join(BOOK) + .on(BOOK.AUTHOR_ID.equal(AUTHOR.ID)) + .fetch();]]> + +

+ The two syntaxes will produce the same SQL statement. However, calling "join" on objects allows for more powerful, nested JOIN expressions (if you can handle the parentheses): +

+ + +  + + +
    +
  • See the section about to learn more about the many ways to create objects in jOOQ.
  • +
  • See the section about to learn about the various ways of referencing objects in jOOQ
  • +
+ +

JOIN ON KEY, convenience provided by jOOQ

+

+ Surprisingly, the SQL standard does not allow to formally JOIN on well-known foreign key relationship information. Naturally, when you join BOOK to AUTHOR, you will want to do that based on the BOOK.AUTHOR_ID foreign key to AUTHOR.ID primary key relation. Not being able to do this in SQL leads to a lot of repetitive code, re-writing the same JOIN predicate again and again - especially, when your foreign keys contain more than one column. With jOOQ, when you use , you can use foreign key constraint information in JOIN expressions as such: +

+ + +  + + +

+ In case of ambiguity, you can also supply field references for your foreign keys, or the generated foreign key reference to the onKey() method. +

+

+ Note that formal support for the Sybase JOIN ON KEY syntax is on the roadmap. +

+ +

The JOIN USING syntax

+

+ Most often, you will provide jOOQ with JOIN conditions in the JOIN .. ON clause. SQL supports a different means of specifying how two tables are to be joined. This is the JOIN .. USING clause. Instead of a condition, you supply a set of fields whose names are common to both tables to the left and right of a JOIN operation. This can be useful when your database schema has a high degree of relational normalisation. An example: +

+ + +  + + +

+ In schemas with high degrees of normalisation, you may also choose to use NATURAL JOIN, which takes no JOIN arguments as it joins using all fields that are common to the table expressions to the left and to the right of the JOIN operator. An example: +

+ + +  + + +

Oracle's partitioned OUTER JOIN

+

+ Oracle SQL ships with a special syntax available for OUTER JOIN clauses. According to the Oracle documentation about partitioned outer joins this can be used to fill gaps for simplified analytical calculations. jOOQ only supports putting the PARTITION BY clause to the right of the OUTER JOIN clause. The following example will create at least one record per AUTHOR and per existing value in BOOK.PUBLISHED_IN, regardless if an AUTHOR has actually published a book in that year. +

+ + +  + + +

SEMI JOIN and ANTI JOIN

+

+ Very few databases (e.g. Apache Impala) ship with a built-in syntax for { LEFT | RIGHT } SEMI JOIN and { LEFT | RIGHT } ANTI JOIN, which are much more concise versions of the SQL standard IN / EXISTS and NOT IN / NOT EXISTS predicates. The idea is that the JOIN syntax is expressed where it belongs, in the FROM clause, not in the WHERE clause. +

+

+ Since jOOQ 3.7, these types of JOIN are also supported and they're emulated using EXISTS and NOT EXISTS respectively. +

+

+ Here's how SEMI JOIN translates to EXISTS. +

+ + + + + +

+ And here's how ANTI JOIN translates to NOT EXISTS +

+ + + + +

T-SQL's CROSS APPLY and OUTER APPLY

+

+ T-SQL has long known what the SQL standard calls lateral derived tables, lateral joins using the APPLY keyword. To every row resulting from the table expression on the left, we apply the table expression on the right. This is extremely useful for table-valued functions, which are also supported by jOOQ. Some examples: +

+ + + +

+ The above example shows standard usage of the LATERAL keyword to connect a derived table to the previous table in the . A similar statement can be written in T-SQL: +

+ + + +

+ While not all forms of LATERAL JOIN have an equivalent APPLY syntax, the inverse is true, and jOOQ can thus emulate OUTER APPLY and CROSS APPLY using LATERAL JOIN. +

+ +

+ LATERAL JOIN or CROSS APPLY are particularly useful together with , which are also supported by jOOQ. +

+ +
+
+ +
+ The WHERE clause + +

+ The WHERE clause can be used for JOIN or filter predicates, in order to restrict the data returned by the supplied to the previously specified and . Here is an example: +

+ + +  + + +

+ The above syntax is convenience provided by jOOQ, allowing you to connect the supplied in the WHERE clause with another condition using an AND operator. You can of course also create a more complex condition and supply that to the WHERE clause directly (observe the different placing of parentheses). The results will be the same: +

+ + +  + + +

+ You will find more information about creating later in the manual. +

+
+
+ +
+ The CONNECT BY clause + +

+ The Oracle database knows a very succinct syntax for creating hierarchical queries: the CONNECT BY clause, which is fully supported by jOOQ, including all related functions and pseudo-columns. A more or less formal definition of this clause is given here: +

+-- SELECT .. +-- FROM .. +-- WHERE .. + CONNECT BY [ NOCYCLE ] condition [ AND condition, ... ] [ START WITH condition ] +-- GROUP BY .. +-- ORDER [ SIBLINGS ] BY .. + +

+ An example for an iterative query, iterating through values between 1 and 5 is this: +

+ + +  + + +

+ Here's a more complex example where you can recursively fetch directories in your database, and concatenate them to a path: +

+ +  + + + +

+ The output might then look like this +

+ ++------------------------------------------------+ +|substring | ++------------------------------------------------+ +|C: | +|C:/eclipse | +|C:/eclipse/configuration | +|C:/eclipse/dropins | +|C:/eclipse/eclipse.exe | ++------------------------------------------------+ +|...21 record(s) truncated... + + +

+ Some of the supported functions and pseudo-columns are these (available from the ): +

+ +
    +
  • LEVEL
  • +
  • CONNECT_BY_IS_CYCLE
  • +
  • CONNECT_BY_IS_LEAF
  • +
  • CONNECT_BY_ROOT
  • +
  • SYS_CONNECT_BY_PATH
  • +
  • PRIOR
  • +
+ +

+ Note that this syntax is also supported in the CUBRID database and might be emulated in other dialects supporting common table expressions in the future. +

+ +

ORDER SIBLINGS

+

+ The Oracle database allows for specifying a SIBLINGS keyword in the . Instead of ordering the overall result, this will only order siblings among each other, keeping the hierarchy intact. An example is given here: +

+ + +  + + + +
+
+ +
+ The GROUP BY clause + +

+ GROUP BY can be used to create unique groups of data, to form aggregations, to remove duplicates and for other reasons. It will transform your previously defined , and return only one record per unique group as specified in this clause. For instance, you can group books by BOOK.AUTHOR_ID: +

+ + +  + + +

+ The above example counts all books per author. +

+

+ Note, as defined in the SQL standard, when grouping, you may no longer project any columns that are not a formal part of the GROUP BY clause, or . +

+ +

MySQL's deviation from the SQL standard

+

+ MySQL has a peculiar way of not adhering to this standard behaviour. This is documented in the MySQL manual. In short, with MySQL, you can also project any other field that is not part of the GROUP BY clause. The projected values will just be arbitrary values from within the group. You cannot rely on any ordering. For example: +

+ + +  + + +

+ This will return an arbitrary title per author. jOOQ supports this syntax, as jOOQ is not doing any checks internally, about the consistence of tables/fields/functions that you provide it. +

+ +

Empty GROUP BY clauses

+

+ jOOQ supports empty GROUP BY () clause as well. This will result in that return only one record. +

+ + +  + + + +

ROLLUP(), CUBE() and GROUPING SETS()

+

+ Some databases support the SQL standard grouping functions and some extensions thereof. See the manual's section about for more details. +

+
+
+ +
+ The HAVING clause + +

+ The HAVING clause is commonly used to further restrict data resulting from a previously issued . An example, selecting only those authors that have written at least two books: +

+ + += 2 +]]>  + + +

+ According to the SQL standard, you may omit the GROUP BY clause and still issue a HAVING clause. This will implicitly GROUP BY (). jOOQ also supports this syntax. The following example selects one record, only if there are at least 4 books in the books table: +

+ + += 4 +]]>  + + +
+
+ +
+ The WINDOW clause + +

+ The SQL:2003 standard as well as PostgreSQL and Sybase SQL Anywhere support a WINDOW clause that allows for specifying WINDOW frames for reuse in and . +

+ + +  + + + +

+ Note that in order to create such a window definition, we need to first create a using . +

+

+ Even if only PostgreSQL and Sybase SQL Anywhere natively support this great feature, jOOQ can emulate it by expanding any and types that you pass to the window() method - if the database supports window functions at all. +

+ +

+ Some more information about and the WINDOW clause can be found on our blog: http://blog.jooq.org/2013/11/03/probably-the-coolest-sql-feature-window-functions/ +

+
+
+ +
+ The ORDER BY clause + +

+ Databases are allowed to return data in any arbitrary order, unless you explicitly declare that order in the ORDER BY clause. In jOOQ, this is straight-forward: +

+ + +  + + +

+ Any jOOQ can be transformed into an by calling the asc() and desc() methods. +

+ +

Ordering by field index

+

+ The SQL standard allows for specifying integer literals (, not !) to reference column indexes from the projection (). This may be useful if you do not want to repeat a lengthy expression, by which you want to order - although most databases also allow for referencing in the ORDER BY clause. An example of this is given here: +

+ + +  + + +

+ Note, how one() is used as a convenience short-cut for inline(1) +

+ +

Ordering and NULLS

+

+ A few databases support the SQL standard "null ordering" clause in sort specification lists, to define whether NULL values should come first or last in an ordered result. +

+ + +  + + +

+ If your database doesn't support this syntax, jOOQ emulates it using a as follows +

+ + + +

Ordering using CASE expressions

+

+ Using in SQL ORDER BY clauses is a common pattern, if you want to introduce some sort indirection / sort mapping into your queries. As with SQL, you can add any type of into your ORDER BY clause. For instance, if you have two favourite books that you always want to appear on top, you could write: +

+ + +  + + +

+ But writing these things can become quite verbose. jOOQ supports a convenient syntax for specifying sort mappings. The same query can be written in jOOQ as such: +

+ + + +

+ More complex sort indirections can be provided using a Map: +

+ +() {{ + put("1984", 1); + put("Animal Farm", 13); + put("The jOOQ book", 10); + }})) + .fetch();]]> + +

+ Of course, you can combine this feature with the previously discussed NULLS FIRST / NULLS LAST feature. So, if in fact these two books are the ones you like least, you can put all NULLS FIRST (all the other books): +

+ + + +

jOOQ's understanding of SELECT .. ORDER BY

+

+ The SQL standard defines that a "query expression" can be ordered, and that query expressions can contain , whose subqueries cannot be ordered. While this is defined as such in the SQL standard, many databases allowing for the in one way or another, do not adhere to this part of the SQL standard. Hence, jOOQ allows for ordering all SELECT statements, regardless whether they are constructed as a part of a UNION or not. Corner-cases are handled internally by jOOQ, by introducing synthetic subselects to adhere to the correct syntax, where this is needed. +

+ + +

Oracle's ORDER SIBLINGS BY clause

+

+ jOOQ also supports Oracle's SIBLINGS keyword to be used with ORDER BY clauses for +

+
+
+ +
+ The LIMIT .. OFFSET clause + +

+ While being extremely useful for every application that does paging, or just to limit result sets to reasonable sizes, this clause is not yet part of any SQL standard (up until SQL:2008). Hence, there exist a variety of possible implementations in various SQL dialects, concerning this limit clause. jOOQ chose to implement the LIMIT .. OFFSET clause as understood and supported by MySQL, H2, HSQLDB, Postgres, and SQLite. Here is an example of how to apply limits with jOOQ: +

+ + + +

+ This will limit the result to 1 books starting with the 2nd book (starting at offset 0!). limit() is supported in all dialects, offset() in all but Sybase ASE, which has no reasonable means to emulate it. This is how jOOQ trivially emulates the above query in various SQL dialects with native OFFSET pagination support: +

+ + + +

+ Things get a little more tricky in those databases that have no native idiom for OFFSET pagination (actual queries may vary): +

+ + 1 +AND RN <= 3 + +-- DB2 (with OFFSET), SQL Server 2008 (with OFFSET) +SELECT * FROM ( + SELECT DISTINCT BOOK.ID, BOOK.TITLE + DENSE_RANK() OVER (ORDER BY ID ASC, TITLE ASC) AS RN + FROM BOOK +) AS X +WHERE RN > 1 +AND RN <= 3 + +-- Oracle 11g and less +SELECT * +FROM ( + SELECT b.*, ROWNUM RN + FROM ( + SELECT * + FROM BOOK + ORDER BY ID ASC + ) b + WHERE ROWNUM <= 3 +) +WHERE RN > 1]]> + +

+ As you can see, jOOQ will take care of the incredibly painful ROW_NUMBER() OVER() (or ROWNUM for Oracle) filtering in subselects for you, you'll just have to write limit(1).offset(2) in any dialect. +

+ +

+ Side-note: If you're interested in understanding why we chose ROWNUM for Oracle, please refer to this very interesting benchmark, comparing the different approaches of doing pagination in Oracle: http://www.inf.unideb.hu/~gabora/pagination/results.html. +

+ +

SQL Server's ORDER BY, TOP and subqueries

+

+ As can be seen in the above example, writing correct SQL can be quite tricky, depending on the SQL dialect. For instance, with SQL Server, you cannot have an ORDER BY clause in a subquery, unless you also have a TOP clause. This is illustrated by the fact that jOOQ renders a TOP 100 PERCENT clause for you. The same applies to the fact that ROW_NUMBER() OVER() needs an ORDER BY windowing clause, even if you don't provide one to the jOOQ query. By default, jOOQ adds ordering by the first column of your projection. +

+
+
+ +
+ The SEEK clause + +

+ The previous chapter talked about using LIMIT .. OFFSET, or OFFSET .. FETCH or some other vendor-specific variant of the same. This can lead to significant performance issues when reaching a high page number, as all unneeded records need to be skipped by the database. +

+

+ A much faster and more stable way to perform paging is the so-called keyset paging method also called seek method. jOOQ supports a synthetic seek() clause, that can be used to perform keyset paging. Imagine we have these data: +

+ + + +

+ Now, if we want to display page 6 to the user, instead of going to page 6 by using a record OFFSET, we could just fetch the record strictly after the last record on page 5, which yields the values (533, 2). This is how you would do it with SQL or with jOOQ: +

+ + + (2, 533) +ORDER BY value, id +LIMIT 5 +]]>  + + + +

+ As you can see, the jOOQ SEEK clause is a synthetic clause that does not really exist in SQL. However, the jOOQ syntax is far more intuitive for a variety of reasons: +

+ +
    +
  • It replaces OFFSET where you would expect
  • +
  • It doesn't force you to mix regular predicates with "seek" predicates
  • +
  • It is typesafe
  • +
  • It emulates for you, in those databases that do not support them
  • +
+ +

+ This query now yields: +

+ + + +

+ Note that you cannot combine the SEEK clause with the OFFSET clause. +

+ +

+ More information about this great feature can be found in the jOOQ blog: +

+ + +

+ Further information about offset pagination vs. keyset pagination performance can be found on our partner page: +

+ +
+ + No more offset + +
+
+
+ +
+ The FOR UPDATE clause + +

+ For inter-process synchronisation and other reasons, you may choose to use the SELECT .. FOR UPDATE clause to indicate to the database, that a set of cells or records should be locked by a given transaction for subsequent updates. With jOOQ, this can be achieved as such: +

+ + +  + + +

+ The above example will produce a record-lock, locking the whole record for updates. Some databases also support cell-locks using FOR UPDATE OF .. +

+ + +  + + +

+ Oracle goes a bit further and also allows to specify the actual locking behaviour. It features these additional clauses, which are all supported by jOOQ: +

+
    +
  • FOR UPDATE NOWAIT: This is the default behaviour. If the lock cannot be acquired, the query fails immediately
  • +
  • FOR UPDATE WAIT n: Try to wait for [n] seconds for the lock acquisition. The query will fail only afterwards
  • +
  • FOR UPDATE SKIP LOCKED: This peculiar syntax will skip all locked records. This is particularly useful when implementing queue tables with multiple consumers
  • +
+

+ With jOOQ, you can use those Oracle extensions as such: +

+ + + +

FOR UPDATE in CUBRID and SQL Server

+

+ The SQL standard specifies a FOR UPDATE clause to be applicable for cursors. Most databases interpret this as being applicable for all SELECT statements. An exception to this rule are the CUBRID and SQL Server databases, that do not allow for any FOR UPDATE clause in a regular SQL SELECT statement. jOOQ emulates the FOR UPDATE behaviour, by locking record by record with JDBC. JDBC allows for specifying the flags TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE for any statement, and then using ResultSet.updateXXX() methods to produce a cell-lock / row-lock. Here's a simplified example in JDBC: +

+ + +

+ The main drawback of this approach is the fact that the database has to maintain a scrollable cursor, whose records are locked one by one. This can cause a major risk of deadlocks or race conditions if the JDBC driver can recover from the unsuccessful locking, if two Java threads execute the following statements: +

+ + + +

+ So use this technique with care, possibly only ever locking single rows! +

+ +

Pessimistic (shared) locking with the FOR SHARE clause

+

+ Some databases (MySQL, Postgres) also allow to issue a non-exclusive lock explicitly using a FOR SHARE clause. This is also supported by jOOQ +

+ +

Optimistic locking in jOOQ

+

+ Note, that jOOQ also supports optimistic locking, if you're doing simple CRUD. This is documented in the section's manual about . +

+
+
+ +
+ UNION, INTERSECTION and EXCEPT + +

+ SQL allows to perform set operations as understood in standard set theory on result sets. These operations include unions, intersections, subtractions. For two subselects to be combinable by such a set operator, each subselect must return a of the same degree and type. +

+ +

UNION and UNION ALL

+

+ These operators combine two results into one. While UNION removes all duplicate records resulting from this combination, UNION ALL leaves subselect results as they are. Typically, you should prefer UNION ALL over UNION, if you don't really need to remove duplicates. The following example shows how to use such a UNION operation in jOOQ. +

+ + +  + + +

INTERSECT [ ALL ] and EXCEPT [ ALL ]

+

+ INTERSECT is the operation that produces only those values that are returned by both subselects. EXCEPT (or MINUS in Oracle) is the operation that returns only those values that are returned exclusively in the first subselect. Both operators will remove duplicates from their results. +

+ +

+ Just like with UNION ALL, these operators have an optional ALL keyword that allows for keeping duplicate rows after intersection or subtraction, which is supported in jOOQ 3.7+. +

+ +

jOOQ's set operators and how they're different from standard SQL

+

+ As previously mentioned in the manual's section about the , jOOQ has slightly changed the semantics of these set operators. While in SQL, a subselect may not contain any or (unless you wrap the subselect into a ), jOOQ allows you to do so. In order to select both the youngest and the oldest author from the database, you can issue the following statement with jOOQ (rendered to the MySQL dialect): +

+ + +  + + +

+ In case your database doesn't support ordered UNION subselects, the subselects are nested in derived tables: +

+ + + + + + +

Projection typesafety for degrees between 1 and {max-row-degree}

+

+ Two subselects that are combined by a set operator are required to be of the same degree and, in most databases, also of the same type. jOOQ 3.0's introduction of helps compile-checking these constraints: +

+ +> s1 = select(BOOK.ID, BOOK.TITLE).from(BOOK); +Select> s2 = selectOne(); +Select> s3 = select(one(), zero()); +Select> s4 = select(one(), inline("abc")); + +// Let's try to combine them: +s1.union(s2); // Doesn't compile because of a degree mismatch. Expected: Record2<...>, got: Record1<...> +s1.union(s3); // Doesn't compile because of a type mismatch. Expected: , got: +s1.union(s4); // OK. The two Record[N] types match]]> +
+
+ +
+ Oracle-style hints + +

+ If you are closely coupling your application to an Oracle (or CUBRID) database, you might need to be able to pass hints of the form /*+HINT*/ with your SQL statements to the Oracle database. For example: +

+ +SELECT /*+ALL_ROWS*/ FIRST_NAME, LAST_NAME + FROM AUTHOR + +

+ This can be done in jOOQ using the .hint() clause in your SELECT statement: +

+ +create.select(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .hint("/*+ALL_ROWS*/") + .from(AUTHOR) + .fetch(); + +

+ Note that you can pass any string in the .hint() clause. If you use that clause, the passed string will always be put in between the SELECT [DISTINCT] keywords and the actual projection list. This can be useful in other databases too, such as MySQL, for instance: +

+ + +  + + +
+
+ +
+ Lexical and logical SELECT clause order + +

+ SQL has a lexical and a logical order of SELECT clauses. The lexical order of SELECT clauses is inspired by the English language. As SQL statements are commands for the database, it is natural to express a statement in an imperative tense, such as "SELECT this and that!". +

+ +

Logical SELECT clause order

+

+ The logical order of SELECT clauses, however, does not correspond to the syntax. In fact, the logical order is this: +

+ +
    +
  • : First, all data sources are defined and joined
  • +
  • : Then, data is filtered as early as possible
  • +
  • : Then, data is traversed iteratively or recursively, to produce new tuples
  • +
  • : Then, data is reduced to groups, possibly producing new tuples if are used
  • +
  • : Then, data is filtered again
  • +
  • : Only now, the projection is evaluated. In case of a SELECT DISTINCT statement, data is further reduced to remove duplicates
  • +
  • : Optionally, the above is repeated for several UNION-connected subqueries. Unless this is a UNION ALL clause, data is further reduced to remove duplicates
  • +
  • : Now, all remaining tuples are ordered
  • +
  • : Then, a paging view is created for the ordered tuples
  • +
  • : Finally, pessimistic locking is applied
  • +
+ +

+ The SQL Server documentation also explains this, with slightly different clauses: +

+ +
    +
  • FROM
  • +
  • ON
  • +
  • JOIN
  • +
  • WHERE
  • +
  • GROUP BY
  • +
  • WITH CUBE or WITH ROLLUP
  • +
  • HAVING
  • +
  • SELECT
  • +
  • DISTINCT
  • +
  • ORDER BY
  • +
  • TOP
  • +
+ +

+ As can be seen, databases have to logically reorder a SQL statement in order to determine the best execution plan. +

+ +

Alternative syntaxes: LINQ, SLICK

+

+ Some "higher-level" abstractions, such as C#'s LINQ or Scala's SLICK try to inverse the lexical order of SELECT clauses to what appears to be closer to the logical order. The obvious advantage of moving the SELECT clause to the end is the fact that the projection type, which is the record type returned by the SELECT statement can be re-used more easily in the target environment of the internal domain specific language. +

+ +

+ A LINQ example: +

+ + +

+ A SLICK example: +

+ + + +

+ While this looks like a good idea at first, it only complicates translation to more advanced SQL statements while impairing readability for those users that are used to writing SQL. jOOQ is designed to look just like SQL. This is specifically true for SLICK, which not only changed the SELECT clause order, but also heavily "integrated" SQL clauses with the Scala language. +

+

+ For these reasons, the jOOQ DSL API is modelled in SQL's lexical order. +

+
+
+
+
+ +
+ The INSERT statement + +

+ The INSERT statement is used to insert new records into a database table. The following sections describe the various operation modes of the jOOQ INSERT statement. +

+
+ + +
+ INSERT .. VALUES + + +

INSERT .. VALUES with a single row

+

+ Records can either be supplied using a VALUES() constructor, or a SELECT statement. jOOQ supports both types of INSERT statements. An example of an INSERT statement using a VALUES() constructor is given here: +

+ + + +  +create.insertInto(AUTHOR, + AUTHOR.ID, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .values(100, "Hermann", "Hesse") + .execute(); + +

+ Note that for explicit degrees up to {max-row-degree}, the VALUES() constructor provides additional typesafety. The following example illustrates this: +

+ + step = + create.insertInto(AUTHOR, AUTHOR.ID, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME); + step.values("A", "B", "C"); + // ^^^ Doesn't compile, the expected type is Integer]]> + +

INSERT .. VALUES with multiple rows

+

+ The SQL standard specifies that multiple rows can be supplied to the VALUES() constructor in an INSERT statement. Here's an example of a multi-record INSERT +

+ +  +create.insertInto(AUTHOR, + AUTHOR.ID, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .values(100, "Hermann", "Hesse") + .values(101, "Alfred", "Döblin") + .execute() + +

+ jOOQ tries to stay close to actual SQL. In detail, however, Java's expressiveness is limited. That's why the values() clause is repeated for every record in multi-record inserts. +

+

+ Some RDBMS do not support inserting several records in a single statement. In those cases, jOOQ emulates multi-record INSERTs using the following SQL: +

+ + +  +create.insertInto(AUTHOR, + AUTHOR.ID, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .values(100, "Hermann", "Hesse") + .values(101, "Alfred", "Döblin") + .execute(); + +
+
+ +
+ INSERT .. DEFAULT VALUES + +

+ A lesser-known syntactic feature of SQL is the INSERT .. DEFAULT VALUES statement, where a single record is inserted, containing only DEFAULT values for every row. It is written as such: +

+ + + +  +create.insertInto(AUTHOR) + .defaultValues() + .execute(); + +

+ This can make a lot of sense in situations where you want to "reserve" a row in the database for an subsequent within the same transaction. Or if you just want to send an event containing trigger-generated default values, such as IDs or timestamps. +

+

+ The DEFAULT VALUES clause is not supported in all databases, but jOOQ can emulate it using the equivalent statement: +

+ + +  +create.insertInto( + AUTHOR, AUTHOR.ID, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, ...) + .values( + defaultValue(AUTHOR.ID), + defaultValue(AUTHOR.FIRST_NAME), + defaultValue(AUTHOR.LAST_NAME), ...) + .execute(); + +

+ The DEFAULT keyword (or method) can also be used for individual columns only, although that will have the same effect as leaving the column away entirely. +

+
+
+ +
+ INSERT .. SET + +

+ MySQL (and some other RDBMS) allow for using a non-SQL-standard, UPDATE-like syntax for INSERT statements. This is also supported in jOOQ, should you prefer that syntax. The above INSERT statement can also be expressed as follows: +

+ +create.insertInto(AUTHOR) + .set(AUTHOR.ID, 100) + .set(AUTHOR.FIRST_NAME, "Hermann") + .set(AUTHOR.LAST_NAME, "Hesse") + .newRecord() + .set(AUTHOR.ID, 101) + .set(AUTHOR.FIRST_NAME, "Alfred") + .set(AUTHOR.LAST_NAME, "Döblin") + .execute(); + +

+ As you can see, this syntax is a bit more verbose, but also more readable, as every field can be matched with its value. Internally, the two syntaxes are strictly equivalent. +

+
+
+ +
+ INSERT .. SELECT + + +

+ In some occasions, you may prefer the INSERT SELECT syntax, for instance, when you copy records from one table to another: +

+ +create.insertInto(AUTHOR_ARCHIVE) + .select(selectFrom(AUTHOR).where(AUTHOR.DECEASED.isTrue())) + .execute(); +
+
+ +
+ INSERT .. ON DUPLICATE KEY + +

The synthetic ON DUPLICATE KEY UPDATE clause

+ +

+ The MySQL database supports a very convenient way to INSERT or UPDATE a record. This is a non-standard extension to the SQL syntax, which is supported by jOOQ and emulated in other RDBMS, where this is possible (i.e. if they support the SQL standard ). Here is an example how to use the ON DUPLICATE KEY UPDATE clause: +

+ +// Add a new author called "Koontz" with ID 3. +// If that ID is already present, update the author's name +create.insertInto(AUTHOR, AUTHOR.ID, AUTHOR.LAST_NAME) + .values(3, "Koontz") + .onDuplicateKeyUpdate() + .set(AUTHOR.LAST_NAME, "Koontz") + .execute(); + +

The synthetic ON DUPLICATE KEY IGNORE clause

+ +

+ The MySQL database also supports an INSERT IGNORE INTO clause. This is supported by jOOQ using the more convenient SQL syntax variant of ON DUPLICATE KEY IGNORE: +

+ +// Add a new author called "Koontz" with ID 3. +// If that ID is already present, ignore the INSERT statement +create.insertInto(AUTHOR, AUTHOR.ID, AUTHOR.LAST_NAME) + .values(3, "Koontz") + .onDuplicateKeyIgnore() + .execute(); +

+ If the underlying database doesn't have any way to "ignore" failing INSERT statements, (e.g. MySQL via INSERT IGNORE), jOOQ can emulate the statement using a , or using INSERT .. SELECT WHERE NOT EXISTS: +

+ +

Emulating IGNORE with MERGE

+ +

+ The above jOOQ statement can be emulated with the following, equivalent SQL statement: +

+ +MERGE INTO AUTHOR +USING (SELECT 1 FROM DUAL) +ON (AUTHOR.ID = 3) +WHEN NOT MATCHED THEN INSERT (ID, LAST_NAME) + VALUES (3, 'Koontz') + +

Emulating IGNORE with INSERT .. SELECT WHERE NOT EXISTS

+ +

+ The above jOOQ statement can be emulated with the following, equivalent SQL statement: +

+ +INSERT INTO AUTHOR (ID, LAST_NAME) +SELECT 3, 'Koontz' +WHERE NOT EXISTS ( + SELECT 1 + FROM AUTHOR + WHERE AUTHOR.ID = 3 +) +
+
+ +
+ INSERT .. RETURNING + + +

+ The Postgres database has native support for an INSERT .. RETURNING clause. This is a very powerful concept that is emulated for all other dialects using JDBC's method. Take this example: +

+ + record = +create.insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .values("Charlotte", "Roche") + .returning(AUTHOR.ID) + .fetchOne(); + +System.out.println(record.getValue(AUTHOR.ID)); + +// For some RDBMS, this also works when inserting several values +// The following should return a 2x2 table +Result result = +create.insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .values("Johann Wolfgang", "von Goethe") + .values("Friedrich", "Schiller") + // You can request any field. Also trigger-generated values + .returning(AUTHOR.ID, AUTHOR.CREATION_DATE) + .fetch();]]> + +

+ Some databases have poor support for returning generated keys after INSERTs. In those cases, jOOQ might need to issue another in order to fetch an @@identity value. Be aware, that this can lead to race-conditions in those databases that cannot properly return generated ID values. For more information, please consider the jOOQ Javadoc for the returning() clause. +

+
+
+
+
+ +
+ The UPDATE statement + +

+ The UPDATE statement is used to modify one or several pre-existing records in a database table. UPDATE statements are only possible on single tables. Support for multi-table updates will be implemented in the near future. An example update query is given here: +

+ + +  +create.update(AUTHOR) + .set(AUTHOR.FIRST_NAME, "Hermann") + .set(AUTHOR.LAST_NAME, "Hesse") + .where(AUTHOR.ID.equal(3)) + .execute(); + + +

+ Most databases allow for using scalar subselects in UPDATE statements in one way or another. jOOQ models this through a set(Field<T>, Select<? extends Record1<T>>) method in the UPDATE DSL API: +

+ + +  +create.update(AUTHOR) + .set(AUTHOR.FIRST_NAME, + select(PERSON.FIRST_NAME) + .from(PERSON) + .where(PERSON.ID.equal(AUTHOR.ID)) + ) + .where(AUTHOR.ID.equal(3)) + .execute(); + + + +

Using row value expressions in an UPDATE statement

+

+ jOOQ supports formal in various contexts, among which the UPDATE statement. Only one row value expression can be updated at a time. Here's an example: +

+ + +  +create.update(AUTHOR) + .set(row(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME), + row("Herman", "Hesse")) + .where(AUTHOR.ID.equal(3)) + .execute(); + + +

+ This can be particularly useful when using subselects: +

+ + +  +create.update(AUTHOR) + .set(row(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME), + select(PERSON.FIRST_NAME, PERSON.LAST_NAME) + .from(PERSON) + .where(PERSON.ID.equal(AUTHOR.ID)) + ) + .where(AUTHOR.ID.equal(3)) + .execute(); + + +

+ The above row value expressions usages are completely typesafe. +

+ +

UPDATE .. RETURNING

+

+ The Firebird and Postgres databases support a RETURNING clause on their UPDATE statements, similar as the RETURNING clause in . This is useful to fetch trigger-generated values in one go. An example is given here: +

+ + + + + + +

+ The UPDATE .. RETURNING clause is emulated for DB2 using the SQL standard SELECT .. FROM FINAL TABLE(UPDATE ..) construct, and in Oracle, using the PL/SQL UPDATE .. RETURNING statement. +

+
+
+ +
+ The DELETE statement + +

+ The DELETE statement removes records from a database table. DELETE statements are only possible on single tables. Support for multi-table deletes will be implemented in the near future. An example delete query is given here: +

+ + +  +create.delete(AUTHOR) + .where(AUTHOR.ID.equal(100)) + .execute(); + + +
+
+ +
+ The MERGE statement + +

+ The MERGE statement is one of the most advanced standardised SQL constructs, which is supported by DB2, HSQLDB, Oracle, SQL Server and Sybase (MySQL has the similar INSERT .. ON DUPLICATE KEY UPDATE construct) +

+

+ The point of the standard MERGE statement is to take a TARGET table, and merge (INSERT, UPDATE) data from a SOURCE table into it. DB2, Oracle, SQL Server and Sybase also allow for DELETING some data and for adding many additional clauses. With jOOQ {jooq-version}, only Oracle's MERGE extensions are supported. Here is an example: +

+ + +  +create.mergeInto(AUTHOR) + .using(create.selectOne()) + .on(AUTHOR.LAST_NAME.equal("Hitchcock")) + .whenMatchedThenUpdate() + .set(AUTHOR.FIRST_NAME, "John") + .whenNotMatchedThenInsert(AUTHOR.LAST_NAME) + .values("Hitchcock") + .execute(); + + +

MERGE Statement (H2-specific syntax)

+

+ The H2 database ships with a somewhat less powerful but a little more intuitive syntax for its own version of the MERGE statement. An example more or less equivalent to the previous one can be seen here: +

+ + +-- Check if there is already an author called 'Hitchcock' +-- If there is, rename him to John. If there isn't add him. + +MERGE INTO AUTHOR (FIRST_NAME, LAST_NAME) +KEY (LAST_NAME) +VALUES ('John', 'Hitchcock') +create.mergeInto(AUTHOR, + AUTHOR.FIRST_NAME, + AUTHOR.LAST_NAME) + .key(AUTHOR.LAST_NAME) + .values("John", "Hitchcock") + .execute(); + + +

+ This syntax can be fully emulated by jOOQ for all other databases that support the SQL standard MERGE statement. For more information about the H2 MERGE syntax, see the documentation here:
+ http://www.h2database.com/html/grammar.html#merge +

+ +

Typesafety of VALUES() for degrees up to {max-row-degree}

+

+ Much like the , the MERGE statement's VALUES() clause provides typesafety for degrees up to {max-row-degree}, in both the standard syntax variant as well as the H2 variant. +

+
+
+ + +
+
+ +
+ SQL Statements (DDL) + +

+ jOOQ's DDL support is currently still very limited. In the long run, jOOQ will support the most important statement types for frequent informal database migrations, though. Note that jOOQ will not aim to replace existing database migration frameworks. At Data Geekery, we usually recommend using Flyway for migrations. See also the for more information. +

+
+ + +
+ The ALTER statement + +

+ jOOQ currently supports the following ALTER statements (SQL examples in PostgreSQL syntax): +

+ +

Tables

+ +

+ These statements alter / add / drop columns and their types: +

+ + + + + + +

+ These statements alter / add / drop constraints: +

+ + + + + + + +

Sequences

+ + + + + + +
+
+ + +
+ The CREATE statement + +

+ jOOQ currently supports the following CREATE statements (SQL examples in PostgreSQL syntax): +

+ +

Indexes

+ + + + + + +

Sequences

+ + + + + + +

Tables

+ + + + + + +

Views

+ + + + + +
+
+ + +
+ The DROP statement + +

+ jOOQ currently supports the following DROP statements (SQL examples in PostgreSQL syntax): +

+ +

Indexes

+ + + + + + +

Sequences

+ + + + + + +

Tables

+ + + + + + +

Views

+ + + + + +
+
+ + +
+ The TRUNCATE statement + +

+ Even if the TRUNCATE statement mainly modifies data, it is generally considered to be a DDL statement. It is popular in many databases when you want to bypass constraints for table truncation. Databases may behave differently, when a truncated table is referenced by other tables. For instance, they may fail if records from a truncated table are referenced, even with ON DELETE CASCADE clauses in place. Please, consider your database manual to learn more about its TRUNCATE implementation. +

+

+ The TRUNCATE syntax is trivial: +

+ + + TRUNCATE TABLE AUTHOR; + create.truncate(AUTHOR).execute(); + + +

+ TRUNCATE is not supported by Ingres and SQLite. jOOQ will execute a DELETE FROM AUTHOR statement instead. +

+
+
+ + +
+ Generating DDL from objects + +

+ When using jOOQ's , a whole set of meta data is generated with the generated artefacts, such as schemas, tables, columns, data types, constraints, default values, etc. +

+ +

+ This meta data can be used to generate DDL CREATE statements in any SQL dialect, in order to partially restore the original schema again on a new database instance. This is particularly useful, for instance, when working with an Oracle production database, and an H2 in-memory test database. The following code produces the DDL for a schema: +

+ + + +

+ When executing the above, you should see something like the following: +

+ + + +

+ Do note that these features only restore parts of the original schema. For instance, vendor-specific storage clauses that are not available to jOOQ's generated meta data cannot be reproduced this way. +

+
+
+
+
+ +
+ Catalog and schema expressions + +

+ Most databases know some sort of namespace to group objects like , , and others into a common catalog or schema. jOOQ uses the types and to model these groupings, following SQL standard naming. +

+ +

The catalog

+ +

+ A catalog is a collection of schemas. In many databases, the catalog corresponds to the database, or the database instance. Most often, catalogs are completely independent and their tables cannot be joined or combined in any way in a single query. The exception here is SQL Server, which allows for fully referencing tables from multiple catalogs: +

+ + + +

+ By default, the Settings.renderCatalog flag is turned on. In case a database supports querying multiple catalogs, jOOQ will generate fully qualified object names, including catalog name. For more information about this setting, see +

+ +

+ jOOQ's generates subpackages for each catalog. +

+ +

The schema

+ +

+ A schema is a collection of objects, such as tables. Most databases support some sort of schema (except for some embedded databases like Access, Firebird, SQLite). In most databases, the schema is an independent structural entity. In Oracle, the schema and the user / owner is mostly treated as the same thing. An example of a query that uses fully qualified tables including schema names is: +

+ + + +

+ By default, the Settings.renderSettings flag is turned on. jOOQ will thus generate fully qualified object names, including the setting name. For more information about this setting, see +

+
+
+ +
+ Table expressions + +

+ The following sections explain the various types of table expressions supported by jOOQ +

+
+ + +
+ Generated Tables + +

+ Most of the times, when thinking about a you're probably thinking about an actual table in your database schema. If you're using jOOQ's , you will have all tables from your database schema available to you as type safe Java objects. You can then use these tables in SQL , or in other , just like any other table expression. An example is given here: +

+ + +  + + +

+ The above example shows how AUTHOR and BOOK tables are joined in a . It also shows how you can access by dereferencing the relevant Java attributes of their tables. +

+

+ See the manual's section about for more information about what is really generated by the +

+
+
+ +
+ Aliased Tables + +

+ The strength of jOOQ's becomes more obvious when you perform table aliasing and dereference fields from generated aliased tables. This can best be shown by example: +

+ + 1920 + AND a.first_name = 'Paulo' + ORDER BY b.title +]]>  + + +

+ As you can see in the above example, calling as() on generated tables returns an object of the same type as the table. This means that the resulting object can be used to dereference fields from the aliased table. This is quite powerful in terms of having your Java compiler check the syntax of your SQL statements. If you remove a column from a table, dereferencing that column from that table alias will cause compilation errors. +

+ +

Dereferencing columns from other table expressions

+

+ Only few table expressions provide the SQL syntax typesafety as shown above, where generated tables are used. Most tables, however, expose their fields through field() methods: +

+ + a = AUTHOR.as("a"); + +// Get fields from a: +Field id = a.field("ID"); +Field firstName = a.field("FIRST_NAME");]]> + +

Derived column lists

+

+ The SQL standard specifies how a table can be renamed / aliased in one go along with its columns. It references the term "derived column list" for the following syntax (as supported by Postgres, for instance): +

+ + + +

+ This feature is useful in various use-cases where column names are not known in advance (but the table's degree is!). An example for this are , or the : +

+ + + +

+ Only few databases really support such a syntax, but fortunately, jOOQ can emulate it easily using UNION ALL and an empty dummy record specifying the new column names. The two statements are equivalent: +

+ + +

+ In jOOQ, you would simply specify a varargs list of column aliases as such: +

+ + +
+
+ +
+ Joined tables + +

+ The that can be used in are the most powerful and best supported means of creating new in SQL. Informally, the following can be said: +

+ +A(colA1, ..., colAn) "join" B(colB1, ..., colBm) "produces" C(colA1, ..., colAn, colB1, ..., colBm) + +

+ SQL and relational algebra distinguish between at least the following JOIN types (upper-case: SQL, lower-case: relational algebra): +

+
    +
  • CROSS JOIN or cartesian product: The basic JOIN in SQL, producing a relational cross product, combining every record of table A with every record of table B. Note that cartesian products can also be produced by listing comma-separated in the of a
  • +
  • NATURAL JOIN: The basic JOIN in relational algebra, yet a rarely used JOIN in databases with everyday degree of normalisation. This JOIN type unconditionally equi-joins two tables by all columns with the same name (requiring foreign keys and primary keys to share the same name). Note that the JOIN columns will only figure once in the resulting .
  • +
  • INNER JOIN or equi-join: This JOIN operation performs a cartesian product (CROSS JOIN) with a being applied to the resulting . Most often, a comparing foreign keys and primary keys will be applied as a filter, but any other predicate will work, too.
  • +
  • OUTER JOIN: This JOIN operation performs a cartesian product (CROSS JOIN) with a being applied to the resulting . Most often, a comparing foreign keys and primary keys will be applied as a filter, but any other predicate will work, too. Unlike the INNER JOIN, an OUTER JOIN will add "empty records" to the left (table A) or right (table B) or both tables, in case the conditional expression fails to produce a .
  • +
  • semi-join: In SQL, this JOIN operation can only be expressed implicitly using or . The resulting from a semi-join will only contain the left-hand side table A
  • +
  • anti-join: In SQL, this JOIN operation can only be expressed implicitly using or . The resulting from a semi-join will only contain the left-hand side table A
  • +
  • division: This JOIN operation is hard to express at all, in SQL. See the manual's chapter about for details on how jOOQ emulates this operation.
  • +
+

+ jOOQ supports all of these JOIN types (including semi-join and anti-join) directly on any : +

+ + table) + +// INNER JOIN +TableOnStep join(TableLike) +TableOnStep innerJoin(TableLike) + +// OUTER JOIN (supporting Oracle's partitioned OUTER JOIN) +TablePartitionByStep leftJoin(TableLike) +TablePartitionByStep leftOuterJoin(TableLike) + +TablePartitionByStep rightJoin(TableLike) +TablePartitionByStep rightOuterJoin(TableLike) + +// FULL OUTER JOIN +TableOnStep fullOuterJoin(TableLike) + +// SEMI JOIN +TableOnStep leftSemiJoin(TableLike); + +// ANTI JOIN +TableOnStep leftAntiJoin(TableLike); + +// CROSS JOIN +Table crossJoin(TableLike) + +// NATURAL JOIN +Table naturalJoin(TableLike) +Table naturalLeftOuterJoin(TableLike) +Table naturalRightOuterJoin(TableLike)]]> + +

+ Most of the above JOIN types are overloaded also to accommodate use-cases for convenience: +

+ +) + +// Overloaded versions taking SQL template strings with bind variables, or other forms of +// "plain SQL" QueryParts: +TableOnStep join(String) +TableOnStep join(String, Object...) +TableOnStep join(String, QueryPart...) +TableOnStep join(SQL) +TableOnStep join(Name)]]> + +

+ Note that most of jOOQ's JOIN operations give way to a similar DSL API hierarchy as previously seen in the manual's section about the +

+
+
+ +
+ The VALUES() table constructor + +

+ Some databases allow for expressing in-memory temporary tables using a VALUES() constructor. This constructor usually works the same way as the VALUES() clause known from the or from the . With jOOQ, you can also use the VALUES() table constructor, to create tables that can be used in a : +

+ + +  + + + +

+ Note, that it is usually quite useful to provide column aliases ("derived column lists") along with the table alias for the VALUES() constructor. +

+ +

+ The above statement is emulated by jOOQ for those databases that do not support the VALUES() constructor, natively (actual emulations may vary): +

+ + +
+
+ +
+ Nested SELECTs + +

+ A can appear almost anywhere a can. Such a "nested SELECT" is often called a "derived table". Apart from many convenience methods accepting objects directly, a SELECT statement can always be transformed into a object using the asTable() method. +

+ +

Example: Scalar subquery

+ + +  +create.select() + .from(BOOK) + .where(BOOK.AUTHOR_ID.equal(create + .select(AUTHOR.ID) + .from(AUTHOR) + .where(AUTHOR.LAST_NAME.equal("Orwell")))) + .fetch(); + + +

Example: Derived table

+ + +   + nested = + create.select(BOOK.AUTHOR_ID, count().as("books")) + .from(BOOK) + .groupBy(BOOK.AUTHOR_ID).asTable("nested"); + +create.select(nested.fields()) + .from(nested) + .orderBy(nested.field("books")) + .fetch();]]> + + +

Example: Correlated subquery

+ +   + +Field books = + create.selectCount() + .from(BOOK) + .where(BOOK.AUTHOR_ID.equal(AUTHOR.ID)) + .asField("books"); + +create.select(AUTHOR.ID, books) + .from(AUTHOR) + .orderBy(books, AUTHOR.ID)) + .fetch();]]> + + + + +
+ The Oracle 11g PIVOT clause + +

+ If you are closely coupling your application to an Oracle database, you can take advantage of some Oracle-specific features, such as the PIVOT clause, used for statistical analyses. The formal syntax definition is as follows: +

+ +-- SELECT .. + FROM table PIVOT (aggregateFunction [, aggregateFunction] FOR column IN (expression [, expression])) +-- WHERE .. + +

+ The PIVOT clause is available from the type, as pivoting is done directly on a table. Currently, only Oracle's PIVOT clause is supported. Support for SQL Server's slightly different PIVOT clause will be added later. Also, jOOQ may emulate PIVOT for other dialects in the future. +

+
+
+ +
+ jOOQ's relational division syntax + +

+ There is one operation in relational algebra that is not given a lot of attention, because it is rarely used in real-world applications. It is the relational division, the opposite operation of the cross product (or, relational multiplication). The following is an approximate definition of a relational division: +

+ +Assume the following cross join / cartesian product +C = A × B + +Then it can be said that +A = C ÷ B +B = C ÷ A + +

+ With jOOQ, you can simplify using relational divisions by using the following syntax: +

+ +C.divideBy(B).on(C.ID.equal(B.C_ID)).returning(C.TEXT) + +

+ The above roughly translates to +

+ +SELECT DISTINCT C.TEXT FROM C "c1" +WHERE NOT EXISTS ( + SELECT 1 FROM B + WHERE NOT EXISTS ( + SELECT 1 FROM C "c2" + WHERE "c2".TEXT = "c1".TEXT + AND "c2".ID = B.C_ID + ) +) + +

+ Or in plain text: Find those TEXT values in C whose ID's correspond to all ID's in B. Note that from the above SQL statement, it is immediately clear that proper indexing is of the essence. Be sure to have indexes on all columns referenced from the on(...) and returning(...) clauses. +

+ +

+ For more information about relational division and some nice, real-life examples, see +

+ + +
+ +
+ +
+ Array and cursor unnesting + +

+ The SQL standard specifies how SQL databases should implement ARRAY and TABLE types, as well as CURSOR types. Put simply, a CURSOR is a pointer to any materialised . Depending on the cursor's features, this table expression can be scrolled through in both directions, records can be locked, updated, removed, inserted, etc. Often, CURSOR types contain s, whereas ARRAY and TABLE types contain simple scalar values, although that is not a requirement +

+ +

+ ARRAY types in SQL are similar to Java's array types. They contain a "component type" or "element type" and a "dimension". This sort of ARRAY type is implemented in H2, HSQLDB and Postgres and supported by jOOQ as such. Oracle uses strongly-typed arrays, which means that an ARRAY type (VARRAY or TABLE type) has a name and possibly a maximum capacity associated with it. +

+ +

Unnesting array and cursor types

+

+ The real power of these types become more obvious when you fetch them from to unnest them as and use them in your . An example is given here, where Oracle's DBMS_XPLAN package is used to fetch a cursor containing data about the most recent execution plan: +

+ + +   + + +

+ Note, in order to access the DbmsXplan package, you can use the to generate Oracle's SYS schema. +

+
+
+ + +
+ Table-valued functions + +

+ Some databases support functions that can produce tables for use in arbitrary . jOOQ supports these functions out-of-the-box for such databases. For instance, in SQL Server, the following function produces a table of (ID, TITLE) columns containing either all the books or just one book by ID: +

+ + + +

+ The jOOQ code generator will now produce a from the above, which can be used as a SQL function: +

+ + r1 = create.selectFrom(fBooks(null)).fetch(); + +// Lateral joining the table-valued function to another table using CROSS APPLY: +create.select(BOOK.ID, F_BOOKS.TITLE) + .from(BOOK.crossApply(fBooks(BOOK.ID))) + .fetch();]]> + +
+
+¨ + +
+ The DUAL table + +

+ The SQL standard specifies that the is optional in a . However, according to the standard, you may then no longer use some other clauses, such as the . In the real world, there exist three types of databases: +

+
    +
  • The ones that always require a FROM clause (as required by the SQL standard)
  • +
  • The ones that never require a FROM clause (and still allow a WHERE clause)
  • +
  • The ones that require a FROM clause only with a WHERE clause, GROUP BY clause, or HAVING clause
  • +
+

+ With jOOQ, you don't have to worry about the above distinction of SQL dialects. jOOQ never requires a FROM clause, but renders the necessary "DUAL" table, if needed. The following program shows how jOOQ renders "DUAL" tables +

+ + + + + + +

+ Note, that some databases (H2, MySQL) can normally do without "DUAL". However, there exist some corner-cases with complex nested SELECT statements, where this will cause syntax errors (or parser bugs). To stay on the safe side, jOOQ will always render "dual" in those dialects. +

+
+
+ + + +
+ Column expressions + +

+ Column expressions can be used in various SQL clauses in order to refer to one or several columns. This chapter explains how to form various types of column expressions with jOOQ. A particular type of column expression is given in the section about , where an expression may have a degree of more than one. +

+ +

Using column expressions in jOOQ

+

+ jOOQ allows you to freely create arbitrary column expressions using a fluent expression construction API. Many expressions can be formed as functions from , other expressions can be formed based on a pre-existing column expression. For example: +

+ + field1 = BOOK.TITLE; + +// A function created from the DSL using "prefix" notation +Field field2 = trim(BOOK.TITLE); + +// The same function created from a pre-existing Field using "postfix" notation +Field field3 = BOOK.TITLE.trim(); + +// More complex function with advanced DSL syntax +Field field4 = listAgg(BOOK.TITLE) + .withinGroupOrderBy(BOOK.ID.asc()) + .over().partitionBy(AUTHOR.ID);]]> + +

+ In general, it is up to you whether you want to use the "prefix" notation or the "postfix" notation to create new column expressions based on existing ones. The "SQL way" would be to use the "prefix notation", with functions created from the . The "Java way" or "object-oriented way" would be to use the "postfix" notation with functions created from objects. Both ways ultimately create the same query part, though. +

+
+ + +
+ Table columns + +

+ Table columns are the most simple implementations of a . They are mainly produced by jOOQ's and can be dereferenced from the generated tables. This manual is full of examples involving table columns. Another example is given in this query: +

+ + +   + + + +

+ Table columns implement a more specific interface called , which is parameterised with its associated <R extends Record> record type. +

+

+ See the manual's section about for more information about what is really generated by the +

+
+
+ +
+ Aliased columns + +

+ Just like , columns can be renamed using aliases. Here is an example: +

+ + SELECT FIRST_NAME || ' ' || LAST_NAME author, COUNT(*) books + FROM AUTHOR + JOIN BOOK ON AUTHOR.ID = AUTHOR_ID +GROUP BY FIRST_NAME, LAST_NAME; + +

+ Here is how it's done with jOOQ: +

+ +Record record = create.select( + concat(AUTHOR.FIRST_NAME, val(" "), AUTHOR.LAST_NAME).as("author"), + count().as("books")) + .from(AUTHOR) + .join(BOOK).on(AUTHOR.ID.equal(BOOK.AUTHOR_ID)) + .groupBy(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .fetchAny(); + +

+ When you alias Fields like above, you can access those Fields' values using the alias name: +

+ +System.out.println("Author : " + record.getValue("author")); +System.out.println("Books : " + record.getValue("books")); + +
+
+ +
+ Cast expressions + +

+ jOOQ's source code generator tries to find the most accurate type mapping between your vendor-specific data types and a matching Java type. For instance, most VARCHAR, CHAR, CLOB types will map to String. Most BINARY, BYTEA, BLOB types will map to byte[]. NUMERIC types will default to , but can also be any of , , , , , , . +

+

+ Sometimes, this automatic mapping might not be what you needed, or jOOQ cannot know the type of a field. In those cases you would write SQL type CAST like this: +

+ +-- Let's say, your Postgres column LAST_NAME was VARCHAR(30) +-- Then you could do this: +SELECT CAST(AUTHOR.LAST_NAME AS TEXT) FROM DUAL + +

+ in jOOQ, you can write something like that: +

+ +create.select(TAuthor.LAST_NAME.cast(PostgresDataType.TEXT)).fetch(); + +

+ The same thing can be achieved by casting a Field directly to String.class, as TEXT is the default data type in Postgres to map to Java's String +

+ +create.select(TAuthor.LAST_NAME.cast(String.class)).fetch(); + +

+ The complete CAST API in consists of these three methods: +

+ + { + + // Cast this field to the type of another field + Field cast(Field field); + + // Cast this field to a given DataType + Field cast(DataType type); + + // Cast this field to the default DataType for a given Class + Field cast(Class type); +} + +// And additional convenience methods in the DSL: +public class DSL { + Field cast(Object object, Field field); + Field cast(Object object, DataType type); + Field cast(Object object, Class type); + Field castNull(Field field); + Field castNull(DataType type); + Field castNull(Class type); +}]]> +
+
+ +
+ Datatype coercions + +

+ A slightly different use case than are data type coercions, which are not rendered through to generated SQL. Sometimes, you may want to pretend that a numeric value is really treated as a string value, for instance when binding a numeric : +

+ + field1 = val(1).coerce(String.class); +Field field2 = val("1").coerce(Integer.class);]]> + +

+ In the above example, field1 will be treated by jOOQ as a Field<String>, binding the numeric literal 1 as a VARCHAR value. The same applies to field2, whose string literal "1" will be bound as an INTEGER value. +

+ +

+ This technique is better than performing unsafe or rawtype casting in Java, if you cannot access the "right" field type from any given expression. +

+
+
+ +
+ Arithmetic expressions + +

Numeric arithmetic expressions

+

+ Your database can do the math for you. Arithmetic operations are implemented just like , with similar limitations as far as type restrictions are concerned. You can use any of these operators: +

+ + + - * / % + +

+ In order to express a SQL query like this one: +

+ +SELECT ((1 + 2) * (5 - 3) / 2) % 10 FROM DUAL + +

+ You can write something like this in jOOQ: +

+ +create.select(val(1).add(2).mul(val(5).sub(3)).div(2).mod(10)).fetch(); + +

Operator precedence

+

+ jOOQ does not know any operator precedence (see also ). All operations are evaluated from left to right, as with any object-oriented API. The two following expressions are the same: +

+ + val(1).add(2) .mul(val(5).sub(3)) .div(2) .mod(10); +(((val(1).add(2)).mul(val(5).sub(3))).div(2)).mod(10); + +

Datetime arithmetic expressions

+

+ jOOQ also supports the Oracle-style syntax for adding days to a Field<? extends java.util.Date> +

+ + + SELECT SYSDATE + 3 FROM DUAL; + create.select(currentTimestamp().add(3)).fetch(); + + +

+ For more advanced datetime arithmetic, use the DSL's timestampDiff() and dateDiff() functions, as well as jOOQ's built-in SQL standard INTERVAL data type support: +

+
    +
  • INTERVAL YEAR TO MONTH:
  • +
  • INTERVAL DAY TO SECOND:
  • +
+
+
+ +
+ String concatenation + +

+ The SQL standard defines the concatenation operator to be an infix operator, similar to the ones we've seen in the chapter about . This operator looks like this: ||. Some other dialects do not support this operator, but expect a concat() function, instead. jOOQ renders the right operator / function, depending on your : +

+ + +SELECT 'A' || 'B' || 'C' FROM DUAL +-- Or in MySQL: +SELECT concat('A', 'B', 'C') FROM DUAL +  +// For all RDBMS, including MySQL: +create.select(concat("A", "B", "C")).fetch(); + + +
+
+ +
+ General functions + +

+ There are a variety of general functions supported by jOOQ As discussed in the chapter about functions are mostly emulated in your database, in case they are not natively supported. +

+

+ This is a list of general functions supported by jOOQ's : +

+
    +
  • COALESCE: Get the first non-null value in a list of arguments.
  • +
  • NULLIF: Return NULL if both arguments are equal, or the first argument, otherwise.
  • +
  • NVL: Get the first non-null value among two arguments.
  • +
  • NVL2: Get the second argument if the first is null, or the third argument, otherwise.
  • +
+ +

+ Please refer to the for more details. +

+
+
+ +
+ Numeric functions + +

+ Math can be done efficiently in the database before returning results to your Java application. In addition to the discussed previously, jOOQ also supports a variety of numeric functions. As discussed in the chapter about numeric functions (as any function type) are mostly emulated in your database, in case they are not natively supported. +

+

+ This is a list of numeric functions supported by jOOQ's : +

+ +
    +
  • ABS: Get the absolute value of a value.
  • +
  • ACOS: Get the arc cosine of a value.
  • +
  • ASIN: Get the arc sine of a value.
  • +
  • ATAN: Get the arc tangent of a value.
  • +
  • ATAN2: Get the atan2 function of two values.
  • +
  • CEIL: Get the smalles integer value larger than a given numeric value.
  • +
  • COS: Get the cosine of a value.
  • +
  • COSH: Get the hyperbolic cosine of a value.
  • +
  • COT: Get the cotangent of a value.
  • +
  • COTH: Get the hyperbolic cotangent of a value.
  • +
  • DEG: Transform radians into degrees.
  • +
  • EXP: Calculate e^value.
  • +
  • FLOOR: Get the largest integer value smaller than a given numeric value.
  • +
  • GREATEST: Finds the greatest among all argument values (can also be used with non-numeric values).
  • +
  • LEAST: Finds the least among all argument values (can also be used with non-numeric values).
  • +
  • LN: Get the natural logarithm of a value.
  • +
  • LOG: Get the logarithm of a value given a base.
  • +
  • POWER: Calculate value^exponent.
  • +
  • RAD: Transform degrees into radians.
  • +
  • RAND: Get a random number.
  • +
  • ROUND: Rounds a value to the nearest integer.
  • +
  • SIGN: Get the sign of a value (-1, 0, 1).
  • +
  • SIN: Get the sine of a value.
  • +
  • SINH: Get the hyperbolic sine of a value.
  • +
  • SQRT: Calculate the square root of a value.
  • +
  • TAN: Get the tangent of a value.
  • +
  • TANH: Get the hyperbolic tangent of a value.
  • +
  • TRUNC: Truncate the decimals off a given value.
  • +
+ +

+ Please refer to the for more details. +

+
+
+ +
+ Bitwise functions + +

+ Interestingly, bitwise functions and bitwise arithmetic is not very popular among SQL databases. Most databases only support a few bitwise operations, while others ship with the full set of operators. jOOQ's API includes most bitwise operations as listed below. In order to avoid ambiguities with , all bitwise functions are prefixed with "bit" +

+
    +
  • BIT_COUNT: Count the number of bits set to 1 in a number
  • +
  • BIT_AND: Set only those bits that are set in two numbers
  • +
  • BIT_OR: Set all bits that are set in at least one number
  • +
  • BIT_NAND: Set only those bits that are set in two numbers, and inverse the result
  • +
  • BIT_NOR: Set all bits that are set in at least one number, and inverse the result
  • +
  • BIT_NOT: Inverse the bits in a number
  • +
  • BIT_XOR: Set all bits that are set in at exactly one number
  • +
  • BIT_XNOR: Set all bits that are set in at exactly one number, and inverse the result
  • +
  • SHL: Shift bits to the left
  • +
  • SHR: Shift bits to the right
  • +
+ +

Some background about bitwise operation emulation

+

+ As stated before, not all databases support all of these bitwise operations. jOOQ emulates them wherever this is possible. More details can be seen in this blog post:
+ http://blog.jooq.org/2011/10/30/the-comprehensive-sql-bitwise-operations-compatibility-list/ +

+ +
+
+ +
+ String functions + +

+ String formatting can be done efficiently in the database before returning results to your Java application. As discussed in the chapter about string functions (as any function type) are mostly emulated in your database, in case they are not natively supported. +

+

+ This is a list of numeric functions supported by jOOQ's : +

+ +
    +
  • ASCII: Get the ASCII code of a character.
  • +
  • BIT_LENGTH: Get the length of a string in bits.
  • +
  • CHAR_LENGTH: Get the length of a string in characters.
  • +
  • CONCAT: Concatenate several strings.
  • +
  • ESCAPE: Escape a string for use with the .
  • +
  • LENGTH: Get the length of a string.
  • +
  • LOWER: Get a string in lower case letters.
  • +
  • LPAD: Pad a string on the left side.
  • +
  • LTRIM: Trim a string on the left side.
  • +
  • OCTET_LENGTH: Get the length of a string in octets.
  • +
  • POSITION: Find a string within another string.
  • +
  • REPEAT: Repeat a string a given number of times.
  • +
  • REPLACE: Replace a string within another string.
  • +
  • RPAD: Pad a string on the right side.
  • +
  • RTRIM: Trim a string on the right side.
  • +
  • SUBSTRING: Get a substring of a string.
  • +
  • TRIM: Trim a string on both sides.
  • +
  • UPPER: Get a string in upper case letters.
  • +
+ +

+ Please refer to the for more details. +

+ +

Regular expressions, REGEXP, REGEXP_LIKE, etc.

+

+ Various databases have some means of searching through columns using regular expressions if the does not provide sufficient pattern matching power. While there are many different functions and operators in the various databases, jOOQ settled for the SQL:2008 standard REGEX_LIKE operator. Being an operator (and not a function), you should use the corresponding method on : +

+ + + +

+ Note that the SQL standard specifies that patterns should follow the XQuery standards. In the real world, the POSIX regular expression standard is the most used one, some use Java regular expressions, and only a few ones use Perl regular expressions. jOOQ does not make any assumptions about regular expression syntax. For cross-database compatibility, please read the relevant database manuals carefully, to learn about the appropriate syntax. Please refer to the for more details. +

+
+ +
+ +
+ Case sensitivity with strings + +

+ Most databases allow for specifying a COLLATION which allows for re-defining the ordering of string values. By default, ASCII, ISO, or Unicode encodings are applied to character data, and ordering is applied according to the respective encoding. +

+

+ Sometimes, however, certain queries like to ignore parts of the encoding by treating upper-case and lower-case characters alike, such that ABC = abc, or such that ABC, jkl, XyZ are an ordered list of strings (case-insensitively). +

+

+ For these ad-hoc ordering use-cases, most people resort to using LOWER() or UPPER() as follows: +

+ + +-- Case-insensitive filtering: +SELECT * FROM BOOK +WHERE upper(TITLE) = 'ANIMAL FARM' + +-- Case-insensitive ordering: +SELECT * +FROM AUTHOR +ORDER BY upper(FIRST_NAME), upper(LAST_NAME) +// Case-insensitive filtering: +create.selectFrom(BOOK) + .where(upper(BOOK.TITLE).eq("ANIMAL FARM")).fetch(); + +// Case-insensitive ordering: +create.selectFrom(AUTHOR) + .orderBy(upper(AUTHOR.FIRST_NAME), upper(AUTHOR.LAST_NAME)) + .fetch(); + + + +
+
+ +
+ Date and time functions + +

+ This is a list of date and time functions supported by jOOQ's : +

+ +
    +
  • CURRENT_DATE: Get current date as a DATE object.
  • +
  • CURRENT_TIME: Get current time as a TIME object.
  • +
  • CURRENT_TIMESTAMP: Get current date as a TIMESTAMP object.
  • +
  • DATE_ADD: Add a number of days or an interval to a date.
  • +
  • DATE_DIFF: Get the difference in days between two dates.
  • +
  • TIMESTAMP_ADD: Add a number of days or an interval to a timestamp.
  • +
  • TIMESTAMP_DIFF: Get the difference as an INTERVAL DAY TO SECOND between two dates.
  • +
+ +

Intervals in jOOQ

+

+ jOOQ fills a gap opened by JDBC, which neglects an important SQL data type as defined by the SQL standards: INTERVAL types. See the manual's section about for more details. +

+
+
+ +
+ System functions + +

+ This is a list of system functions supported by jOOQ's : +

+
    +
  • CURRENT_USER: Get current user.
  • +
+
+
+ +
+ Aggregate functions + +

+ Aggregate functions work just like functions, even if they have a slightly different semantics. Here are some example aggregate functions from the : +

+ + count(); +AggregateFunction count(Field field); +AggregateFunction max (Field field); +AggregateFunction min (Field field); +AggregateFunction sum (Field field); +AggregateFunction avg (Field field); + +// DISTINCT keyword in aggregate functions +AggregateFunction countDistinct(Field field); +AggregateFunction maxDistinct (Field field); +AggregateFunction minDistinct (Field field); +AggregateFunction sumDistinct (Field field); +AggregateFunction avgDistinct (Field field); + +// String aggregate functions +AggregateFunction groupConcat (Field field); +AggregateFunction groupConcatDistinct(Field field); +OrderedAggregateFunction listAgg(Field field); +OrderedAggregateFunction listAgg(Field field, String separator); + +// Statistical functions +AggregateFunction median (Field field); +AggregateFunction stddevPop (Field field); +AggregateFunction stddevSamp(Field field); +AggregateFunction varPop (Field field); +AggregateFunction varSamp (Field field); + +// Linear regression functions +AggregateFunction regrAvgX (Field y, Field x); +AggregateFunction regrAvgY (Field y, Field x); +AggregateFunction regrCount (Field y, Field x); +AggregateFunction regrIntercept(Field y, Field x); +AggregateFunction regrR2 (Field y, Field x); +AggregateFunction regrSlope (Field y, Field x); +AggregateFunction regrSXX (Field y, Field x); +AggregateFunction regrSXY (Field y, Field x); +AggregateFunction regrSYY (Field y, Field x);]]> + +

+ Here's an example, counting the number of books any author has written: +

+ + +SELECT AUTHOR_ID, COUNT(*) +FROM BOOK +GROUP BY AUTHOR_ID +  +create.select(BOOK.AUTHOR_ID, count()) + .from(BOOK) + .groupBy(BOOK.AUTHOR_ID) + .fetch(); + + +

+ Aggregate functions have strong limitations about when they may be used and when not. For instance, you can use aggregate functions in scalar queries. Typically, this means you only select aggregate functions, no or other . Another use case is to use them along with a as seen in the previous example. Note, that jOOQ does not check whether your using of aggregate functions is correct according to the SQL standards, or according to your database's behaviour. +

+ +

Ordered-set aggregate functions

+

+ Oracle and some other databases support "ordered-set aggregate functions". This means you can provide an ORDER BY clause to an aggregate function, which will be taken into consideration when aggregating. The best example for this is Oracle's LISTAGG() (also known as GROUP_CONCAT in other ). The following query groups by authors and concatenates their books' titles +

+ +SELECT LISTAGG(TITLE, ', ') + WITHIN GROUP (ORDER BY TITLE) +FROM BOOK +GROUP BY AUTHOR_ID +  +create.select(listAgg(BOOK.TITLE, ", ") + .withinGroupOrderBy(BOOK.TITLE)) + .from(BOOK) + .groupBy(BOOK.AUTHOR_ID) + .fetch(); + + +

+ The above query might yield: +

+ ++---------------------+ +| LISTAGG | ++---------------------+ +| 1984, Animal Farm | +| O Alquimista, Brida | ++---------------------+ + +

FIRST and LAST: Oracle's "ranked" aggregate functions

+

+ Oracle allows for restricting aggregate functions using the KEEP() clause, which is supported by jOOQ. In Oracle, some aggregate functions (MIN, MAX, SUM, AVG, COUNT, VARIANCE, or STDDEV) can be restricted by this clause, hence also allows for specifying it. Here are a couple of examples using this clause: +

+ + +SUM(BOOK.AMOUNT_SOLD) + KEEP(DENSE_RANK FIRST ORDER BY BOOK.AUTHOR_ID) +sum(BOOK.AMOUNT_SOLD) + .keepDenseRankFirstOrderBy(BOOK.AUTHOR_ID) + + +

User-defined aggregate functions

+

+ jOOQ also supports using your own user-defined aggregate functions. See the manual's section about for more details. +

+ +

Window functions / analytical functions

+

+ In those databases that support , jOOQ's can be transformed into a window function / analytical function by calling over() on it. See the manual's section about for more details. +

+
+
+ +
+ Window functions + +

+ Most major RDBMS support the concept of window functions. jOOQ knows of implementations in DB2, Oracle, Postgres, SQL Server, and Sybase SQL Anywhere, and supports most of their specific syntaxes. Note, that H2 and HSQLDB have implemented ROW_NUMBER() functions, without true windowing support. +

+

+ As previously discussed, any can be transformed into a window function using the over() method. See the chapter about for details. In addition to those, there are also some more window functions supported by jOOQ, as declared in the : +

+ + rowNumber(); + WindowOverStep rank(); + WindowOverStep denseRank(); + WindowOverStep percentRank(); + +// Windowing functions + WindowIgnoreNullsStep firstValue(Field field); + WindowIgnoreNullsStep lastValue(Field field); + WindowIgnoreNullsStep nthValue(Field field, int nth); + WindowIgnoreNullsStep nthValue(Field field, Field nth); + WindowIgnoreNullsStep lead(Field field); + WindowIgnoreNullsStep lead(Field field, int offset); + WindowIgnoreNullsStep lead(Field field, int offset, T defaultValue); + WindowIgnoreNullsStep lead(Field field, int offset, Field defaultValue); + WindowIgnoreNullsStep lag(Field field); + WindowIgnoreNullsStep lag(Field field, int offset); + WindowIgnoreNullsStep lag(Field field, int offset, T defaultValue); + WindowIgnoreNullsStep lag(Field field, int offset, Field defaultValue); + +// Statistical functions + WindowOverStep cumeDist(); + WindowOverStep ntile(int number); + +// Inverse distribution functions + OrderedAggregateFunction precentileCont(Number number); + OrderedAggregateFunction precentileCont(Field number); + OrderedAggregateFunction precentileDisc(Number number); + OrderedAggregateFunction precentileDisc(Field number);]]> + +

+ SQL distinguishes between various window function types (e.g. "ranking functions"). Depending on the function, SQL expects mandatory PARTITION BY or ORDER BY clauses within the OVER() clause. jOOQ does not enforce those rules for two reasons: +

+
    +
  • Your JDBC driver or database already checks SQL syntax semantics
  • +
  • Not all databases behave correctly according to the SQL standard
  • +
+

+ If possible, however, jOOQ tries to render missing clauses for you, if a given is more restrictive. +

+

Some examples

+

+ Here are some simple examples of window functions with jOOQ: +

+ + +-- Sample uses of ROW_NUMBER() +ROW_NUMBER() OVER() +ROW_NUMBER() OVER(PARTITION BY 1) +ROW_NUMBER() OVER(ORDER BY BOOK.ID) +ROW_NUMBER() OVER(PARTITION BY BOOK.AUTHOR_ID ORDER BY BOOK.ID) + +-- Sample uses of FIRST_VALUE +FIRST_VALUE(BOOK.ID) OVER() +FIRST_VALUE(BOOK.ID IGNORE NULLS) OVER() +FIRST_VALUE(BOOK.ID RESPECT NULLS) OVER() + +// Sample uses of rowNumber() +rowNumber().over() +rowNumber().over().partitionByOne() +rowNumber().over().partitionBy(BOOK.AUTHOR_ID) +rowNumber().over().partitionBy(BOOK.AUTHOR_ID).orderBy(BOOK.ID) + +// Sample uses of firstValue() +firstValue(BOOK.ID).over() +firstValue(BOOK.ID).ignoreNulls().over() +firstValue(BOOK.ID).respectNulls().over() + + + +

An advanced window function example

+

+ Window functions can be used for things like calculating a "running total". The following example fetches transactions and the running total for every transaction going back to the beginning of the transaction table (ordered by booked_at). Window functions are accessible from the previously seen type using the over() method: +

+ + +SELECT booked_at, amount, + SUM(amount) OVER (PARTITION BY 1 + ORDER BY booked_at + ROWS BETWEEN UNBOUNDED PRECEDING + AND CURRENT ROW) AS total + FROM transactions +  +create.select(t.BOOKED_AT, t.AMOUNT, + sum(t.AMOUNT).over().partitionByOne() + .orderBy(t.BOOKED_AT) + .rowsBetweenUnboundedPreceding() + .andCurrentRow().as("total") + .from(TRANSACTIONS.as("t")) + .fetch(); + + + +

Window functions created from ordered-set aggregate functions

+

+ In the previous chapter about , we have seen the concept of "ordered-set aggregate functions", such as Oracle's LISTAGG(). These functions have a window function / analytical function variant, as well. For example: +

+ + +SELECT LISTAGG(TITLE, ', ') + WITHIN GROUP (ORDER BY TITLE) + OVER (PARTITION BY BOOK.AUTHOR_ID) +FROM BOOK +  +create.select(listAgg(BOOK.TITLE, ", ") + .withinGroupOrderBy(BOOK.TITLE) + .over().partitionBy(BOOK.AUTHOR_ID)) + .from(BOOK) + .fetch(); + + +

Window functions created from Oracle's FIRST and LAST aggregate functions

+

+ In the previous chapter about , we have seen the concept of "FIRST and LAST aggregate functions". These functions have a window function / analytical function variant, as well. For example: +

+ + +SUM(BOOK.AMOUNT_SOLD) + KEEP(DENSE_RANK FIRST ORDER BY BOOK.AUTHOR_ID) + OVER(PARTITION BY 1) +sum(BOOK.AMOUNT_SOLD) + .keepDenseRankFirstOrderBy(BOOK.AUTHOR_ID) + .over().partitionByOne(); + + + +

Window functions created from user-defined aggregate functions

+

+ User-defined aggregate functions also implement , hence they can also be transformed into window functions using over(). This is supported by Oracle in particular. See the manual's section about for more details. +

+
+
+ +
+ Grouping functions + +

ROLLUP() explained in SQL

+

+ The SQL standard defines special functions that can be used in the : the grouping functions. These functions can be used to generate several groupings in a single clause. This can best be explained in SQL. Let's take ROLLUP() for instance: +

+ + + + + + +

+ In English, the ROLLUP() grouping function provides N+1 groupings, when N is the number of arguments to the ROLLUP() function. Each grouping has an additional group field from the ROLLUP() argument field list. The results of the second query might look something like this: +

+ + + +

CUBE() explained in SQL

+

+ CUBE() is different from ROLLUP() in the way that it doesn't just create N+1 groupings, it creates all 2^N possible combinations between all group fields in the CUBE() function argument list. Let's re-consider our second query from before: +

+ + + + + + +

+ The results would then hold: +

+ + + +

GROUPING SETS()

+

+ GROUPING SETS() are the generalised way to create multiple groupings. From our previous examples +

+
    +
  • ROLLUP(AUTHOR_ID, PUBLISHED_IN) corresponds to GROUPING SETS((AUTHOR_ID, PUBLISHED_IN), (AUTHOR_ID), ())
  • +
  • CUBE(AUTHOR_ID, PUBLISHED_IN) corresponds to GROUPING SETS((AUTHOR_ID, PUBLISHED_IN), (AUTHOR_ID), (PUBLISHED_IN), ())
  • +
+

+ This is nicely explained in the SQL Server manual pages about GROUPING SETS() and other grouping functions:
+ http://msdn.microsoft.com/en-us/library/bb510427(v=sql.105) +

+ +

jOOQ's support for ROLLUP(), CUBE(), GROUPING SETS()

+

+ jOOQ fully supports all of these functions, as well as the utility functions GROUPING() and GROUPING_ID(), used for identifying the grouping set ID of a record. The thus includes: +

+ +... fields); +GroupField cube(Field... fields); +GroupField groupingSets(Field... fields); +GroupField groupingSets(Field[]... fields); +GroupField groupingSets(Collection>... fields); + +// The utility functions generating IDs per GROUPING SET +Field grouping(Field); +Field groupingId(Field...);]]> + +

MySQL's and CUBRID's WITH ROLLUP syntax

+

+ MySQL and CUBRID don't know any grouping functions, but they support a WITH ROLLUP clause, that is equivalent to simple ROLLUP() grouping functions. jOOQ emulates ROLLUP() in MySQL and CUBRID, by rendering this WITH ROLLUP clause. The following two statements mean the same: +

+ + + + + +
+
+ +
+ User-defined functions + +

+ Some databases support user-defined functions, which can be embedded in any SQL statement, if you're using jOOQ's . Let's say you have the following simple function in Oracle SQL: +

+ + + +

+ The above function will be made available from a generated class. You can use it like any other : +

+ + + + + + +

+ Note that user-defined functions returning or data types can also be used wherever can be used, if they are +

+
+
+ +
+ User-defined aggregate functions + +

+ Some databases support user-defined aggregate functions, which can then be used along with or as . An example for such a database is Oracle. With Oracle, you can define the following OBJECT type (the example was taken from the Oracle 11g documentation): +

+ + SELF.MAX THEN + SELF.SECMAX := SELF.MAX; + SELF.MAX := VALUE; + ELSIF VALUE > SELF.SECMAX THEN + SELF.SECMAX := VALUE; + END IF; + RETURN ODCIConst.Success; +END; + +MEMBER FUNCTION ODCIAggregateTerminate(self IN U_SECOND_MAX, returnValue OUT NUMBER, flags IN NUMBER) RETURN NUMBER IS +BEGIN + RETURNVALUE := SELF.SECMAX; + RETURN ODCIConst.Success; +END; + +MEMBER FUNCTION ODCIAggregateMerge(self IN OUT U_SECOND_MAX, ctx2 IN U_SECOND_MAX) RETURN NUMBER IS +BEGIN + IF CTX2.MAX > SELF.MAX THEN + IF CTX2.SECMAX > SELF.SECMAX THEN + SELF.SECMAX := CTX2.SECMAX; + ELSE + SELF.SECMAX := SELF.MAX; + END IF; + SELF.MAX := CTX2.MAX; + ELSIF CTX2.MAX > SELF.SECMAX THEN + SELF.SECMAX := CTX2.MAX; + END IF; + RETURN ODCIConst.Success; +END; +END;]]> + +

+ The above OBJECT type is then available to function declarations as such: +

+ + + +

Using the generated aggregate function

+

+ jOOQ's will detect such aggregate functions and generate them differently from regular . They implement the type, as mentioned in the manual's section about . Here's how you can use the SECOND_MAX() aggregate function with jOOQ: +

+ + + + + +
+
+ +
+ The CASE expression + +

+ The CASE expression is part of the standard SQL syntax. While some RDBMS also offer an IF expression, or a DECODE function, you can always rely on the two types of CASE syntax: +

+ + + + + + +

+ In jOOQ, both syntaxes are supported (The second one is emulated in Derby, which only knows the first one). Unfortunately, both case and else are reserved words in Java. jOOQ chose to use decode() from the Oracle DECODE function, or choose(), and otherwise(), which means the same as else. +

+ +

+ A CASE expression can be used anywhere where you can place a . For instance, you can SELECT the above expression, if you're selecting from AUTHOR: +

+ +SELECT AUTHOR.FIRST_NAME, [... CASE EXPR ...] AS nationality + FROM AUTHOR + +

The Oracle DECODE() function

+

+ Oracle knows a more succinct, but maybe less readable DECODE() function with a variable number of arguments. This function roughly does the same as the second case expression syntax. jOOQ supports the DECODE() function and emulates it using CASE expressions in all dialects other than Oracle: +

+ + + + + + +

CASE clauses in an ORDER BY clause

+

+ Sort indirection is often implemented with a CASE clause of a SELECT's ORDER BY clause. See the manual's section about the for more details. +

+
+
+ +
+ Sequences and serials + +

+ Sequences implement the interface, providing essentially this functionality: +

+ + currval(); + +// Get a field for the NEXTVAL sequence property +Field nextval();]]> + +

+ So if you have a sequence like this in Oracle: +

+ +CREATE SEQUENCE s_author_id + +

+ You can then use your object directly in a SQL statement as such: +

+ + + +
    +
  • For more information about generated sequences, refer to the manual's section about
  • +
  • For more information about executing standalone calls to sequences, refer to the manual's section about
  • +
+
+
+ +
+ Tuples or row value expressions + +

+ According to the SQL standard, row value expressions can have a degree of more than one. This is commonly used in the , where the VALUES row value constructor allows for providing a row value expression as a source for INSERT data. Row value expressions can appear in various other places, though. They are supported by jOOQ as records / rows. jOOQ's allows for the construction of type-safe records up to the degree of {max-row-degree}. Higher-degree Rows are supported as well, but without any type-safety. Row types are modelled as follows: +

+ + Row1 row(T1 t1) { ... } +public static Row2 row(T1 t1, T2 t2) { ... } +public static Row3 row(T1 t1, T2 t2, T3 t3) { ... } +public static Row4 row(T1 t1, T2 t2, T3 t3, T4 t4) { ... } + +// [ ... idem for Row5, Row6, Row7, ..., Row{max-row-degree} ] + +// Degrees of more than {max-row-degree} are supported without type-safety +public static RowN row(Object... values) { ... }]]> + +

Using row value expressions in predicates

+

+ Row value expressions are incompatible with most other , but they can be used as a basis for constructing various , such as: +

+
    +
  • +
  • +
  • +
  • +
  • (for degree 2 row value expressions only)
  • +
+

+ See the relevant sections for more details about how to use row value expressions in predicates. +

+ +

Using row value expressions in UPDATE statements

+

+ The also supports a variant where row value expressions are updated, rather than single columns. See the relevant section for more details +

+ +

Higher-degree row value expressions

+

+ jOOQ chose to explicitly support degrees up to {max-row-degree} to match Scala's typesafe tuple, function and product support. Unlike Scala, however, jOOQ also supports higher degrees without the additional typesafety. +

+
+
+
+
+ +
+ Conditional expressions + +

+ Conditions or conditional expressions are widely used in SQL and in the jOOQ API. They can be used in +

+
    +
  • The
  • +
  • The (or JOIN .. ON clause, to be precise) of a , ,
  • +
  • The of a , ,
  • +
  • The of a
  • +
  • The of a
  • +
  • The 's ON clause
  • +
+ +

Boolean types in SQL

+

+ Before SQL:1999, boolean types did not really exist in SQL. They were modelled by 0 and 1 numeric/char values. With SQL:1999, true booleans were introduced and are now supported by most databases. In short, these are possible boolean values: +

+
    +
  • 1 or TRUE
  • +
  • 0 or FALSE
  • +
  • NULL or UNKNOWN
  • +
+

+ It is important to know that SQL differs from many other languages in the way it interprets the NULL boolean value. Most importantly, the following facts are to be remembered: +

+
    +
  • [ANY] = NULL yields NULL (not FALSE)
  • +
  • [ANY] != NULL yields NULL (not TRUE)
  • +
  • NULL = NULL yields NULL (not TRUE)
  • +
  • NULL != NULL yields NULL (not FALSE)
  • +
+

+ For simplified NULL handling, please refer to the section about the . +

+

+ Note that jOOQ does not model these values as actual compatible. +

+
+ + +
+ Condition building + +

+ With jOOQ, most are built from , calling various methods on them. For instance, to build a , you can write the following expression: +

+ + + + + + +

Create conditions from the DSL

+

+ There are a few types of conditions, that can be created statically from the . These are: +

+
    +
  • , that allow you to phrase your own SQL string
  • +
  • The , a standalone predicate that creates a conditional expression
  • +
  • Constant TRUE and FALSE conditional expressions
  • +
+ +

Connect conditions using boolean operators

+

+ Conditions can also be connected using as will be discussed in a subsequent chapter. +

+
+
+ +
+ AND, OR, NOT boolean operators + +

+ In SQL, as in most other languages, can be connected using the AND and OR binary operators, as well as the NOT unary operator, to form new conditional expressions. In jOOQ, this is modelled as such: +

+ + + + + + +

+ The above example shows that the number of parentheses in Java can quickly explode. Proper indentation may become crucial in making such code readable. In order to understand how jOOQ composes combined conditional expressions, let's assign component expressions first: +

+ + + +

The Condition API

+

+ Here are all boolean operators on the interface: +

+ +) // Combine conditions with AND. Convenience for adding an exists predicate to the rhs +andNot(Condition) // Combine conditions with AND. Convenience for adding an inverted condition to the rhs +andNotExists(Select) // Combine conditions with AND. Convenience for adding an inverted exists predicate to the rhs + +or(Condition) // Combine conditions with OR +or(String) // Combine conditions with OR. Convenience for adding plain SQL to the right-hand side +or(String, Object...) // Combine conditions with OR. Convenience for adding plain SQL to the right-hand side +or(String, QueryPart...) // Combine conditions with OR. Convenience for adding plain SQL to the right-hand side +orExists(Select) // Combine conditions with OR. Convenience for adding an exists predicate to the rhs +orNot(Condition) // Combine conditions with OR. Convenience for adding an inverted condition to the rhs +orNotExists(Select) // Combine conditions with OR. Convenience for adding an inverted exists predicate to the rhs + +not() // Invert a condition (synonym for DSL.not(Condition)]]> +
+
+ +
+ Comparison predicate + +

+ In SQL, comparison predicates are formed using common comparison operators: +

+
    +
  • = to test for equality
  • +
  • <> or != to test for non-equality
  • +
  • > to test for being strictly greater
  • +
  • >= to test for being greater or equal
  • +
  • < to test for being strictly less
  • +
  • <= to test for being less or equal
  • +
+ +

+ Unfortunately, Java does not support operator overloading, hence these operators are also implemented as methods in jOOQ, like any other SQL syntax elements. The relevant parts of the interface are these: +

+ +); // = (some column expression) +eq or equal(Select>); // = (some scalar SELECT statement) +ne or notEqual(T); // <> (some bind value) +ne or notEqual(Field); // <> (some column expression) +ne or notEqual(Select>); // <> (some scalar SELECT statement) +lt or lessThan(T); // < (some bind value) +lt or lessThan(Field); // < (some column expression) +lt or lessThan(Select>); // < (some scalar SELECT statement) +le or lessOrEqual(T); // <= (some bind value) +le or lessOrEqual(Field); // <= (some column expression) +le or lessOrEqual(Select>); // <= (some scalar SELECT statement) +gt or greaterThan(T); // > (some bind value) +gt or greaterThan(Field); // > (some column expression) +gt or greaterThan(Select>); // > (some scalar SELECT statement) +ge or greaterOrEqual(T); // >= (some bind value) +ge or greaterOrEqual(Field); // >= (some column expression) +ge or greaterOrEqual(Select>); // >= (some scalar SELECT statement)]]> + +

+ Note that every operator is represented by two methods. A verbose one (such as equal()) and a two-character one (such as eq()). Both methods are the same. You may choose either one, depending on your taste. The manual will always use the more verbose one. +

+ +

jOOQ's convenience methods using comparison operators

+

+ In addition to the above, jOOQ provides a few convenience methods for common operations performed on strings using comparison predicates: +

+ + + LOWER('animal farm')]]> + + +
+
+ +
+ Boolean operator precedence + +

+ As previously mentioned in the manual's section about , jOOQ does not implement operator precedence. All operators are evaluated from left to right, as expected in an object-oriented API. This is important to understand when combining , such as AND, OR, and NOT. The following expressions are equivalent: +

+ + + A.and(B) .or(C) .and(D) .or(E) +(((A.and(B)).or(C)).and(D)).or(E) + + +

+ In SQL, the two expressions wouldn't be the same, as SQL natively knows operator precedence. +

+ + + A AND B OR C AND D OR E -- Precedence is applied +(((A AND B) OR C) AND D) OR E -- Precedence is overridden + +
+
+ +
+ Comparison predicate (degree > 1) + +

+ All variants of the that we've seen in the previous chapter also work for . If your database does not support row value expression comparison predicates, jOOQ emulates them the way they are defined in the SQL standard: +

+ + + (X, Y) + +(A, B, C) > (X, Y, Z) + + +-- greater or equal +(A, B) >= (X, Y) + + +(A, B, C) >= (X, Y, Z) + + + +-- Inverse comparisons + +(A, B) <> (X, Y) +(A, B) < (X, Y) +(A, B) <= (X, Y)]]> + X) + OR ((A = X) AND (B > Y)) +(A > X) + OR ((A = X) AND (B > Y)) + OR ((A = X) AND (B = Y) AND (C > Z)) +-- greater or equal +(A > X) + OR ((A = X) AND (B > Y)) + OR ((A = X) AND (B = Y)) +(A > X) + OR ((A = X) AND (B > Y)) + OR ((A = X) AND (B = Y) AND (C > Z)) + OR ((A = X) AND (B = Y) AND (C = Z)) +-- For simplicity, these predicates are shown in terms +-- of their negated counter parts +NOT((A, B) = (X, Y)) +NOT((A, B) >= (X, Y)) +NOT((A, B) > (X, Y))]]> + + +

+ jOOQ supports all of the above row value expression comparison predicates, both with and at the right-hand side: +

+ + + + + +
+
+ +
+ Quantified comparison predicate + +

+ If the right-hand side of a turns out to be a non-scalar table subquery, you can wrap that subquery in a quantifier, such as ALL, ANY, or SOME. Note that the SQL standard defines ANY and SOME to be equivalent. jOOQ settled for the more intuitive ANY and doesn't support SOME. Here are some examples, supported by jOOQ: +

+ + + ALL(1920, 1940)]]> + + + +

+ For the example, the right-hand side of the quantified comparison predicates were filled with argument lists. But it is easy to imagine that the source of values results from a . +

+ +

ANY and the IN predicate

+

+ It is interesting to note that the SQL standard defines the in terms of the ANY-quantified predicate. The following two expressions are equivalent: +

+ + + + + + +

+ Typically, the is more readable than the quantified comparison predicate. +

+
+
+ +
+ NULL predicate + +

+ In SQL, you cannot compare NULL with any value using , as the result would yield NULL again, which is neither TRUE nor FALSE (see also the manual's section about ). In order to test a for NULL, use the NULL predicate as such: +

+ + + + + + +
+
+ +
+ NULL predicate (degree > 1) + +

+ The SQL NULL predicate also works well for , although it has some subtle, counter-intuitive features when it comes to inversing predicates with the NOT() operator! Here are some examples: +

+ + + + + + +

+ The SQL standard contains a nice truth table for the above rules: +

+ ++-----------------------+-----------+---------------+---------------+-------------------+ +| Expression | R IS NULL | R IS NOT NULL | NOT R IS NULL | NOT R IS NOT NULL | ++-----------------------+-----------+---------------+---------------+-------------------+ +| degree 1: null | true | false | false | true | +| degree 1: not null | false | true | true | false | +| degree > 1: all null | true | false | false | true | +| degree > 1: some null | false | false | true | true | +| degree > 1: none null | false | true | true | false | ++-----------------------+-----------+---------------+---------------+-------------------+ + +

+ In jOOQ, you would simply use the isNull() and isNotNull() methods on row value expressions. Again, as with the , the row value expression NULL predicate is emulated by jOOQ, if your database does not natively support it: +

+ + + +
+
+ +
+ DISTINCT predicate + +

+ Some databases support the DISTINCT predicate, which serves as a convenient, NULL-safe . With the DISTINCT predicate, the following truth table can be assumed: +

+
    +
  • [ANY] IS DISTINCT FROM NULL yields TRUE
  • +
  • [ANY] IS NOT DISTINCT FROM NULL yields FALSE
  • +
  • NULL IS DISTINCT FROM NULL yields FALSE
  • +
  • NULL IS NOT DISTINCT FROM NULL yields TRUE
  • +
+

+ For instance, you can compare two fields for distinctness, ignoring the fact that any of the two could be NULL, which would lead to funny results. This is supported by jOOQ as such: +

+ + + + + + +

+ If your database does not natively support the DISTINCT predicate, jOOQ emulates it with an equivalent , modelling the above truth table: +

+ + + + + + +

+ ... or better, if the INTERSECT set operation is supported: +

+ + + + + +
+
+ +
+ BETWEEN predicate + +

+ The BETWEEN predicate can be seen as syntactic sugar for a pair of . According to the SQL standard, the following two predicates are equivalent: +

+ + + += [B] AND [A] <= [C]]]> + + +

+ Note the inclusiveness of range boundaries in the definition of the BETWEEN predicate. Intuitively, this is supported in jOOQ as such: +

+ + + + + + +

BETWEEN SYMMETRIC

+

+ The SQL standard defines the SYMMETRIC keyword to be used along with BETWEEN to indicate that you do not care which bound of the range is larger than the other. A database system should simply swap range bounds, in case the first bound is greater than the second one. jOOQ supports this keyword as well, emulating it if necessary. +

+ + + + + + +

+ The emulation is done trivially: +

+ + + + + + +
+
+ +
+ BETWEEN predicate (degree > 1) + +

+ The SQL BETWEEN predicate also works well for . Much like the , it is defined in terms of a pair of regular : +

+ + + += [B] AND [A] <= [C] +([A] >= [B] AND [A] <= [C]) OR ([A] >= [C] AND [A] <= [B])]]> + + +

+ The above can be factored out according to the rules listed in the manual's section about . +

+ +

+ jOOQ supports the BETWEEN [SYMMETRIC] predicate and emulates it in all SQL dialects where necessary. An example is given here: +

+ + + +
+
+ +
+ LIKE predicate + +

+ LIKE predicates are popular for simple wildcard-enabled pattern matching. Supported wildcards in all SQL databases are: +

+
    +
  • _: (single-character wildcard)
  • +
  • %: (multi-character wildcard)
  • +
+

+ With jOOQ, the LIKE predicate can be created from any as such: +

+ + + + + + +

Escaping operands with the LIKE predicate

+

+ Often, your pattern may contain any of the wildcard characters "_" and "%", in case of which you may want to escape them. jOOQ does not automatically escape patterns in like() and notLike() methods. Instead, you can explicitly define an escape character as such: +

+ + + + + + +

+ In the above predicate expressions, the exclamation mark character is passed as the escape character to escape wildcard characters "!_" and "!%", as well as to escape the escape character itself: "!!" +

+

+ Please refer to your database manual for more details about escaping patterns with the LIKE predicate. +

+ +

jOOQ's convenience methods using the LIKE predicate

+

+ In addition to the above, jOOQ provides a few convenience methods for common operations performed on strings using the LIKE predicate. Typical operations are "contains predicates", "starts with predicates", "ends with predicates", etc. Here is the full convenience API wrapping LIKE predicates: +

+ + + + + + +

+ Note, that jOOQ escapes % and _ characters in value in some of the above predicate implementations. For simplicity, this has been omitted in this manual. +

+
+
+ +
+ IN predicate + +

+ In SQL, apart from comparing a value against several values, the IN predicate can be used to create semi-joins or anti-joins. jOOQ knows the following methods on the interface, to construct such IN predicates: +

+ +) // Construct an IN predicate from a collection of bind values +in(T...) // Construct an IN predicate from bind values +in(Field...) // Construct an IN predicate from column expressions +in(Select>) // Construct an IN predicate from a subselect +notIn(Collection) // Construct a NOT IN predicate from a collection of bind values +notIn(T...) // Construct a NOT IN predicate from bind values +notIn(Field...) // Construct a NOT IN predicate from column expressions +notIn(Select>) // Construct a NOT IN predicate from a subselect]]> + +

+ A sample IN predicate might look like this: +

+ + + + + + + +

NOT IN and NULL values

+

+ Beware that you should probably not have any NULL values in the right hand side of a NOT IN predicate, as the whole expression would evaluate to NULL, which is rarely desired. This can be shown informally using the following reasoning: +

+ +-- The following conditional expressions are formally or informally equivalent +A NOT IN (B, C) +A != ANY(B, C) +A != B AND A != C + +-- Substitute C for NULL, you'll get +A NOT IN (B, NULL) -- Substitute C for NULL +A != B AND A != NULL -- From the above rules +A != B AND NULL -- [ANY] != NULL yields NULL +NULL -- [ANY] AND NULL yields NULL + +

+ A good way to prevent this from happening is to use the for anti-joins, which is NULL-value insensitive. See the manual's section about to see a boolean truth table. +

+
+
+ +
+ IN predicate (degree > 1) + +

+ The SQL IN predicate also works well for . Much like the , it is defined in terms of a . The two expressions are equivalent: +

+ + + + + + +

+ jOOQ supports the IN predicate with row value expressions.An example is given here: +

+ + + + + + +

+ In both cases, i.e. when using an IN list or when using a subselect, the type of the predicate is checked. Both sides of the predicate must be of equal degree and row type. +

+ +

+ Emulation of the IN predicate where row value expressions aren't well supported is currently only available for IN predicates that do not take a subselect as an IN predicate value. +

+
+
+ +
+ EXISTS predicate + +

+ Slightly less intuitive, yet more powerful than the previously discussed is the EXISTS predicate, that can be used to form semi-joins or anti-joins. With jOOQ, the EXISTS predicate can be formed in various ways: +

+
    +
  • From the , using static methods. This is probably the most used case
  • +
  • From a using
  • +
  • From a using , and from other clauses
  • +
+ +

+ An example of an EXISTS predicate can be seen here: +

+ + + + + + +

+ Note that in SQL, the projection of a subselect in an EXISTS predicate is irrelevant. To help you write queries like the above, you can use jOOQ's selectZero() or selectOne() methods +

+ +

Performance of IN vs. EXISTS

+

+ In theory, the two types of predicates can perform equally well. If your database system ships with a sophisticated cost-based optimiser, it will be able to transform one predicate into the other, if you have all necessary constraints set (e.g. referential constraints, not null constraints). However, in reality, performance between the two might differ substantially. An interesting blog post investigating this topic on the MySQL database can be seen here:
+ http://blog.jooq.org/2012/07/27/not-in-vs-not-exists-vs-left-join-is-null-mysql/ +

+
+
+ +
+ OVERLAPS predicate + +

+ When comparing dates, the SQL standard allows for using a special OVERLAPS predicate, which checks whether two date ranges overlap each other. The following can be said: +

+ + + +

The OVERLAPS predicate in jOOQ

+

+ jOOQ supports the OVERLAPS predicate on . The following methods are contained in : +

+ + t1, Field t2); +Condition overlaps(Row2 row);]]> + +

+ This allows for expressing the above predicates as such: +

+ + + +

jOOQ's extensions to the standard

+

+ Unlike the standard (or any database implementing the standard), jOOQ also supports the OVERLAPS predicate for comparing arbitrary . For instance, (1, 3) OVERLAPS (2, 4) will yield true in jOOQ. This is emulated as such +

+ + +
+
+
+
+ +
+ Dynamic SQL + +

+ In most cases, , , and as introduced in the previous chapters will be embedded into different SQL statement clauses as if the statement were a static SQL statement (e.g. in a view or stored procedure): +

+ + + +

+ It is, however, interesting to think of all of the above expressions as what they are: expressions. And as such, nothing keeps users from extracting expressions and referencing them from outside the statement. The following statement is exactly equivalent: +

+ +[] select = { + AUTHOR.FIRST_NAME.concat(AUTHOR.LAST_NAME), + count() +}; +Table from = AUTHOR.join(BOOK).on(AUTHOR.ID.eq(BOOK.AUTHOR_ID)); +GroupField[] groupBy = { AUTHOR.ID, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME }; +SortField[] orderBy = { count().desc() }; + +create.select(select) + .from(from) + .groupBy(groupBy) + .orderBy() + .fetch();]]> + +

+ Each individual expression, and collection of expressions can be seen as an independent entity that can be +

+ +
    +
  1. Constructed dynamically
  2. +
  3. Reused across queries
  4. +
+ +

+ Dynamic construction is particularly useful in the case of the , for dynamic predicate building. For instance: +

+ + + +

+ The dynamic SQL building power may be one of the biggest advantages of using a runtime query model like the one offered by jOOQ. Queries can be created dynamically, of arbitrary complexity. In the above example, we've just constructed a dynamic . The same can be done for any other clauses, including dynamic , or adding additional as needed. +

+
+
+ +
+ Plain SQL + +

+ A DSL is a nice thing to have, it feels "fluent" and "natural", especially if it models a well-known language, such as SQL. But a DSL is always expressed in a host language (Java in this case), which was not made for exactly the same purposes as its hosted DSL. If it were, then jOOQ would be implemented on a compiler-level, similar to LINQ in .NET. But it's not, and so, the DSL is limited by language constraints of its host language. We have seen many functionalities where the DSL becomes a bit verbose. This can be especially true for: +

+
    +
  • +
  • +
  • +
  • +
+ +

+ You'll probably find other examples. If verbosity scares you off, don't worry. The verbose use-cases for jOOQ are rather rare, and when they come up, you do have an option. Just write SQL the way you're used to! +

+

+ jOOQ allows you to embed SQL as a String into any supported in these contexts: +

+
    +
  • Plain SQL as a
  • +
  • Plain SQL as a
  • +
  • Plain SQL as a
  • +
  • Plain SQL as a
  • +
  • Plain SQL as a
  • +
+ +

The DSL plain SQL API

+

+ Plain SQL API methods are usually overloaded in three ways. Let's look at the condition query part constructor: +

+ + +

+ Please refer to the Javadoc for more details. The following is a more complete listing of plain SQL construction methods from the DSL: +

+ + field(String sql); +Field field(String sql, Object... bindings); +Field field(String sql, QueryPart... parts); + +// A field with a known data type + Field field(String sql, Class type); + Field field(String sql, Class type, Object... bindings); + Field field(String sql, Class type, QueryPart... parts); + Field field(String sql, DataType type); + Field field(String sql, DataType type, Object... bindings); + Field field(String sql, DataType type, QueryPart... parts); + +// A field with a known name (properly escaped) +Field field(Name name); + Field field(Name name, Class type); + Field field(Name name, DataType type); + +// A function + Field function(String name, Class type, Field... arguments); + Field function(String name, DataType type, Field... arguments); + +// A table +Table table(String sql); +Table table(String sql, Object... bindings); +Table table(String sql, QueryPart... parts); + +// A table with a known name (properly escaped) +Table table(Name name); + +// A query without results (update, insert, etc) +Query query(String sql); +Query query(String sql, Object... bindings); +Query query(String sql, QueryPart... parts); + +// A query with results +ResultQuery resultQuery(String sql); +ResultQuery resultQuery(String sql, Object... bindings); +ResultQuery resultQuery(String sql, QueryPart... parts); + +// A query with results. This is the same as resultQuery(...).fetch(); +Result fetch(String sql); +Result fetch(String sql, Object... bindings); +Result fetch(String sql, QueryPart... parts);]]> + +

+ Apart from the general factory methods, plain SQL is also available in various other contexts. For instance, when adding a .where("a = b") clause to a query. Hence, there exist several convenience methods where plain SQL can be inserted usefully. This is an example displaying all various use-cases in one single query: +

+ + LAST_NAME = create.field("a.LAST_NAME"); + +// You can alias your plain SQL fields +Field COUNT1 = create.field("count(*) x"); + +// If you know a reasonable Java type for your field, you +// can also provide jOOQ with that type +Field COUNT2 = create.field("count(*) y", Integer.class); + + // Use plain SQL as select fields +create.select(LAST_NAME, COUNT1, COUNT2) + + // Use plain SQL as aliased tables (be aware of syntax!) + .from("author a") + .join("book b") + + // Use plain SQL for conditions both in JOIN and WHERE clauses + .on("a.id = b.author_id") + + // Bind a variable in plain SQL + .where("b.title != ?", "Brida") + + // Use plain SQL again as fields in GROUP BY and ORDER BY clauses + .groupBy(LAST_NAME) + .orderBy(LAST_NAME) + .fetch();]]> + +

Important things to note about plain SQL!

+

+ There are some important things to keep in mind when using plain SQL: +

+
    +
  • jOOQ doesn't know what you're doing. You're on your own again!
  • +
  • You have to provide something that will be syntactically correct. If it's not, then jOOQ won't know. Only your JDBC driver or your RDBMS will detect the syntax error.
  • +
  • You have to provide consistency when you use variable binding. The number of ? must match the number of variables
  • +
  • Your SQL is inserted into jOOQ queries without further checks. Hence, jOOQ can't prevent SQL injection.
  • +
+ + + +
+ Names and identifiers + +

+ Various SQL objects or can be referenced using names (often also called identifiers). SQL dialects differ in the way they understand names, syntactically. The differences include: +

+ +
    +
  • The permitted characters to be used in "unquoted" names
  • +
  • The permitted characters to be used in "quoted" names
  • +
  • The name quoting characters
  • +
  • The standard case for case-insensitive ("unquoted") names
  • +
+ +

+ For the above reasons, jOOQ by default quotes all names in generated SQL to be sure they match what is really contained in your database. This means that the following names will be rendered +

+ + + +

+ Note that you can influence jOOQ's name rendering behaviour through , if you prefer another name style to be applied. +

+ +

Creating custom names

+ +

+ Custom, qualified or unqualified names can be created very easily using the constructor: +

+ + + +

+ Such names can be used as standalone , or as DSL entry point for SQL expressions, like +

+ +
    +
  • Common table expressions to be used with
  • +
  • Window specifications to be used with
  • +
+ +

+ More details about how to use names / identifiers to construct such expressions can be found in the relevant sections of the manual. +

+
+
+ +
+ Bind values and parameters + +

+ Bind values are used in SQL / JDBC for various reasons. Among the most obvious ones are: +

+
    +
  • + Protection against SQL injection. Instead of inlining values possibly originating from user input, you bind those values to your prepared statement and let the JDBC driver / database take care of handling security aspects. +
  • +
  • + Increased speed. Advanced databases such as Oracle can keep execution plans of similar queries in a dedicated cache to prevent hard-parsing your query again and again. In many cases, the actual value of a bind variable does not influence the execution plan, hence it can be reused. Preparing a statement will thus be faster +
  • +
  • + On a JDBC level, you can also reuse the SQL string and prepared statement object instead of constructing it again, as you can bind new values to the prepared statement. jOOQ currently does not cache prepared statements, internally. +
  • +
+ +

+ The following sections explain how you can introduce bind values in jOOQ, and how you can control the way they are rendered and bound to SQL. +

+
+ + +
+ Indexed parameters + +

+ JDBC only knows indexed bind values. A typical example for using bind values with JDBC is this: +

+ + + +

+ With dynamic SQL, keeping track of the number of question marks and their corresponding index may turn out to be hard. jOOQ abstracts this and lets you provide the bind value right where it is needed. A trivial example is this: +

+ + + +

+ Note the using of to explicitly create an indexed bind value. You don't have to worry about that index. When the query is , each bind value will render a question mark. When the query , each bind value will generate the appropriate bind value index. +

+ +

Extract bind values from a query

+

+ Should you decide to run the above query outside of jOOQ, using your own , you can do so as follows: +

+ + select = create.select().from(BOOK).where(BOOK.ID.equal(5)).and(BOOK.TITLE.equal("Animal Farm")); + +// Render the SQL statement: +String sql = select.getSQL(); +assertEquals("SELECT * FROM BOOK WHERE ID = ? AND TITLE = ?", sql); + +// Get the bind values: +List values = select.getBindValues(); +assertEquals(2, values.size()); +assertEquals(5, values.get(0)); +assertEquals("Animal Farm", values.get(1));]]> + +

+ You can also extract specific bind values by index from a query, if you wish to modify their underlying value after creating a query. This can be achieved as such: +

+ + select = create.select().from(BOOK).where(BOOK.ID.equal(5)).and(BOOK.TITLE.equal("Animal Farm")); +Param param = select.getParam("2"); + +// You could now modify the Query's underlying bind value: +if ("Animal Farm".equals(param.getValue())) { + param.setConverted("1984"); +}]]> + +

+ For more details about jOOQ's internals, see the manual's section about . +

+ + + +
+ Named parameters + +

+ Some SQL access abstractions that are built on top of JDBC, or some that bypass JDBC may support named parameters. jOOQ allows you to give names to your parameters as well, although those names are not rendered to SQL strings by default. Here is an example of how to create named parameters using the type: +

+ + param1 = query.getParam("lastName"); + +// Or, keep a reference to the typed parameter in order not to lose the type information: +Param param2 = param("lastName", "Poe"); +Query query2 = create.select().from(AUTHOR).where(LAST_NAME.equal(param2)); + +// You can now change the bind value directly on the Param reference: +param2.setValue("Orwell");]]> + +

+ The interface also allows for setting new bind values directly, without accessing the Param type: +

+ + + +

+ In order to actually render named parameter names in generated SQL, use the method: +

+ + + + + +
+
+ +
+ Inlined parameters + +

+ Sometimes, you may wish to avoid rendering bind variables while still using custom values in SQL. jOOQ refers to that as "inlined" bind values. When bind values are inlined, they render the actual value in SQL rather than a JDBC question mark. Bind value inlining can be achieved in two ways: +

+
    +
  • + By using the and setting the to STATIC_STATEMENT. This will inline all bind values for SQL statements rendered from such a Configuration. +
  • +
  • + By using methods. +
  • +
+

+ In both cases, your inlined bind values will be properly escaped to avoid SQL syntax errors and SQL injection. Some examples: +

+ + +
+
+ + +
+ SQL injection + + +

SQL injection is serious

+

+ SQL injection is a serious problem that needs to be taken care of thoroughly. A single vulnerability can be enough for an attacker to dump your whole database, and potentially seize your database server. We've blogged about the severity of this threat on the jOOQ blog. +

+ +

+ SQL injection happens because a programming language (SQL) is used to dynamically create arbitrary server-side statements based on user input. Programmers must take lots of care not to mix the language parts (SQL) with the user input () +

+ +

SQL injection in jOOQ

+

+ With jOOQ, SQL is usually created via a type safe, non-dynamic Java abstract syntax tree, where bind variables are a part of that abstract syntax tree. It is not possible to expose SQL injection vulnerabilities this way. +

+ +

+ However, jOOQ offers convenient ways of introducing in various places of the jOOQ API (which are annotated using since jOOQ 3.6). While jOOQ's API allows you to specify bind values for use with plain SQL, you're not forced to do that. For instance, both of the following queries will lead to the same, valid result: +

+ + + +

+ All methods in the jOOQ API that allow for plain (unescaped, untreated) SQL contain a warning message in their relevant Javadoc, to remind you of the risk of SQL injection in what is otherwise a SQL-injection-safe API. +

+
+
+ + + +
+ QueryParts + +

+ A and all its contained objects is a . QueryParts essentially provide this functionality: +

+
    +
  • they can using the method
  • +
  • they can using the method
  • +
+ +

+ Both of these methods are contained in jOOQ's internal API's , which is internally implemented by every QueryPart. +

+ +

+ The following sections explain some more details about and , as well as other implementation details about QueryParts in general. +

+
+ + +
+ SQL rendering + +

+ Every must implement the method to render its SQL string to a . This RenderContext has two purposes: +

+
    +
  • It provides some information about the "state" of SQL rendering.
  • +
  • It provides a common API for constructing SQL strings on the context's internal
  • +
+

+ An overview of the API is given here: +

+ + + +

+ The following additional methods are inherited from a common , which is shared among and : +

+ + + +

An example of rendering SQL

+

+ A simple example can be provided by checking out jOOQ's internal representation of a (simplified) . It is used for any comparing two fields as for example the AUTHOR.ID = BOOK.AUTHOR_ID condition here: +

+ +-- [...] +FROM AUTHOR +JOIN BOOK ON AUTHOR.ID = BOOK.AUTHOR_ID +-- [...] + +

+ This is how jOOQ renders such a condition (simplified example): +

+ + context) { + // The CompareCondition delegates rendering of the Fields to the Fields + // themselves and connects them using the Condition's comparator operator: + context.visit(field1) + .sql(" ") + .keyword(comparator.toSQL()) + .sql(" ") + .visit(field2); +}]]> + +

+ See the manual's sections about and to learn about how to write your own query parts in order to extend jOOQ. +

+
+
+ +
+ Pretty printing SQL + +

+ As mentioned in the previous chapter about , there are some elements in the that are used for formatting / pretty-printing rendered SQL. In order to obtain pretty-printed SQL, just use the following : +

+ + + + +

+ And then, use the above DSLContext to render pretty-printed SQL: +

+ + + + '1984' +group by "TEST"."AUTHOR"."LAST_NAME" +having count(*) = 2]]> + + +

+ The section about shows an example of how such pretty printing can be used to log readable SQL to the stdout. +

+
+
+ +
+ Variable binding + +

+ Every must implement the method. This Context has two purposes (among many others): +

+
    +
  • It provides some information about the "state" of the variable binding in process.
  • +
  • It provides a common API for binding values to the context's internal
  • +
+

+ An overview of the API is given here: +

+ + parts) throws DataAccessException; +BindContext bind(QueryPart[] parts) throws DataAccessException; + +// These methods perform the actual variable binding +BindContext bindValue(Object value, Class type) throws DataAccessException; +BindContext bindValues(Object... values) throws DataAccessException;]]> + +

+ Some additional methods are inherited from a common , which is shared among and . Details are documented in the previous chapter about +

+ +

An example of binding values to SQL

+

+ A simple example can be provided by checking out jOOQ's internal representation of a (simplified) . It is used for any comparing two fields as for example the AUTHOR.ID = BOOK.AUTHOR_ID condition here: +

+ +-- [...] +WHERE AUTHOR.ID = ? +-- [...] + +

+ This is how jOOQ binds values on such a condition: +

+ + +

+ See the manual's sections about and to learn about how to write your own query parts in order to extend jOOQ. +

+
+
+ +
+ Custom data type bindings + +

+ jOOQ supports all the standard SQL data types out of the box, i.e. the types contained in . But your domain model might be more specific, or you might be using a vendor-specific data type, such as JSON, HSTORE, or some other data structure. If this is the case, this section will be right for you, we'll see how you can create types and types. +

+ +

+ Converters +

+ +

+ The simplest use-case of injecting custom data types is by using . A Converter can convert from a database type <T> to a user-defined type <U> and vice versa. You'll be implementing this SPI: +

+ + { + + // Your conversion logic goes into these two methods, that can convert + // between the database type T and the user type U: + U from(T databaseObject); + T to(U userObject); + + // You need to provide Class instances for each type, too: + Class fromType(); + Class toType(); +}]]> + +

+ If, for instance, you want to use Java 8's for SQL DATE and for SQL TIMESTAMP, you write a converter like this: +

+ + { + + @Override + public LocalDate from(Date t) { + return t == null ? null : LocalDate.parse(t.toString()); + } + + @Override + public Date to(LocalDate u) { + return u == null ? null : Date.valueOf(u.toString()); + } + + @Override + public Class fromType() { + return Date.class; + } + + @Override + public Class toType() { + return LocalDate.class; + } +}]]> + +

+ This converter can now be used in a variety of jOOQ API, most importanly to create a new data type: +

+ + type = SQLDataType.DATE.asConvertedDataType(new LocalDateConverter());]]> + +

+ And data types, in turn, can be used with any , i.e. with any , including or based ones: +

+ + type = SQLDataType.DATE.asConvertedDataType(new LocalDateConverter()); + +// Plain SQL based +Field date1 = DSL.field("my_table.my_column", type); + +// Name based +Field date2 = DSL.field(name("my_table", "my_column"), type);]]> + + +

+ Bindings +

+ +

+ While converters are very useful for simple use-cases, is useful when you need to customise data type interactions at a JDBC level, e.g. when you want to bind a PostgreSQL JSON data type. Custom bindings implement the following SPI: +

+ + extends Serializable { + + // A converter that does the conversion between the database type T + // and the user type U (see previous examples) + Converter converter(); + + // A callback that generates the SQL string for bind values of this + // binding type. Typically, just ?, but also ?::json, etc. + void sql(BindingSQLContext ctx) throws SQLException; + + // Callbacks that implement all interaction with JDBC types, such as + // PreparedStatement, CallableStatement, SQLOutput, SQLinput, ResultSet + void register(BindingRegisterContext ctx) throws SQLException; + void set(BindingSetStatementContext ctx) throws SQLException; + void set(BindingSetSQLOutputContext ctx) throws SQLException; + void get(BindingGetResultSetContext ctx) throws SQLException; + void get(BindingGetStatementContext ctx) throws SQLException; + void get(BindingGetSQLInputContext ctx) throws SQLException; +}]]> + +

+ Below is full fledged example implementation that uses Google Gson to model JSON documents in Java +

+ + = Object (unknown database type), and = JsonElement (user type) +public class PostgresJSONGsonBinding implements Binding { + + // The converter does all the work + @Override + public Converter converter() { + return new Converter() { + @Override + public JsonElement from(Object t) { + return t == null ? JsonNull.INSTANCE : new Gson().fromJson("" + t, JsonElement.class); + } + + @Override + public Object to(JsonElement u) { + return u == null || u == JsonNull.INSTANCE ? null : new Gson().toJson(u); + } + + @Override + public Class fromType() { + return Object.class; + } + + @Override + public Class toType() { + return JsonElement.class; + } + }; + } + + // Rending a bind variable for the binding context's value and casting it to the json type + @Override + public void sql(BindingSQLContext ctx) throws SQLException { + ctx.render().visit(DSL.val(ctx.convert(converter()).value())).sql("::json"); + } + + // Registering VARCHAR types for JDBC CallableStatement OUT parameters + @Override + public void register(BindingRegisterContext ctx) throws SQLException { + ctx.statement().registerOutParameter(ctx.index(), Types.VARCHAR); + } + + // Converting the JsonElement to a String value and setting that on a JDBC PreparedStatement + @Override + public void set(BindingSetStatementContext ctx) throws SQLException { + ctx.statement().setString(ctx.index(), Objects.toString(ctx.convert(converter()).value(), null)); + } + + // Getting a String value from a JDBC ResultSet and converting that to a JsonElement + @Override + public void get(BindingGetResultSetContext ctx) throws SQLException { + ctx.convert(converter()).value(ctx.resultSet().getString(ctx.index())); + } + + // Getting a String value from a JDBC CallableStatement and converting that to a JsonElement + @Override + public void get(BindingGetStatementContext ctx) throws SQLException { + ctx.convert(converter()).value(ctx.statement().getString(ctx.index())); + } + + // Setting a value on a JDBC SQLOutput (useful for Oracle OBJECT types) + @Override + public void set(BindingSetSQLOutputContext ctx) throws SQLException { + throw new SQLFeatureNotSupportedException(); + } + + // Getting a value from a JDBC SQLInput (useful for Oracle OBJECT types) + @Override + public void get(BindingGetSQLInputContext ctx) throws SQLException { + throw new SQLFeatureNotSupportedException(); + } +}]]> + +

+ Code generation +

+

+ There is a special section in the manual explaining how to automatically tie your Converters and Bindings to your generated code. The relevant sections are: +

+ +
    +
  • +
  • +
+ + + +
+ Custom syntax elements + +

+ If a SQL clause is too complex to express with jOOQ, you can extend either one of the following types for use directly in a jOOQ query: +

+ + extends AbstractField {} +public abstract class CustomCondition extends AbstractCondition {} +public abstract class CustomTable> extends TableImpl {} +public abstract class CustomRecord> extends TableRecordImpl {}]]> + +

+ These classes are declared public and covered by jOOQ's integration tests. When you extend these classes, you will have to provide your own implementations for the method, as discussed before: +

+ + + +

+ An example for implementing multiplication. +

+

+ The above contract may be a bit tricky to understand at first. The best thing is to check out jOOQ source code and have a look at a couple of QueryParts, to see how it's done. Here's an example showing how to create a field multiplying another field by 2 +

+ + IDx2 = new CustomField(BOOK.ID.getName(), BOOK.ID.getDataType()) { + @Override + public void toSQL(RenderContext context) { + + // In inline mode, render the multiplication directly + if (context.inline()) { + context.sql(BOOK.ID).sql(" * 2"); + } + + // In non-inline mode, render a bind value + else { + context.sql(BOOK.ID).sql(" * ?"); + } + } + + @Override + public void bind(BindContext context) { + try { + + // Manually bind the value 2 + context.statement().setInt(context.nextIndex(), 2); + + // Alternatively, you could also write: + // context.bind(DSL.val(2)); + } + catch (SQLException e) { + throw new DataAccessException("Bind error", e); + } + } +}; + +// Use the above field in a SQL statement: +create.select(IDx2).from(BOOK);]]> + +

+ An example for implementing vendor-specific functions. +

+

+ Many vendor-specific functions are not officially supported by jOOQ, but you can implement such support yourself using CustomField, for instance. Here's an example showing how to implement Oracle's TO_CHAR() function, emulating it in SQL Server using CONVERT(): +

+ + { + + final Field arg0; + final Field arg1; + + ToChar(Field arg0, Field arg1) { + super("to_char", SQLDataType.VARCHAR); + + this.arg0 = arg0; + this.arg1 = arg1; + } + + @Override + public void accept(RenderContext context) { + context.visit(delegate(context.configuration())); + } + + private QueryPart delegate(Configuration configuration) { + switch (configuration.dialect().family()) { + case ORACLE: + return DSL.field("TO_CHAR({0}, {1})", String.class, arg0, arg1); + + case SQLSERVER: + return DSL.field("CONVERT(VARCHAR(8), {0}, {1})", String.class, arg0, arg1); + + default: + throw new UnsupportedOperationException("Dialect not supported"); + } + } +} +]]> + +

+ The above CustomField implementation can be exposed from your own custom DSL class: +

+ + toChar(Field field, String format) { + return new ToChar(field, DSL.inline(format)); + } +}]]> +
+
+ +
+ Plain SQL QueryParts + +

+ If you don't need the integration of rather complex QueryParts into jOOQ, then you might be safer using simple functionality, where you can provide jOOQ with a simple String representation of your embedded SQL. Plain SQL methods in jOOQ's API come in two flavours. +

+
    +
  • method(String, Object...): This is a method that accepts a SQL string and a list of bind values that are to be bound to the variables contained in the SQL string
  • +
  • method(String, QueryPart...): This is a method that accepts a SQL string and a list of QueryParts that are "injected" at the position of their respective placeholders in the SQL string
  • +
+

+ The above distinction is best explained using an example: +

+ + id = val(5); +Field title = val("Animal Farm"); +create.selectFrom(BOOK).where("BOOK.ID = {0} AND TITLE = {1}", id, title).fetch();]]> + +

+ The above technique allows for creating rather complex SQL clauses that are currently not supported by jOOQ, without extending any of the as indicated in the previous chapter. +

+
+
+ +
+ Serializability + +

+ The only transient, non-serializable element in any jOOQ object is the underlying . When you want to execute queries after de-serialisation, or when you want to store/refresh/delete , you may have to "re-attach" them to a Configuration +

+ + select = (Select) in.readObject(); + +// This will throw a DetachedException: +select.execute(); + +// In order to execute the above select, attach it first +DSLContext create = DSL.using(connection, SQLDialect.ORACLE); +create.attach(select);]]> + +

Automatically attaching QueryParts

+

+ Another way of attaching QueryParts automatically, or rather providing them with a new at will, is to hook into the . More details about this can be found in the manual's chapter about +

+
+
+ +
+ Custom SQL transformation + +

+ With jOOQ 3.2's SPI, it is possible to perform custom SQL transformation to implement things like shared-schema multi-tenancy, or a security layer centrally preventing access to certain data. This SPI is extremely powerful, as you can make ad-hoc decisions at runtime regarding local or global transformation of your SQL statement. The following sections show a couple of simple, yet real-world use-cases. +

+
+ + +
+ Logging abbreviated bind values + +

+ When implementing a logger, one needs to carefully assess how much information should really be disclosed on what logger level. In log4j and similar frameworks, we distinguish between FATAL, ERROR, WARN, INFO, DEBUG, and TRACE. In DEBUG level, jOOQ's logs all executed statements including inlined bind values as such: +

+ + with bind values : select * from "BOOK" where "BOOK"."TITLE" like 'How I stopped worrying%']]> + +

+ But textual or binary bind values can get quite long, quickly filling your log files with irrelevant information. It would be good to be able to abbreviate such long values (and possibly add a remark to the logged statement). Instead of patching jOOQ's internals, we can just transform the SQL statements in the logger implementation, cleanly separating concerns. This can be done with the following VisitListener: +

+ +) { + Param param = (Param) part; + Object value = param.getValue(); + + // If the bind value is a String (or Clob) of a given length, abbreviate it + // e.g. using commons-lang's StringUtils.abbreviate() + if (value instanceof String && ((String) value).length() > maxLength) { + anyAbbreviations = true; + + // ... and replace it in the current rendering context (not in the Query) + context.queryPart(val(abbreviate((String) value, maxLength))); + } + + // If the bind value is a byte[] (or Blob) of a given length, abbreviate it + // e.g. by removing bytes from the array + else if (value instanceof byte[] && ((byte[]) value).length > maxLength) { + anyAbbreviations = true; + + // ... and replace it in the current rendering context (not in the Query) + context.queryPart(val(Arrays.copyOf((byte[]) value, maxLength))); + } + } + } + } + + @Override + public void visitEnd(VisitContext context) { + + // If any abbreviations were performed before... + if (anyAbbreviations) { + + // ... and if this is the top-level QueryPart, then append a SQL comment to indicate the abbreviation + if (context.queryPartsLength() == 1) { + context.renderContext().sql(" -- Bind values may have been abbreviated"); + } + } + } +}]]> + +

+ If maxLength were set to 5, the above listener would produce the following log output: +

+ + with bind values : select * from "BOOK" where "BOOK"."TITLE" like 'Ho...' -- Bind values may have been abbreviated]]> + +

+ The above VisitListener is in place since jOOQ 3.3 in the . +

+
+
+
+
+ + + +
+ SQL building in Scala + +

+ jOOQ-Scala is a maven module used for leveraging some advanced Scala features for those users that wish to use jOOQ with Scala. +

+ +

Using Scala's implicit defs to allow for operator overloading

+

+ The most obvious Scala feature to use in jOOQ are implicit defs for implicit conversions in order to enhance the type with SQL-esque operators. +

+

+ The following depicts a trait which wraps all fields: +

+ +(value : T) : Condition + def <>(value : Field[T]) : Condition + + def >(value : T) : Condition + def >(value : Field[T]) : Condition + + def >=(value : T) : Condition + def >=(value : Field[T]) : Condition + + def <(value : T) : Condition + def <(value : Field[T]) : Condition + + def <=(value : T) : Condition + def <=(value : Field[T]) : Condition + + def <=>(value : T) : Condition + def <=>(value : Field[T]) : Condition +}]]> + +

+ The following depicts a trait which wraps numeric fields: +

+ +>(value : T) : Field[T] + def >>(value : Field[T]) : Field[T] +}]]> + +

+ An example query using such overloaded operators would then look like this: +

+ + 2) +or (BOOK.TITLE in ("O Alquimista", "Brida")) +fetch]]> + +

Scala 2.10 Macros

+

+ This feature is still being experimented with. With Scala Macros, it might be possible to inline a true SQL dialect into the Scala syntax, backed by the jOOQ API. Stay tuned! +

+
+
+ + + +
+ SQL execution + +

+ In a previous section of the manual, we've seen how jOOQ can be used to that can be executed with any API including JDBC or ... jOOQ. This section of the manual deals with various means of actually executing SQL with jOOQ. +

+ +

SQL execution with JDBC

+

+ JDBC calls executable objects "". It distinguishes between three types of statements: +

+
    +
  • , or "static statement": This statement type is used for any arbitrary type of SQL statement. It is particularly useful with
  • +
  • : This statement type is used for any arbitrary type of SQL statement. It is particularly useful with (note that JDBC does not support )
  • +
  • : This statement type is used for SQL statements that are "called" rather than "executed". In particular, this includes calls to . Callable statements can register OUT parameters
  • +
+

+ Today, the JDBC API may look weird to users being used to object-oriented design. While statements hide a lot of SQL dialect-specific implementation details quite well, they assume a lot of knowledge about the internal state of a statement. For instance, you can use the method, to add a the prepared statement being created to an "internal list" of batch statements. Instead of returning a new type, this method forces user to reflect on the prepared statement's internal state or "mode". +

+ +

jOOQ is wrapping JDBC

+

+ These things are abstracted away by jOOQ, which exposes such concepts in a more object-oriented way. For more details about jOOQ's batch query execution, see the manual's section about . +

+

+ The following sections of this manual will show how jOOQ is wrapping JDBC for SQL execution +

+ +

Alternative execution modes

+

+ Just because you can, doesn't mean you must. At the end of this chapter, we'll show how you can use jOOQ to generate SQL statements that are then executed with other APIs, such as Spring's JdbcTemplate, or Hibernate. For more information see the . +

+
+ + +
+ Comparison between jOOQ and JDBC + +

Similarities with JDBC

+

+ Even if there are , there are a lot of similarities between JDBC and jOOQ. Just to name a few: +

+
    +
  • Both APIs return the number of affected records in non-result queries. JDBC: , jOOQ:
  • +
  • Both APIs return a scrollable result set type from result queries. JDBC: , jOOQ:
  • +
+ +

Differences to JDBC

+

+ Some of the most important differences between JDBC and jOOQ are listed here: +

+
    +
  • : JDBC does not formally distinguish between queries that can return results, and queries that cannot. The same API is used for both. This greatly reduces the possibility for
  • +
  • : While SQL uses the checked , jOOQ wraps all exceptions in an unchecked
  • +
  • : Unlike its JDBC counter-part, this type implements and is fully loaded into Java memory, freeing resources as early as possible. Just like statements, this means that users don't have to deal with a "weird" internal result set state.
  • +
  • : If you want more fine-grained control over how many records are fetched into memory at once, you can still do that using jOOQ's feature
  • +
  • : jOOQ does not formally distinguish between static statements and prepared statements. By default, all statements are prepared statements in jOOQ, internally. Executing a statement as a static statement can be done simply using a
  • +
  • : JDBC keeps open resources even if they are already consumed. With JDBC, there is a lot of verbosity around safely closing resources. In jOOQ, resources are closed after consumption, by default. If you want to keep them open after consumption, you have to explicitly say so.
  • +
  • : JDBC execution flags and modes are not modified. They can be set fluently on a
  • +
+
+
+ +
+ Query vs. ResultQuery + +

+ Unlike JDBC, jOOQ has a lot of knowledge about a SQL query's structure and internals (see the manual's section about ). Hence, jOOQ distinguishes between these two fundamental types of queries. While every can be executed, only can return results (see the manual's section about to learn more about fetching results). With plain SQL, the distinction can be made clear most easily: +

+ + resultQuery = create.resultQuery("SELECT * FROM BOOK"); +Result result = resultQuery.fetch();]]> +
+
+ +
+ Fetching + +

+ Fetching is something that has been completely neglegted by JDBC and also by various other database abstraction libraries. Fetching is much more than just looping or listing records or mapped objects. There are so many ways you may want to fetch data from a database, it should be considered a first-class feature of any database abstraction API. Just to name a few, here are some of jOOQ's fetching modes: +

+
    +
  • : Sometimes you care about the returned type of your records, sometimes (with arbitrary projections) you don't.
  • +
  • : Instead of letting you transform your result sets into any more suitable data type, a library should do that work for you.
  • +
  • : This is an entirely different fetching paradigm. With Java 8's lambda expressions, this will become even more powerful.
  • +
  • : This is an entirely different fetching paradigm. With Java 8's lambda expressions, this will become even more powerful.
  • +
  • : This is what made Hibernate and JPA so strong. Automatic mapping of tables to custom POJOs.
  • +
  • : It should be easy to distinguish these two fetch modes.
  • +
  • : Some databases allow for returning many result sets from a single query. JDBC can handle this but it's very verbose. A list of results should be returned instead.
  • +
  • : Some queries take too long to execute to wait for their results. You should be able to spawn query execution in a separate process.
  • +
+ +

Convenience and how ResultQuery, Result, and Record share API

+

+ The term "fetch" is always reused in jOOQ when you can fetch data from the database. An provides many overloaded means of fetching data: +

+ +

Various modes of fetching

+

+ These modes of fetching are also documented in subsequent sections of the manual +

+ + fetch(); + +// The "standard" fetch when you know your query returns only one record. This may return null. +R fetchOne(); + +// The "standard" fetch when you know your query returns only one record. +Optional fetchOptional(); + +// The "standard" fetch when you only want to fetch the first record +R fetchAny(); + +// Create a "lazy" Cursor, that keeps an open underlying JDBC ResultSet +Cursor fetchLazy(); +Cursor fetchLazy(int fetchSize); +Stream stream(); + +// Fetch several results at once +List> fetchMany(); + +// Fetch records into a custom callback +> H fetchInto(H handler); + +// Map records using a custom callback + List fetch(RecordMapper mapper); + +// Execute a ResultQuery with jOOQ, but return a JDBC ResultSet, not a jOOQ object +ResultSet fetchResultSet();]]> + +

Fetch convenience

+

+ These means of fetching are also available from and APIs +

+ + List fetch(Field field); + List fetch(Field field, Class type); + List fetch(Field field, Converter converter); + List fetch(int fieldIndex); + List fetch(int fieldIndex, Class type); + List fetch(int fieldIndex, Converter converter); + List fetch(String fieldName); + List fetch(String fieldName, Class type); + List fetch(String fieldName, Converter converter); + +// These methods are convenience for fetching only a single field, possibly converting results to another type +// Instead of returning lists, these return arrays + T[] fetchArray(Field field); + T[] fetchArray(Field field, Class type); + U[] fetchArray(Field field, Converter converter); + Object[] fetchArray(int fieldIndex); + T[] fetchArray(int fieldIndex, Class type); + U[] fetchArray(int fieldIndex, Converter converter); + Object[] fetchArray(String fieldName); + T[] fetchArray(String fieldName, Class type); + U[] fetchArray(String fieldName, Converter converter); + +// These methods are convenience for fetching only a single field from a single record, +// possibly converting results to another type + T fetchOne(Field field); + T fetchOne(Field field, Class type); + U fetchOne(Field field, Converter converter); + Object fetchOne(int fieldIndex); + T fetchOne(int fieldIndex, Class type); + U fetchOne(int fieldIndex, Converter converter); + Object fetchOne(String fieldName); + T fetchOne(String fieldName, Class type); + U fetchOne(String fieldName, Converter converter);]]> + +

Fetch transformations

+

+ These means of fetching are also available from and APIs +

+ + Map fetchMap(Field key); + Map fetchMap(Field key, Field value); + Map fetchMap(Field key, Class value); + Map fetchMap(Field[] key); + Map fetchMap(Field[] key, Class value); + +// Transform your Result object into maps + List> fetchMaps(); + Map fetchOneMap(); + +// Transform your Result object into groups + Map> fetchGroups(Field key); + Map> fetchGroups(Field key, Field value); + Map> fetchGroups(Field key, Class value); + Map> fetchGroups(Field[] key); + Map> fetchGroups(Field[] key, Class value); + +// Transform your Records into custom POJOs + List fetchInto(Class type); + +// Transform your records into another table type + Result fetchInto(Table table);]]> + +

+ Note, that apart from the methods, all fetch() methods will immediately close underlying JDBC result sets. +

+
+ + +
+ Record vs. TableRecord + +

+ jOOQ understands that SQL is much more expressive than Java, when it comes to the declarative typing of . As a declarative language, SQL allows for creating ad-hoc row value expressions (records with indexed columns, or tuples) and records (records with named columns). In Java, this is not possible to the same extent. +

+

+ Yet, still, sometimes you wish to use strongly typed records, when you know that you're selecting only from a single table: +

+ +

Fetching strongly or weakly typed records

+

+ When fetching data only from a single table, the type is known to jOOQ if you use jOOQ's to generate for your database tables. In order to fetch such strongly typed records, you will have to use the : +

+ + + +

+ When you use the method, jOOQ will return the record type supplied with the argument table. Beware though, that you will no longer be able to use any clause that modifies the type of your . This includes: +

+
    +
  • +
  • +
+ +

+ Mapping custom row types to strongly typed records +

+ +

+ Sometimes, you may want to explicitly select only a subset of your columns, but still use strongly typed records. Alternatively, you may want to join a one-to-one relationship and receive the two individual strongly typed records after the join. +

+

+ In both of the above cases, you can map your "into" a type by using . +

+ + +
+
+ +
+ Record1 to Record{max-row-degree} + +

+ jOOQ's support has been explained earlier in this manual. It is useful for constructing row value expressions where they can be used in SQL. The same typesafety is also applied to records for degrees up to {max-row-degree}. To express this fact, is extended by to . Apart from the fact that these extensions of the R type can be used throughout the , they also provide a useful API. Here is , for instance: +

+ + extends Record { + + // Access fields and values as row value expressions + Row2 fieldsRow(); + Row2 valuesRow(); + + // Access fields by index + Field field1(); + Field field2(); + + // Access values by index + T1 value1(); + T2 value2(); +}]]> + +

Higher-degree records

+

+ jOOQ chose to explicitly support degrees up to {max-row-degree} to match Scala's typesafe tuple, function and product support. Unlike Scala, however, jOOQ also supports higher degrees without the additional typesafety. +

+
+
+ +
+ Arrays, Maps and Lists + +

+ By default, jOOQ returns an object, which is essentially a of . Often, you will find yourself wanting to transform this result object into a type that corresponds more to your specific needs. Or you just want to list all values of one specific column. Here are some examples to illustrate those use cases: +

+ + titles1 = create.select().from(BOOK).fetch().getValues(BOOK.TITLE); +List titles2 = create.select().from(BOOK).fetch(BOOK.TITLE); +String[] titles3 = create.select().from(BOOK).fetchArray(BOOK.TITLE); + +// Fetching only book IDs, converted to Long +List ids1 = create.select().from(BOOK).fetch().getValues(BOOK.ID, Long.class); +List ids2 = create.select().from(BOOK).fetch(BOOK.ID, Long.class); +Long[] ids3 = create.select().from(BOOK).fetchArray(BOOK.ID, Long.class); + +// Fetching book IDs and mapping each ID to their records or titles +Map map1 = create.selectFrom(BOOK).fetch().intoMap(BOOK.ID); +Map map2 = create.selectFrom(BOOK).fetchMap(BOOK.ID); +Map map3 = create.selectFrom(BOOK).fetch().intoMap(BOOK.ID, BOOK.TITLE); +Map map4 = create.selectFrom(BOOK).fetchMap(BOOK.ID, BOOK.TITLE); + +// Group by AUTHOR_ID and list all books written by any author: +Map> group1 = create.selectFrom(BOOK).fetch().intoGroups(BOOK.AUTHOR_ID); +Map> group2 = create.selectFrom(BOOK).fetchGroups(BOOK.AUTHOR_ID); +Map> group3 = create.selectFrom(BOOK).fetch().intoGroups(BOOK.AUTHOR_ID, BOOK.TITLE); +Map> group4 = create.selectFrom(BOOK).fetchGroups(BOOK.AUTHOR_ID, BOOK.TITLE);]]> + +

+ Note that most of these convenience methods are available both through and , some are even available through as well. +

+
+
+ +
+ RecordHandler + +

+ In a more functional operating mode, you might want to write callbacks that receive records from your select statement results in order to do some processing. This is a common data access pattern in Spring's JdbcTemplate, and it is also available in jOOQ. With jOOQ, you can implement your own classes and plug them into jOOQ's : +

+ +() { + @Override + public void next(BookRecord book) { + Util.doThingsWithBook(book); + } + }); + +// Or more concisely +create.selectFrom(BOOK) + .orderBy(BOOK.ID) + .fetchInto(new RecordHandler() {...}); + +// Or even more concisely with Java 8's lambda expressions: +create.selectFrom(BOOK) + .orderBy(BOOK.ID) + .fetchInto(book -> { Util.doThingsWithBook(book); }; ); +]]> + +

+ See also the manual's section about the , which provides similar features +

+
+
+ +
+ RecordMapper + +

+ In a more functional operating mode, you might want to write callbacks that map records from your select statement results in order to do some processing. This is a common data access pattern in Spring's JdbcTemplate, and it is also available in jOOQ. With jOOQ, you can implement your own classes and plug them into jOOQ's : +

+ + ids = +create.selectFrom(BOOK) + .orderBy(BOOK.ID) + .fetch() + .map(new RecordMapper() { + @Override + public Integer map(BookRecord book) { + return book.getId(); + } + }); + +// Or more concisely +create.selectFrom(BOOK) + .orderBy(BOOK.ID) + .fetch(new RecordMapper() {...}); + +// Or even more concisely with Java 8's lambda expressions: +create.selectFrom(BOOK) + .orderBy(BOOK.ID) + .fetch(book -> book.getId()); +]]> + +

+ Your custom RecordMapper types can be used automatically through jOOQ's , by injecting a into your . +

+ +

+ See also the manual's section about the , which provides similar features +

+
+
+ +
+ POJOs + +

+ Fetching data in records is fine as long as your application is not really layered, or as long as you're still writing code in the DAO layer. But if you have a more advanced application architecture, you may not want to allow for jOOQ artefacts to leak into other layers. You may choose to write POJOs (Plain Old Java Objects) as your primary DTOs (Data Transfer Objects), without any dependencies on jOOQ's types, which may even potentially hold a reference to a , and thus a JDBC . Like Hibernate/JPA, jOOQ allows you to operate with POJOs. Unlike Hibernate/JPA, jOOQ does not "attach" those POJOs or create proxies with any magic in them. +

+

+ If you're using jOOQ's , you can configure it to for you, but you're not required to use those generated POJOs. You can use your own. See the manual's section about to see how to modify jOOQ's standard POJO mapping behaviour. +

+ +

Using JPA-annotated POJOs

+

+ jOOQ tries to find JPA annotations on your POJO types. If it finds any, they are used as the primary source for mapping meta-information. Only the annotation is used and understood by jOOQ. An example: +

+ + myBooks = create.select().from(BOOK).fetch().into(MyBook.class); +List myBooks = create.select().from(BOOK).fetchInto(MyBook.class);]]> + +

+ Just as with any other JPA implementation, you can put the annotation on any class member, including attributes, setters and getters. Please refer to the Javadoc for more details. +

+ +

Using simple POJOs

+

+ If jOOQ does not find any JPA-annotations, columns are mapped to the "best-matching" constructor, attribute or setter. An example illustrates this: +

+ + myBooks = create.select().from(BOOK).fetch().into(MyBook1.class); +List myBooks = create.select().from(BOOK).fetchInto(MyBook1.class);]]> + +

+ Please refer to the Javadoc for more details. +

+ +

Using "immutable" POJOs

+

+ If jOOQ does not find any default constructor, columns are mapped to the "best-matching" constructor. This allows for using "immutable" POJOs with jOOQ. An example illustrates this: +

+ + myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetch().into(MyBook2.class); +List myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchInto(MyBook2.class); + +// An "immutable" POJO class with a java.beans.ConstructorProperties annotation +public class MyBook3 { + public final String title; + public final int id; + + @ConstructorProperties({ "title", "id"}) + public MyBook2(String title, int id) { + this.title = title; + this.id = id; + } +} + +// With annotated "immutable" POJO classes, there doesn't need to be an exact match between fields and constructor arguments. +// In the below cases, only BOOK.ID is really set onto the POJO, BOOK.TITLE remains null and BOOK.AUTHOR_ID is ignored +MyBook3 myBook = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetchAny().into(MyBook3.class); +List myBooks = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetch().into(MyBook3.class); +List myBooks = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetchInto(MyBook3.class); +]]> + +

+ Please refer to the Javadoc for more details. +

+ +

Using proxyable types

+

+ jOOQ also allows for fetching data into abstract classes or interfaces, or in other words, "proxyable" types. This means that jOOQ will return a wrapped in a implementing your custom type. An example of this is given here: +

+ + myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetch().into(MyBook3.class); +List myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchInto(MyBook3.class);]]> + +

+ Please refer to the Javadoc for more details. +

+ +

Loading POJOs back into Records to store them

+

+ The above examples show how to fetch data into your own custom POJOs / DTOs. When you have modified the data contained in POJOs, you probably want to store those modifications back to the database. An example of this is given here: +

+ + + +

+ Note: Because of your manual setting of ID = 10, jOOQ's store() method will asume that you want to insert a new record. See the manual's section about for more details on this. +

+ +

Interaction with DAOs

+

+ If you're using jOOQ's , you can configure it to for you. Those DAOs operate on . An example of using such a DAO is given here: +

+ + + +

More complex data structures

+

+ jOOQ currently doesn't support more complex data structures, the way Hibernate/JPA attempt to map relational data onto POJOs. While future developments in this direction are not excluded, jOOQ claims that generic mapping strategies lead to an enormous additional complexity that only serves very few use cases. You are likely to find a solution using any of jOOQ's various , with only little boiler-plate code on the client side. +

+
+
+ +
+ POJOs with RecordMappers + +

+ In the previous sections we have seen how to create types to map jOOQ records onto arbitrary objects. We have also seen how jOOQ provides default algorithms to map jOOQ records onto . Your own custom domain model might be much more complex, but you want to avoid looking up the most appropriate RecordMapper every time you need one. For this, you can provide jOOQ's with your own implementation of the interface. An example is given here: +

+ + RecordMapper provide(RecordType recordType, Class type) { + + // UUID mappers will always try to find the ID column + if (type == UUID.class) { + return new RecordMapper() { + @Override + public E map(R record) { + return (E) record.getValue("ID"); + } + } + } + + // Books might be joined with their authors, create a 1:1 mapping + if (type == Book.class) { + return new BookMapper(); + } + + // Fall back to jOOQ's DefaultRecordMapper, which maps records onto + // POJOs using reflection. + return new DefaultRecordMapper(recordType, type); + } + } + )) + .selectFrom(BOOK) + .orderBy(BOOK.ID) + .fetchInto(UUID.class);]]> + +

+ The above is a very simple example showing that you will have complete flexibility in how to override jOOQ's record to POJO mapping mechanisms. +

+ +

Using third party libraries

+

+ A couple of useful libraries exist out there, which implement custom, more generic mapping algorithms. Some of them have been specifically made to work with jOOQ. Among them are: +

+ + +
+
+ +
+ Lazy fetching + +

+ Unlike JDBC's , jOOQ's does not represent an open database cursor with various fetch modes and scroll modes, that needs to be closed after usage. jOOQ's results are simple in-memory Java objects, containing all of the result values. If your result sets are large, or if you have a lot of network latency, you may wish to fetch records one-by-one, or in small chunks. jOOQ supports a type for that purpose. In order to obtain such a reference, use the method. An example is given here: +

+ + cursor = create.selectFrom(BOOK).fetchLazy()) { + + // Cursor has similar methods as Iterator + while (cursor.hasNext()) { + BookRecord book = cursor.fetchOne(); + + Util.doThingsWithBook(book); + } +}]]> + +

+ As a holds an internal reference to an open , it may need to be closed at the end of iteration. If a cursor is completely scrolled through, it will conveniently close the underlying ResultSet. However, you should not rely on that. +

+ +

Cursors ship with all the other fetch features

+

+ Like or , gives access to all of the other fetch features that we've seen so far, i.e. +

+
    +
  • : Cursors are also typed with the <R> type, allowing to fetch custom, generated or plain types.
  • +
  • : You can use your own callbacks to receive lazily fetched records.
  • +
  • : You can use your own callbacks to map lazily fetched records.
  • +
  • : You can fetch data into your own custom POJO types.
  • +
+
+
+ +
+ Lazy fetching with Streams + +

+ jOOQ 3.7+ supports Java 8, and with Java 8, it supports . This opens up a range of possibilities of combining the declarative aspects of SQL with the functional aspects of the new Stream API. Much like , such a Stream keeps an internal reference to a JDBC , which means that the Stream has to be treated like a resource. Here's an example of using such a stream: +

+ + stream = create.selectFrom(BOOK).stream()) { + stream.forEach(Util::doThingsWithBook); +}]]> + +

+ A more sophisticated example would be using streams to transform the results and add business logic to it. For instance, to generate a DDL script with CREATE TABLE statements from the INFORMATION_SCHEMA of an H2 database: +

+ + r.getValue(COLUMNS.TABLE_NAME), + LinkedHashMap::new, + mapping( + r -> new SimpleEntry( + r.getValue(COLUMNS.COLUMN_NAME), + r.getValue(COLUMNS.TYPE_NAME) + ), + toList() + ))) + .forEach( + (table, columns) -> { + // Just emit a CREATE TABLE statement + System.out.println("CREATE TABLE " + table + " ("); + + // Map each "Column" type into a String containing the column specification, + // and join them using comma and newline. Done! + System.out.println( + columns.stream() + .map(col -> " " + col.getKey() + + " " + col.getValue()) + .collect(Collectors.joining(",\n")) + ); + + System.out.println(");"); + });]]> + +

+ The above combination of SQL and functional programming will produce the following output: +

+ + + +
+
+ +
+ Many fetching + +

+ Many databases support returning several result sets, or cursors, from single queries. An example for this is Sybase ASE's sp_help command: +

+ sp_help 'author' + ++--------+-----+-----------+-------------+-------------------+ +|Name |Owner|Object_type|Object_status|Create_date | ++--------+-----+-----------+-------------+-------------------+ +| author|dbo |user table | -- none -- |Sep 22 2011 11:20PM| ++--------+-----+-----------+-------------+-------------------+ + ++-------------+-------+------+----+-----+-----+ +|Column_name |Type |Length|Prec|Scale|... | ++-------------+-------+------+----+-----+-----+ +|id |int | 4|NULL| NULL| 0| +|first_name |varchar| 50|NULL| NULL| 1| +|last_name |varchar| 50|NULL| NULL| 0| +|date_of_birth|date | 4|NULL| NULL| 1| +|year_of_birth|int | 4|NULL| NULL| 1| ++-------------+-------+------+----+-----+-----+]]> + + +

+ The correct (and verbose) way to do this with JDBC is as follows: +

+ + + +

+ As previously discussed in the chapter about , jOOQ does not rely on an internal state of any JDBC object, which is "externalised" by Javadoc. Instead, it has a straight-forward API allowing you to do the above in a one-liner: +

+ + + +

+ The returned type extends the List<Result<Record>> type for backwards-compatibility reasons, but it also allows to access individual update counts that may have been returned by the database in between result sets. +

+
+
+ +
+ Later fetching + +

Using Java 8 CompletableFutures

+ +

+ Java 8 has introduced the new type, which allows for functional composition of asynchronous execution units. When applying this to SQL and jOOQ, you might be writing code as follows: +

+ + + DSL.using(configuration) + .insertInto(AUTHOR, AUTHOR.ID, AUTHOR.LAST_NAME) + .values(3, "Hitchcock") + .execute() + ) + + // This will supply an AuthorRecord value for the newly inserted author + .handleAsync((rows, throwable) -> + DSL.using(configuration) + .fetchOne(AUTHOR, AUTHOR.ID.eq(3)) + ) + + // This should supply an int value indicating the number of rows, + // but in fact it'll throw a constraint violation exception + .handleAsync((record, throwable) -> { + record.changed(true); + return record.insert(); + }) + + // This will supply an int value indicating the number of deleted rows + .handleAsync((rows, throwable) -> + DSL.using(configuration) + .delete(AUTHOR) + .where(AUTHOR.ID.eq(3)) + .execute() + ) + .join();]]> + +

+ The above example will execute four actions one after the other, but asynchronously in the JDK's default or common . +

+ +

+ For more information, please refer to the Javadoc and official documentation. +

+ +

Using deprecated API

+ +

+ Some queries take very long to execute, yet they are not crucial for the continuation of the main program. For instance, you could be generating a complicated report in a Swing application, and while this report is being calculated in your database, you want to display a background progress bar, allowing the user to pursue some other work. This can be achived simply with jOOQ, by creating a , a type that extends . An example is given here: +

+ + future = create.selectFrom(BOOK).where(... complex predicates ...).fetchLater(); + +// This example actively waits for the result to be done +while (!future.isDone()) { + progressBar.increment(1); + Thread.sleep(50); +} + +// The result should be ready, now +Result result = future.get();]]> + +

+ Note, that instead of letting jOOQ spawn a new thread, you can also provide jOOQ with your own : +

+ + future = create.selectFrom(BOOK).where(... complex predicates ...).fetchLater(service);]]> + +
+
+ +
+ ResultSet fetching + +

+ When interacting with legacy applications, you may prefer to have jOOQ return a , rather than jOOQ's own types. This can be done simply, in two ways: +

+ + + +

Transform jOOQ's Result into a JDBC ResultSet

+

+ Instead of operating on a JDBC ResultSet holding an open resource from your database, you can also let jOOQ's wrap itself in a . The advantage of this is that the so-created ResultSet has no open connection to the database. It is a completely in-memory ResultSet: +

+ + result = create.selectFrom(BOOK).fetch(); +ResultSet rs = result.intoResultSet();]]> + +

The inverse: Fetch data from a legacy ResultSet using jOOQ

+

+ The inverse of the above is possible too. Maybe, a legacy part of your application produces JDBC , and you want to turn them into a : +

+ + result = create.fetch(rs); + +// As a Cursor +Cursor cursor = create.fetchLazy(rs);]]> + +

+ You can also tighten the interaction with jOOQ's data type system and features, by passing the record type to the above fetch methods: +

+ + result = create.fetch (rs, Integer.class, String.class); +Cursor result = create.fetchLazy(rs, Integer.class, String.class); + +// Pass an array of data types: +Result result = create.fetch (rs, SQLDataType.INTEGER, SQLDataType.VARCHAR); +Cursor result = create.fetchLazy(rs, SQLDataType.INTEGER, SQLDataType.VARCHAR); + +// Pass an array of fields: +Result result = create.fetch (rs, BOOK.ID, BOOK.TITLE); +Cursor result = create.fetchLazy(rs, BOOK.ID, BOOK.TITLE);]]> + +

+ If supplied, the additional information is used to override the information obtained from the 's

+
+
+ +
+ Data type conversion + +

+ Apart from a few extra features (), jOOQ only supports basic types as supported by the JDBC API. In your application, you may choose to transform these data types into your own ones, without writing too much boiler-plate code. This can be done using jOOQ's types. A converter essentially allows for two-way conversion between two Java data types <T> and <U>. By convention, the <T> type corresponds to the type in your database whereas the >U> type corresponds to your own user type. The Converter API is given here: +

+ + extends Serializable { + + /** + * Convert a database object to a user object + */ + U from(T databaseObject); + + /** + * Convert a user object to a database object + */ + T to(U userObject); + + /** + * The database type + */ + Class fromType(); + + /** + * The user type + */ + Class toType(); +}]]> + +

+ Such a converter can be used in many parts of the jOOQ API. Some examples have been illustrated in the manual's section about . +

+ +

A Converter for GregorianCalendar

+

+ Here is a some more elaborate example involving a Converter for : +

+ + { + + @Override + public GregorianCalendar from(Timestamp databaseObject) { + GregorianCalendar calendar = (GregorianCalendar) Calendar.getInstance(); + calendar.setTimeInMillis(databaseObject.getTime()); + return calendar; + } + + @Override + public Timestamp to(GregorianCalendar userObject) { + return new Timestamp(userObject.getTime().getTime()); + } + + @Override + public Class fromType() { + return Timestamp.class; + } + + @Override + public Class toType() { + return GregorianCalendar.class; + } +} + +// Now you can fetch calendar values from jOOQ's API: +List dates1 = create.selectFrom(BOOK).fetch().getValues(BOOK.PUBLISHING_DATE, new CalendarConverter()); +List dates2 = create.selectFrom(BOOK).fetch(BOOK.PUBLISHING_DATE, new CalendarConverter()); +]]> + +

Enum Converters

+

+ jOOQ ships with a built-in default , that you can use to map VARCHAR values to enum literals or NUMBER values to enum ordinals (both modes are supported). Let's say, you want to map a YES / NO / MAYBE column to a custom Enum: +

+ + { + public YNMConverter() { + super(String.class, YNM.class); + } +} + +// And you're all set for converting records to your custom Enum: +for (BookRecord book : create.selectFrom(BOOK).fetch()) { + switch (book.getValue(BOOK.I_LIKE, new YNMConverter())) { + case YES: System.out.println("I like this book : " + book.getTitle()); break; + case NO: System.out.println("I didn't like this book : " + book.getTitle()); break; + case MAYBE: System.out.println("I'm not sure about this book : " + book.getTitle()); break; + } +}]]> + +

Using Converters in generated source code

+

+ jOOQ also allows for generated source code to reference your own custom converters, in order to permanently replace a <T> type by your own, custom <U> type. See the manual's section about for details. +

+
+
+ +
+ Interning data + +

+ SQL result tables are not optimal in terms of used memory as they are not designed to represent hierarchical data as produced by JOIN operations. Specifically, FOREIGN KEY values may repeat themselves unnecessarily: +

+ ++----+-----------+--------------+ +| ID | AUTHOR_ID | TITLE | ++----+-----------+--------------+ +| 1 | 1 | 1984 | +| 2 | 1 | Animal Farm | +| 3 | 2 | O Alquimista | +| 4 | 2 | Brida | ++----+-----------+--------------+ + +

+ Now, if you have millions of records with only few distinct values for AUTHOR_ID, you may not want to hold references to distinct (but equal) objects. This is specifically true for IDs of type or string representations thereof. jOOQ allows you to "intern" those values: +

+ + r1 = create.select(BOOK.ID, BOOK.AUTHOR_ID, BOOK.TITLE) + .from(BOOK) + .join(AUTHOR).on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) + .fetch() + .intern(BOOK.AUTHOR_ID); + +// Interning data while fetching +Result r1 = create.select(BOOK.ID, BOOK.AUTHOR_ID, BOOK.TITLE) + .from(BOOK) + .join(AUTHOR).on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) + .intern(BOOK.AUTHOR_ID) + .fetch();]]> + +

+ You can specify as many fields as you want for interning. The above has the following effect: +

+ +
    +
  • If the interned Field is of type , then is called upon each string
  • +
  • If the interned Field is of any other type, then the call is ignored
  • +
+ +

+ Future versions of jOOQ will implement interning of data for non-String data types by collecting values in , removing duplicate instances. +

+ +

+ Note, that jOOQ will not use interned data for identity comparisons: string1 == string2. Interning is used only to reduce the memory footprint of objects. +

+
+
+
+
+ +
+ Static statements vs. Prepared Statements + +

+ With JDBC, you have full control over your SQL statements. You can decide yourself, if you want to execute a static without bind values, or a with (or without) bind values. But you have to decide early, which way to go. And you'll have to prevent SQL injection and syntax errors manually, when inlining your bind variables. +

+

+ With jOOQ, this is easier. As a matter of fact, it is plain simple. With jOOQ, you can just set a flag in your , and all queries produced by that configuration will be executed as static statements, with all bind values inlined. An example is given here: +

+ + + + + + +

Reasons for choosing one or the other

+

+ Not all databases are equal. Some databases show improved performance if you use , as the database will then be able to re-use execution plans for identical SQL statements, regardless of actual bind values. This heavily improves the time it takes for soft-parsing a SQL statement. In other situations, assuming that bind values are irrelevant for SQL execution plans may be a bad idea, as you might run into "bind value peeking" issues. You may be better off spending the extra cost for a new hard-parse of your SQL statement and instead having the database fine-tune the new plan to the concrete bind values. +

+

+ Whichever aproach is more optimal for you cannot be decided by jOOQ. In most cases, prepared statements are probably better. But you always have the option of forcing jOOQ to render inlined bind values. +

+ +

Inlining bind values on a per-bind-value basis

+

+ Note that you don't have to inline all your bind values at once. If you know that a bind value is not really a variable and should be inlined explicitly, you can do so by using , as documented in the manual's section about +

+
+
+ +
+ Reusing a Query's PreparedStatement + +

+ As previously discussed in the chapter about , reusing PreparedStatements is handled a bit differently in jOOQ from how it is handled in JDBC +

+ +

Keeping open PreparedStatements with JDBC

+

+ With JDBC, you can easily reuse a by not closing it between subsequent executions. An example is given here: +

+ + + +

+ The above technique can be quite useful when you want to reuse expensive database resources. This can be the case when your statement is executed very frequently and your database would take non-negligible time to soft-parse the prepared statement and generate a new statement / cursor resource. +

+ +

Keeping open PreparedStatements with jOOQ

+

+ This is also modeled in jOOQ. However, the difference to JDBC is that closing a statement is the default action, whereas keeping it open has to be configured explicitly. This is better than JDBC, because the default action should be the one that is used most often. Keeping open statements is rarely done in average applications. Here's an example of how to keep open PreparedStatements with jOOQ: +

+ + query = create.selectOne().keepStatement(true)) { + Result result1 = query.fetch(); // This will lazily create a new PreparedStatement + Result result2 = query.fetch(); // This will reuse the previous PreparedStatement +}]]> + +

+ The above example shows how a query can be executed twice against the same underlying PreparedStatement. Notice how the Query must now be treated like a resource, i.e. it must be managed in a try-with-resources statement, or must be called explicitly. +

+
+
+ +
+ JDBC flags + +

+ JDBC knows a couple of execution flags and modes, which can be set through the jOOQ API as well. jOOQ essentially supports these flags and execution modes: +

+ + + + extends Query { + + // [...] + + // The query execution timeout. + // ----------------------------------------------------------- + @Override + ResultQuery queryTimeout(int timeout); + + // Flags allowing to specify the resulting ResultSet modes + // ----------------------------------------------------------- + ResultQuery resultSetConcurrency(int resultSetConcurrency); + ResultQuery resultSetType(int resultSetType); + ResultQuery resultSetHoldability(int resultSetHoldability); + + // The maximum number of rows to be fetched by JDBC + // ----------------------------------------------------------- + ResultQuery maxRows(int rows); + +}]]> + +

Using ResultSet concurrency with ExecuteListeners

+

+ An example of why you might want to manually set a ResultSet's concurrency flag to something non-default is given here: +

+ + + +

+ In the above example, your custom is triggered before jOOQ loads a new Record from the JDBC ResultSet. With the concurrency being set to ResultSet.CONCUR_UPDATABLE, you can now modify the database cursor through the standard JDBC ResultSet API. +

+
+
+ +
+ Using JDBC batch operations + +

+ With JDBC, you can easily execute several statements at once using the addBatch() method. Essentially, there are two modes in JDBC +

+ +
    +
  • Execute several queries without bind values
  • +
  • Execute one query several times with bind values
  • +
+ +

Using JDBC

+

+ In code, this looks like the following snippet: +

+ + + +

Using jOOQ

+

+ jOOQ supports executing queries in batch mode as follows: +

+ + + +

+ When creating a batch execution with a single query and multiple bind values, you will still have to provide jOOQ with dummy bind values for the original query. In the above example, these are set to null. For subsequent calls to bind(), there will be no type safety provided by jOOQ. +

+
+
+ +
+ Sequence execution + +

+ Most databases support sequences of some sort, to provide you with unique values to be used for primary keys and other enumerations. If you're using jOOQ's , it will generate a sequence object per sequence for you. There are two ways of using such a sequence object: +

+ +

Standalone calls to sequences

+

+ Instead of actually phrasing a select statement, you can also use the convenience methods: +

+ + + +

Inlining sequence references in SQL

+

+ You can inline sequence references in jOOQ SQL statements. The following are examples of how to do that: +

+ + + +

+ For more info about inlining sequence references in SQL statements, please refer to the manual's section about . +

+
+
+ +
+ Stored procedures and functions + +

+ Many RDBMS support the concept of "routines", usually calling them procedures and/or functions. These concepts have been around in programming languages for a while, also outside of databases. Famous languages distinguishing procedures from functions are: +

+
    +
  • Ada
  • +
  • BASIC
  • +
  • Pascal
  • +
  • etc...
  • +
+

+ The general distinction between (stored) procedures and (stored) functions can be summarised like this: +

+ +

Procedures

+
    +
  • Are called using JDBC CallableStatement
  • +
  • Have no return value
  • +
  • Usually support OUT parameters
  • +
+ +

Functions

+
    +
  • Can be used in SQL statements
  • +
  • Have a return value
  • +
  • Usually don't support OUT parameters
  • +
+ +

Exceptions to these rules

+
    +
  • DB2, H2, and HSQLDB don't allow for JDBC escape syntax when calling functions. Functions must be used in a SELECT statement
  • +
  • H2 only knows functions (without OUT parameters)
  • +
  • Oracle functions may have OUT parameters
  • +
  • Oracle knows functions that must not be used in SQL statements for transactional reasons
  • +
  • Postgres only knows functions (with all features combined). OUT parameters can also be interpreted as return values, which is quite elegant/surprising, depending on your taste
  • +
  • The Sybase jconn3 JDBC driver doesn't handle null values correctly when using the JDBC escape syntax on functions
  • +
+

+ In general, it can be said that the field of routines (procedures / functions) is far from being standardised in modern RDBMS even if the SQL:2008 standard specifies things quite well. Every database has its ways and JDBC only provides little abstraction over the great variety of procedures / functions implementations, especially when advanced data types such as cursors / UDT's / arrays are involved. +

+

+ To simplify things a little bit, jOOQ handles both procedures and functions the same way, using a more general type. +

+ +

Using jOOQ for standalone calls to stored procedures and functions

+

+ If you're using jOOQ's , it will generate objects for you. Let's consider the following example: +

+ + + +

+ The generated artefacts can then be used as follows: +

+ + + +

+ But you can also call the procedure using a generated convenience method in a global Routines class: +

+ + + +

+ For more details about for procedures, see the manual's section about . +

+ +

Inlining stored function references in SQL

+

+ Unlike procedures, functions can be inlined in SQL statements to generate or , if you're using . Assume you have a function like this: +

+ + + +

+ The generated artefacts can then be used as follows: +

+ + + + + + +

+ For more info about inlining stored function references in SQL statements, please refer to the manual's section about . +

+
+ + +
+ Oracle Packages + +

+ Oracle uses the concept of a PACKAGE to group several procedures/functions into a sort of namespace. The SQL 92 standard talks about "modules", to represent this concept, even if this is rarely implemented as such. This is reflected in jOOQ by the use of Java sub-packages in the destination package. Every Oracle package will be reflected by +

+
    +
  • A Java package holding classes for formal Java representations of the procedure/function in that package
  • +
  • A Java class holding convenience methods to facilitate calling those procedures/functions
  • +
+

+ Apart from this, the generated source code looks exactly like the one for standalone procedures/functions. +

+

+ For more details about for procedures and packages see the manual's section about . +

+
+
+ +
+ Oracle member procedures + +

+ Oracle UDTs can have object-oriented structures including member functions and procedures. With Oracle, you can do things like this: +

+ + + +

+ These member functions and procedures can simply be mapped to Java methods: +

+ + + +

+ For more details about for UDTs see the manual's section about . +

+
+
+
+
+ +
+ Exporting to XML, CSV, JSON, HTML, Text + +

+ If you are using jOOQ for scripting purposes or in a slim, unlayered application server, you might be interested in using jOOQ's exporting functionality (see also the ). You can export any Result<Record> into the formats discussed in the subsequent chapters of the manual +

+
+ + +
+ Exporting XML + + +// Fetch books and format them as XML +String xml = create.selectFrom(BOOK).fetch().formatXML(); + +

+ The above query will result in an XML document looking like the following one: +

+ + + + + + + + + + 1 + 1 + 1984 + + + 2 + 1 + Animal Farm + + +]]> + +

+ The same result as an can be obtained using the Result.intoXML() method: +

+ + +// Fetch books and format them as XML +Document xml = create.selectFrom(BOOK).fetch().intoXML(); + +

+ See the XSD schema definition here, for a formal definition of the XML export format:
+ http://www.jooq.org/xsd/jooq-export-{export-xsd-version}.xsd +

+
+
+ +
+ Exporting CSV + + +// Fetch books and format them as CSV +String csv = create.selectFrom(BOOK).fetch().formatCSV(); + +

+ The above query will result in a CSV document looking like the following one: +

+ +ID,AUTHOR_ID,TITLE +1,1,1984 +2,1,Animal Farm + +

+ In addition to the standard behaviour, you can also specify a separator character, as well as a special string to represent NULL values (which cannot be represented in standard CSV): +

+ +// Use ";" as the separator character +String csv = create.selectFrom(BOOK).fetch().formatCSV(';'); + +// Specify "{null}" as a representation for NULL values +String csv = create.selectFrom(BOOK).fetch().formatCSV(';', "{null}"); + +
+
+ +
+ Exporting JSON + + +// Fetch books and format them as JSON +String json = create.selectFrom(BOOK).fetch().formatJSON(); + +

+ The above query will result in a JSON document looking like the following one: +

+ +{"fields":[{"schema":"schema-1","table":"table-1","name":"field-1","type":"type-1"}, + {"schema":"schema-2","table":"table-2","name":"field-2","type":"type-2"}, + ..., + {"schema":"schema-n","table":"table-n","name":"field-n","type":"type-n"}], + "records":[[value-1-1,value-1-2,...,value-1-n], + [value-2-1,value-2-2,...,value-2-n]]} + +

+ Note: This format has been modified in jOOQ 2.6.0 and 3.7.0 +

+
+
+ +
+ Exporting HTML + +// Fetch books and format them as HTML +String html = create.selectFrom(BOOK).fetch().formatHTML(); + +

+ The above query will result in an HTML document looking like the following one +

+ + + + + ID + AUTHOR_ID + TITLE + + + + + 1 + 1 + 1984 + + + 2 + 1 + Animal Farm + + +]]> + +
+
+ +
+ Exporting Text + + +// Fetch books and format them as text +String text = create.selectFrom(BOOK).fetch().format(); + +

+ The above query will result in a text document looking like the following one +

+ ++---+---------+-----------+ +| ID|AUTHOR_ID|TITLE | ++---+---------+-----------+ +| 1| 1|1984 | +| 2| 1|Animal Farm| ++---+---------+-----------+ + +

+ A simple text representation can also be obtained by calling toString() on a Result object. See also the manual's section about +

+
+
+
+
+ +
+ Importing data + +

+ If you are using jOOQ for scripting purposes or in a slim, unlayered application server, you might be interested in using jOOQ's importing functionality (see also exporting functionality). You can import data directly into a table from the formats described in the subsequent sections of this manual. +

+
+ + +
+ Importing CSV + + +

+ The below CSV data represents two author records that may have been exported previously, by jOOQ's , and then modified in Microsoft Excel or any other spreadsheet tool: +

+ + + +

+ With jOOQ, you can load this data using various parameters from the loader API. A simple load may look like this: +

+ +DSLContext create = DSL.using(connection, dialect); + +// Load data into the BOOK table from an input stream +// holding the CSV data. +create.loadInto(BOOK) + .loadCSV(inputstream, encoding) + .fields(BOOK.ID, BOOK.AUTHOR_ID, BOOK.TITLE) + .execute(); + +

+ Here are various other examples: +

+ +// Ignore the AUTHOR_ID column from the CSV file when inserting +create.loadInto(BOOK) + .loadCSV(inputstream, encoding) + .fields(AUTHOR.ID, null, AUTHOR.TITLE) + .execute(); + +// Specify behaviour for duplicate records. +create.loadInto(BOOK) + + // choose any of these methods + .onDuplicateKeyUpdate() + .onDuplicateKeyIgnore() + .onDuplicateKeyError() // the default + + .loadCSV(inputstream) + .fields(BOOK.ID, null, BOOK.TITLE) + .execute(); + +// Specify behaviour when errors occur. +create.loadInto(BOOK) + + // choose any of these methods + .onErrorIgnore() + .onErrorAbort() // the default + + .loadCSV(inputstream, encoding) + .fields(BOOK.ID, null, BOOK.TITLE) + .execute(); + +// Specify transactional behaviour where this is possible +// (e.g. not in container-managed transactions) +create.loadInto(BOOK) + + // choose any of these methods + .commitEach() + .commitAfter(10) + .commitAll() + .commitNone() // the default + + .loadCSV(inputstream, encoding) + .fields(BOOK.ID, null, BOOK.TITLE) + .execute(); + +

+ Any of the above configuration methods can be combined to achieve the type of load you need. Please refer to the API's Javadoc to learn about more details. Errors that occur during the load are reported by the execute method's result: +

+ + loader = /* .. */ .execute(); + +// The number of processed rows +int processed = loader.processed(); + +// The number of stored rows (INSERT or UPDATE) +int stored = loader.stored(); + +// The number of ignored rows (due to errors, or duplicate rule) +int ignored = loader.ignored(); + +// The errors that may have occurred during loading +List errors = loader.errors(); +LoaderError error = errors.get(0); + +// The exception that caused the error +DataAccessException exception = error.exception(); + +// The row that caused the error +int rowIndex = error.rowIndex(); +String[] row = error.row(); + +// The query that caused the error +Query query = error.query();]]> + +
+
+ +
+ Importing JSON + + +

+ The below JSON data represents two author records that may have been exported previously, by jOOQ's : +

+ + + +

+ With jOOQ, you can load this data using various parameters from the loader API. A simple load may look like this: +

+ +DSLContext create = DSL.using(connection, dialect); + +// Load data into the BOOK table from an input stream +// holding the JSON data. +create.loadInto(BOOK) + .loadJSON(inputstream, encoding) + .fields(BOOK.ID, BOOK.AUTHOR_ID, BOOK.TITLE) + .execute(); + +

+ No other, JSON-specific options are currently available. For additional Loader API options, please refer to the manual's section about . +

+
+
+ +
+ Importing Records + +

+ A common use-case for importing records via jOOQ's Loader API is when data needs to be transferred between databases. For instance, when fetching the following data from database 1: +

+ +> result = +DSL.using(configuration1) + .select(BOOK.ID, BOOK.AUTHOR_ID, BOOK.TITLE) + .from(BOOK) + .fetch(); +]]> + +

+ Now, this result should be imported back into a database 2: +

+ + + +

+ No other, Record-specific options are currently available. For additional Loader API options, please refer to the manual's section about . +

+
+
+ +
+ Importing Arrays + +

+ A common use-case for importing arrays via jOOQ's Loader API is when data is fetched into memory from some data source, or even ad-hoc data, which needs to be imported into a database. +

+ + + +

+ No other, Array-specific options are currently available. For additional Loader API options, please refer to the manual's section about . +

+
+
+ +
+ Importing XML + +

This is not yet supported

+
+
+
+
+ +
+ CRUD with UpdatableRecords + +

+ Your database application probably consists of 50% - 80% CRUD, whereas only the remaining 20% - 50% of querying is actual querying. Most often, you will operate on records of tables without using any advanced relational concepts. This is called CRUD for +

+
    +
  • Create ()
  • +
  • Read ()
  • +
  • Update ()
  • +
  • Delete ()
  • +
+

+ CRUD always uses the same patterns, regardless of the nature of underlying tables. This again, leads to a lot of boilerplate code, if you have to issue your statements yourself. Like Hibernate / JPA and other ORMs, jOOQ facilitates CRUD using a specific API involving types. +

+ +

Primary keys and updatability

+

+ In normalised databases, every table has a primary key by which a tuple/record within that table can be uniquely identified. In simple cases, this is a (possibly auto-generated) number called ID. But in many cases, primary keys include several non-numeric columns. An important feature of such keys is the fact that in most databases, they are enforced using an index that allows for very fast random access to the table. A typical way to access / modify / delete a book is this: +

+ + + +

+ Normalised databases assume that a primary key is unique "forever", i.e. that a key, once inserted into a table, will never be changed or re-inserted after deletion. In order to use jOOQ's operations correctly, you should design your database accordingly. +

+
+ + +
+ Simple CRUD + +

+ If you're using jOOQ's , it will generate implementations for every table that has a primary key. When such a record form the database, these records are "attached" to the that created them. This means that they hold an internal reference to the same database connection that was used to fetch them. This connection is used internally by any of the following methods of the UpdatableRecord: +

+ + + +

+ See the manual's section about for some more insight on "attached" objects. +

+ +

Storing

+

+ Storing a record will perform an or an . In general, new records are always inserted, whereas records loaded from the database are always updated. This is best visualised in code: +

+ + + +

+ Some remarks about storing: +

+
    +
  • jOOQ sets only modified values in or . This allows for default values to be applied to inserted records, as specified in CREATE TABLE DDL statements.
  • +
  • When store() performs an , jOOQ attempts to load any generated keys from the database back into the record. For more details, see the manual's section about .
  • +
  • When loading records from , jOOQ will assume the record is a new record. It will hence attempt to INSERT it.
  • +
  • When you activate , storing a record may fail, if the underlying database record has been changed in the mean time.
  • +
+ +

Deleting

+

+ Deleting a record will remove it from the database. Here's how you delete records: +

+ + + +

Refreshing

+

+ Refreshing a record from the database means that jOOQ will issue a to refresh all record values that are not the primary key. This is particularly useful when you use jOOQ's feature, in case a modified record is "stale" and cannot be stored to the database, because the underlying database record has changed in the mean time. +

+

+ In order to perform a refresh, use the following Java code: +

+ + + +

CRUD and SELECT statements

+

+ CRUD operations can be combined with regular querying, if you select records from single database tables, as explained in the manual's section about . For this, you will need to use the selectFrom() method from the : +

+ + +
+
+ +
+ Records' internal flags + +

+ All of jOOQ's maintain an internal state for every column value. This state is composed of three elements: +

+
    +
  • The value itself
  • +
  • The "original" value, i.e. the value as it was originally fetched from the database or null, if the record was never in the database
  • +
  • The "changed" flag, indicating if the value was ever changed through the Record API.
  • +
+ +

+ The purpose of the above information is for jOOQ's to know, which values need to be stored to the database, and which values have been left untouched. +

+
+
+ +
+ IDENTITY values + +

+ Many databases support the concept of IDENTITY values, or key values. This is reflected by JDBC's method. jOOQ abstracts using this method as many databases and JDBC drivers behave differently with respect to generated keys. Let's assume the following SQL Server BOOK table: +

+ + + +

+ If you're using jOOQ's , the above table will generate a with an IDENTITY column. This information is used by jOOQ internally, to update IDs after calling : +

+ + + +

Database compatibility

+

+ DB2, Derby, HSQLDB, Ingres +

+

+ These SQL dialects implement the standard very neatly. +

+ + +

+ H2, MySQL, Postgres, SQL Server, Sybase ASE, Sybase SQL Anywhere +

+

+ These SQL dialects implement identites, but the DDL syntax doesn’t follow the standard +

+ + +

+ Oracle +

+

+ Oracle does not know any identity columns at all. Instead, you will have to use a trigger and update the ID column yourself, using a custom sequence. Something along these lines: +

+ + +

+ Note, that this approach can be employed in most databases supporting sequences and triggers! It is a lot more flexible than standard identities +

+
+
+ + + +
+ Non-updatable records + +

+ Tables without a PRIMARY KEY are considered non-updatable by jOOQ, as jOOQ has no way of uniquely identifying such a record within the database. If you're using jOOQ's , such tables will generate classes, instead of classes. When you fetch from such a table, the returned records will not allow for calling any of the methods. +

+ +

+ Note, that some databases use internal rowid or object-id values to identify such records. jOOQ does not support these vendor-specific record meta-data. +

+
+
+ +
+ Optimistic locking + +

+ jOOQ allows you to perform operations using optimistic locking. You can immediately take advantage of this feature by activating the relevant . Without any further knowledge of the underlying data semantics, this will have the following impact on store() and delete() methods: +

+
    +
  • INSERT statements are not affected by this Setting flag
  • +
  • Prior to UPDATE or DELETE statements, jOOQ will run a statement, pessimistically locking the record for the subsequent UPDATE / DELETE
  • +
  • The data fetched with the previous SELECT will be compared against the data in the record being stored or deleted
  • +
  • An is thrown if the record had been modified in the mean time
  • +
  • The record is successfully stored / deleted, if the record had not been modified in the mean time.
  • +
+

+ The above changes to jOOQ's behaviour are transparent to the API, the only thing you need to do for it to be activated is to set the Settings flag. Here is an example illustrating optimistic locking: +

+ + + +

Optimised optimistic locking using TIMESTAMP fields

+

+ If you're using jOOQ's , you can take indicate TIMESTAMP or UPDATE COUNTER fields for every generated table in the . Let's say we have this table: +

+ + + +

+ The MODIFIED column will contain a timestamp indicating the last modification timestamp for any book in the BOOK table. If you're using jOOQ and it's , jOOQ will then generate this TIMESTAMP value for you, automatically. However, instead of running an additional statement prior to an UPDATE or DELETE statement, jOOQ adds a WHERE-clause to the UPDATE or DELETE statement, checking for TIMESTAMP's integrity. This can be best illustrated with an example: +

+ + + +

+ As before, without the added TIMESTAMP column, optimistic locking is transparent to the API. +

+ +

Optimised optimistic locking using VERSION fields

+

+ Instead of using TIMESTAMPs, you may also use numeric VERSION fields, containing version numbers that are incremented by jOOQ upon store() calls. +

+ +

+ Note, for explicit pessimistic locking, please consider the manual's section about the . For more details about how to configure TIMESTAMP or VERSION fields, consider the manual's section about . +

+
+
+ +
+ Batch execution + +

+ When inserting, updating, deleting a lot of records, you may wish to profit from JDBC batch operations, which can be performed by jOOQ. These are available through jOOQ's as shown in the following example: +

+ + books = create.fetch(BOOK); + +// Modify the above books, and add some new ones: +modify(books); +addMore(books); + +// Batch-update and/or insert all of the above books +create.batchStore(books);]]> + +

+ Internally, jOOQ will render all the required SQL statements and execute them as a regular . +

+
+
+ +
+ CRUD SPI: RecordListener + +

+ When performing CRUD, you may want to be able to centrally register one or several listener objects that receive notification every time CRUD is performed on an . Example use cases of such a listener are: +

+
    +
  • Adding a central ID generation algorithm, generating UUIDs for all of your records.
  • +
  • Adding a central record initialisation mechanism, preparing the database prior to inserting a new record.
  • +
+ +

+ An example of such a RecordListener is given here: +

+ + + +

+ Now, configure jOOQ's runtime to load your listener +

+ + + +

+ For a full documentation of what RecordListener can do, please consider the . Note that RecordListener instances can be registered with a independently of . +

+
+
+
+
+ +
+ DAOs + +

+ If you're using jOOQ's , you can configure it to generate and DAOs for you. jOOQ then generates one DAO per , i.e. per table with a single-column primary key. Generated DAOs implement a common jOOQ type called . This type contains the following methods: +

+ + corresponds to the DAO's related table +//

corresponds to the DAO's related generated POJO type +// corresponds to the DAO's related table's primary key type. +// Note that multi-column primary keys are not yet supported by DAOs +public interface DAO, P, T> { + + // These methods allow for inserting POJOs + void insert(P object) throws DataAccessException; + void insert(P... objects) throws DataAccessException; + void insert(Collection

objects) throws DataAccessException; + + // These methods allow for updating POJOs based on their primary key + void update(P object) throws DataAccessException; + void update(P... objects) throws DataAccessException; + void update(Collection

objects) throws DataAccessException; + + // These methods allow for deleting POJOs based on their primary key + void delete(P... objects) throws DataAccessException; + void delete(Collection

objects) throws DataAccessException; + void deleteById(T... ids) throws DataAccessException; + void deleteById(Collection ids) throws DataAccessException; + + // These methods allow for checking record existence + boolean exists(P object) throws DataAccessException; + boolean existsById(T id) throws DataAccessException; + long count() throws DataAccessException; + + // These methods allow for retrieving POJOs by primary key or by some other field + List

findAll() throws DataAccessException; + P findById(T id) throws DataAccessException; + List

fetch(Field field, Z... values) throws DataAccessException; + P fetchOne(Field field, Z value) throws DataAccessException; + + // These methods provide DAO meta-information + Table getTable(); + Class

getType(); +}]]> + +

+ Besides these base methods, generated DAO classes implement various useful fetch methods. An incomplete example is given here, for the BOOK table: +

+ + { + + // Columns with primary / unique keys produce fetchOne() methods + public Book fetchOneById(Integer value) { ... } + + // Other columns produce fetch() methods, returning several records + public List fetchByAuthorId(Integer... values) { ... } + public List fetchByTitle(String... values) { ... } +}]]> +

+ Note that you can further subtype those pre-generated DAO classes, to add more useful DAO methods to them. Using such a DAO is simple: +

+ + + +
+
+ +
+ Transaction management + +

+ There are essentially four ways how you can handle transactions in Java / SQL: +

+
    +
  • You can issue vendor-specific COMMIT, ROLLBACK and other statements directly in your database.
  • +
  • You can call JDBC's , and other methods on your JDBC driver.
  • +
  • You can use third-party transaction management libraries like Spring TX. Examples shown in the .
  • +
  • You can use a JTA-compliant Java EE transaction manager from your container.
  • +
+ +

+ While jOOQ does not aim to replace any of the above, it offers a simple API (and a corresponding SPI) to provide you with jOOQ-style programmatic fluency to express your transactions. Below are some Java examples showing how to implement (nested) transactions with jOOQ. For these examples, we're using Java 8 syntax. Java 8 is not a requirement, though. +

+ + { + AuthorRecord author = + DSL.using(configuration) + .insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .values("George", "Orwell") + .returning() + .fetchOne(); + + DSL.using(configuration) + .insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE) + .values(author.getId(), "1984") + .values(author.getId(), "Animal Farm") + .execute(); + + // Implicit commit executed here +}); +]]> + +

+ Note how the lambda expression receives a new, derived configuration that should be used within the local scope: +

+ + { + + // Wrap configuration in a new DSLContext: + DSL.using(configuration).insertInto(...); + DSL.using(configuration).insertInto(...); + + // Or, reuse the new DSLContext within the transaction scope: + DSLContext ctx = DSL.using(configuration); + ctx.insertInto(...); + ctx.insertInto(...); + + // ... but avoid using the scope from outside the transaction: + create.insertInto(...); + create.insertInto(...); +}); +]]> + +

+ While some implementations (e.g. ones based on ThreadLocals, e.g. Spring or JTA) may allow you to reuse the globally scoped reference, the jOOQ transaction API design allows for TransactionProvider implementations that require your transactional code to use the new, locally scoped Configuration, instead. +

+ +

+ Transactional code is wrapped in jOOQ's or types: +

+ + + { + T run(Configuration configuration) throws Exception; +}]]> + +

+ Such transactional code can be passed to or methods. +

+ +

Rollbacks

+ +

+ Any uncaught checked or unchecked exception thrown from your transactional code will rollback the transaction to the beginning of the block. This behaviour will allow for nesting transactions, if your configured supports nesting of transactions. An example can be seen here: +

+ + { + final AuthorRecord author = + DSL.using(outer) + .insertInto(AUTHOR, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME) + .values("George", "Orwell") + .returning() + .fetchOne(); + + // Implicit savepoint created here + try { + DSL.using(outer) + .transaction(nested -> { + DSL.using(nested) + .insertInto(BOOK, BOOK.AUTHOR_ID, BOOK.TITLE) + .values(author.getId(), "1984") + .values(author.getId(), "Animal Farm") + .execute(); + + // Rolls back the nested transaction + if (oops) + throw new RuntimeException("Oops"); + + // Implicit savepoint is discarded, but no commit is issued yet. + }); + } + catch (RuntimeException e) { + + // We can decide whether an exception is "fatal enough" to roll back also the outer transaction + if (isFatal(e)) + + // Rolls back the outer transaction + throw e; + } + + // Implicit commit executed here +}); +]]> + +

TransactionProvider implementations

+ +

+ By default, jOOQ ships with the , which implements nested transactions using JDBC . You can, however, implement your own and supply that to your to override jOOQ's default behaviour. A simple example implementation using Spring's DataSourceTransactionManager can be seen here: +

+ + + +

+ More information about how to use jOOQ with Spring can be found in the +

+
+
+ +
+ Exception handling + +

Checked vs. unchecked exceptions

+

+ This is an eternal and religious debate. Pros and cons have been discussed time and again, and it still is a matter of taste, today. In this case, jOOQ clearly takes a side. jOOQ's exception strategy is simple: +

+
    +
  • All "system exceptions" are unchecked. If in the middle of a transaction involving business logic, there is no way that you can recover sensibly from a lost database connection, or a constraint violation that indicates a bug in your understanding of your database model.
  • +
  • All "business exceptions" are checked. Business exceptions are true exceptions that you should handle (e.g. not enough funds to complete a transaction).
  • +
+

+ With jOOQ, it's simple. All of jOOQ's exceptions are "system exceptions", hence they are all unchecked. +

+ +

jOOQ's DataAccessException

+

+ jOOQ uses its own to wrap any underlying that might have occurred. Note that all methods in jOOQ that may cause such a DataAccessException document this both in the Javadoc as well as in their method signature. +

+

+ DataAccessException is subtyped several times as follows: +

+
    +
  • DataAccessException: General exception usually originating from a
  • +
  • DataChangedException: An exception indicating that the database's underlying record has been changed in the mean time (see )
  • +
  • DataTypeException: Something went wrong during type conversion
  • +
  • DetachedException: A SQL statement was executed on a "detached" or a "detached" .
  • +
  • InvalidResultException: An operation was performed expecting only one result, but several results were returned.
  • +
  • MappingException: Something went wrong when loading a record from a or when mapping a record into a POJO
  • +
+ +

Override jOOQ's exception handling

+

+ The following section about documents means of overriding jOOQ's exception handling, if you wish to deal separately with some types of constraint violations, or if you raise business errors from your database, etc. +

+
+
+ +
+ ExecuteListeners + +

+ The lets you specify a list of instances. The ExecuteListener is essentially an event listener for Query, Routine, or ResultSet render, prepare, bind, execute, fetch steps. It is a base type for loggers, debuggers, profilers, data collectors, triggers, etc. Advanced ExecuteListeners can also provide custom implementations of Connection, PreparedStatement and ResultSet to jOOQ in apropriate methods. +

+

+ For convenience and better backwards-compatibility, consider extending instead of implementing this interface. +

+ +

Example: Query statistics ExecuteListener

+

+ Here is a sample implementation of an ExecuteListener, that is simply counting the number of queries per type that are being executed using jOOQ: +

+ + STATISTICS = new HashMap(); + + // Count "start" events for every type of query executed by jOOQ + @Override + public void start(ExecuteContext ctx) { + synchronized (STATISTICS) { + Integer count = STATISTICS.get(ctx.type()); + + if (count == null) { + count = 0; + } + + STATISTICS.put(ctx.type(), count + 1); + } + } +}]]> + +

+ Now, configure jOOQ's runtime to load your listener +

+ + + +

+ And log results any time with a snippet like this: +

+ + +

+ This may result in the following log output: +

+ +15:16:52,982 INFO - TEST STATISTICS +15:16:52,982 INFO - --------------- +15:16:52,983 INFO - READ : 919 executions +15:16:52,983 INFO - WRITE : 117 executions +15:16:52,983 INFO - DDL : 2 executions +15:16:52,983 INFO - BATCH : 4 executions +15:16:52,983 INFO - ROUTINE : 21 executions +15:16:52,983 INFO - OTHER : 30 executions + +

+ Please read the for more details +

+ +

Example: Custom Logging ExecuteListener

+

+ The following depicts an example of a custom ExecuteListener, which pretty-prints all queries being executed by jOOQ to stdout: +

+ + +

+ See also the manual's sections about for more sample implementations of actual ExecuteListeners. +

+ +

Example: Bad query execution ExecuteListener

+

+ You can also use ExecuteListeners to interact with your SQL statements, for instance when you want to check if executed or statements contain a WHERE clause. This can be achieved trivially with the following sample ExecuteListener: +

+ + +

+ You might want to replace the above implementation with a more efficient and more reliable one, of course. +

+
+
+ +
+ Database meta data + +

+ Since jOOQ 3.0, a simple wrapping API has been added to wrap JDBC's rather awkward

+
+
+ +
+ Logging + +

+ jOOQ logs all SQL queries and fetched result sets to its internal DEBUG logger, which is implemented as an . By default, execute logging is activated in the . In order to see any DEBUG log output, put either log4j or slf4j on jOOQ's classpath along with their respective configuration. A sample log4j configuration can be seen here: +

+ + + + + + + + + + + + + +]]> + +

+ With the above configuration, let's fetch some data with jOOQ +

+ + + +

+ The above query may result in the following log output: +

+ + with bind values : select "BOOK"."ID", "BOOK"."TITLE" from "BOOK" order by "BOOK"."ID" asc limit 2 offset 1 +Query executed : Total: 1.439ms +Fetched result : +----+------------+ + : | ID|TITLE | + : +----+------------+ + : | 2|Animal Farm | + : | 3|O Alquimista| + : +----+------------+ +Finishing : Total: 4.814ms, +3.375ms +]]> + +

+ Essentially, jOOQ will log +

+
    +
  • The SQL statement as rendered to the prepared statement
  • +
  • The SQL statement with inlined bind values (for improved debugging)
  • +
  • The query execution time
  • +
  • The first 5 records of the result. This is formatted using
  • +
  • The total execution + fetching time
  • +
+ +

+ If you wish to use your own logger (e.g. avoiding printing out sensitive data), you can deactivate jOOQ's logger using and implement your own . +

+
+
+ +
+ Performance considerations + +

+ Many users may have switched from higher-level abstractions such as Hibernate to jOOQ, because of Hibernate's difficult-to-manage performance, when it comes to large database schemas and complex second-level caching strategies. However, jOOQ itself is not a lightweight database abstraction framework, and it comes with its own overhead. Please be sure to consider the following points: +

+
    +
  • It takes some time to construct jOOQ queries. If you can reuse the same queries, you might cache them. Beware of thread-safety issues, though, as jOOQ's is not necessarily threadsafe, and queries are "attached" to their creating DSLContext
  • +
  • It takes some time to render SQL strings. Internally, jOOQ reuses the same for the complete query, but some rendering elements may take their time. You could, of course, cache SQL generated by jOOQ and prepare your own objects
  • +
  • It takes some time to bind values to prepared statements. jOOQ does not keep any open prepared statements, internally. Use a sophisticated connection pool, that will cache prepared statements and inject them into jOOQ through the standard JDBC API
  • +
  • It takes some time to fetch results. By default, jOOQ will always fetch the complete into memory. Use to prevent that, and scroll over an open underlying database cursor
  • +
+ +

Optimise wisely

+

+ Don't be put off by the above paragraphs. You should optimise wisely, i.e. only in places where you really need very high throughput to your database. jOOQ's overhead compared to plain JDBC is typically less than 1ms per query. +

+
+
+ +
+ Alternative execution models + + +

+ Just because you can, doesn't mean you must. In this chapter, we'll show how you can use jOOQ to generate SQL statements that are then executed with other APIs, such as Spring's JdbcTemplate, or Hibernate. +

+ +
+ + +
+ Using jOOQ with JPA + + +

+ These sections will show how to use jOOQ with JPA's native query API in order to fetch tuples or managed entities using the Java EE standards. +

+ +

+ In all of the following sections, let's assume we have the following JPA entities to model our database: +

+ + books; + + @Override + public String toString() { + return "JPAAuthor [id=" + id + ", firstName=" + firstName + + ", lastName=" + lastName + ", book size=" + books.size() + "]"; + } +}]]> +
+ + +
+ Using jOOQ with JPA Native Query + + +

+ If your query doesn't really map to JPA entities, you can fetch ordinary, untyped Object[] representations for your database records by using the following utility method: +

+ + nativeQuery(EntityManager em, org.jooq.Query query) { + + // Extract the SQL statement from the jOOQ query: + Query result = em.createNativeQuery(query.getSQL()); + + // Extract the bind values from the jOOQ query: + List values = query.getBindValues(); + for (int i = 0; i < values.size(); i++) { + result.setParameter(i + 1, values.get(i)); + } + + return result.getResultList(); +}]]> + +

+ Note, if you're using or , make sure to take those into account as well. E.g. as follows: +

+ + nativeQuery(EntityManager em, org.jooq.Query query) { + + // Extract the SQL statement from the jOOQ query: + Query result = em.createNativeQuery(query.getSQL()); + + // Extract the bind values from the jOOQ query: + int i = 0; + for (Param param : query.getParams().values()) { + result.setParameter(i + 1, convertToDatabaseType(param)); + i++; + } + + return result.getResultList(); +} + +static Object convertToDatabaseType(Param param) { + return param.getBinding().converter().to(param.getValue()); +}]]> + +

+ This way, you can construct complex, type safe queries using the jOOQ API and have your execute it with all the transaction semantics attached: +

+ + books = +nativeQuery(em, DSL.using(configuration) + .select(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, BOOK.TITLE) + .from(AUTHOR) + .join(BOOK).on(AUTHOR.ID.eq(BOOK.AUTHOR_ID)) + .orderBy(BOOK.ID)); + +books.forEach((Object[] book) -> System.out.println(book[0] + " " + book[1] + " wrote " + book[2]));]]> + + + +
+ Using jOOQ with JPA entities + + +

+ The simplest way to fetch entities via the native query API is by passing the entity class along to the native query method. The following example maps jOOQ query results to JPA entities (). Just add the following utility method: +

+ + List nativeQuery(EntityManager em, org.jooq.Query query, Class type) { + + // Extract the SQL statement from the jOOQ query: + Query result = em.createNativeQuery(query.getSQL(), type); + + // Extract the bind values from the jOOQ query: + List values = query.getBindValues(); + for (int i = 0; i < values.size(); i++) { + result.setParameter(i + 1, values.get(i)); + } + + // There's an unsafe cast here, but we can be sure that we'll get the right type from JPA + return result.getResultList(); +}]]> + +

+ Note, if you're using or , make sure to take those into account as well. E.g. as follows: +

+ + nativeQuery(EntityManager em, org.jooq.Query query, Class type) { + + // Extract the SQL statement from the jOOQ query: + Query result = em.createNativeQuery(query.getSQL(), type); + + // Extract the bind values from the jOOQ query: + int i = 0; + for (Param param : query.getParams().values()) { + result.setParameter(i + 1, convertToDatabaseType(param)); + i++; + } + + return result.getResultList(); +} + +static Object convertToDatabaseType(Param param) { + return param.getBinding().converter().to(param.getValue()); +}]]> + +

+ With the above simple API, we're ready to write complex jOOQ queries and map their results to JPA entities: +

+ + authors = +nativeQuery(em, + DSL.using(configuration) + .select() + .from(AUTHOR) + .orderBy(AUTHOR.ID) +, JPAAuthor.class); + +authors.forEach(author -> { + System.out.println(author.firstName + " " + author.lastName + " wrote"); + + books.forEach(book -> { + System.out.println(" " + book.title); + }); +});]]> + + + +
+ Using jOOQ with JPA EntityResult + + +

+ While JPA specifies how the mapping should be implemented (e.g. using ), there are no limitations regarding how you want to generate the SQL statement. The following, simple example shows how you can produce JPABook and JPAAuthor entities () from a jOOQ-generated SQL statement. +

+ +

+ In order to do so, we'll need to specify the . This can be done on any entity, and in this case, we're using : +

+ + + +

+ Note how we need to map between: +

+
    +
  • , which corresponds to the entity's attribute name
  • +
  • , which corresponds to the SQL result's column name
  • +
+ +

+ With the above boilerplate in place, we can now fetch entities using jOOQ and JPA: +

+ + List nativeQuery(EntityManager em, org.jooq.Query query, String resultSetMapping) { + + // Extract the SQL statement from the jOOQ query: + Query result = em.createNativeQuery(query.getSQL(), resultSetMapping); + + // Extract the bind values from the jOOQ query: + List values = query.getBindValues(); + for (int i = 0; i < values.size(); i++) { + result.setParameter(i + 1, values.get(i)); + } + + return result.getResultList(); +}]]> + +

+ Note, if you're using or , make sure to take those into account as well. E.g. as follows: +

+ + List nativeQuery(EntityManager em, org.jooq.Query query, String resultSetMapping) { + + // Extract the SQL statement from the jOOQ query: + Query result = em.createNativeQuery(query.getSQL(), resultSetMapping); + + // Extract the bind values from the jOOQ query: + int i = 0; + for (Param param : query.getParams().values()) { + result.setParameter(i + 1, convertToDatabaseType(param)); + i++; + } + + return result.getResultList(); +} + +static Object convertToDatabaseType(Param param) { + return param.getBinding().converter().to(param.getValue()); +}]]> + +

Using the above API

+ +

+ Now that we have everything setup, we can use the above API to run a jOOQ query to fetch JPA entities like this: +

+ + result = +nativeQuery(em, + DSL.using(configuration + .select( + AUTHOR.ID.as("a_id"), + AUTHOR.FIRST_NAME.as("a_first_name"), + AUTHOR.LAST_NAME.as("a_last_name"), + BOOK.ID.as("b_id"), + BOOK.AUTHOR_ID.as("b_author_id"), + BOOK.TITLE.as("b_title") + ) + .from(AUTHOR) + .join(BOOK).on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) + .orderBy(BOOK.ID)), + "bookmapping" // The name of the SqlResultSetMapping +); + +result.forEach((Object[] entities) -> { + JPAAuthor author = (JPAAuthor) entities[1]; + JPABook book = (JPABook) entities[0]; + + System.out.println(author.firstName + " " + author.lastName + " wrote " + book.title); +});]]> +

+ The entities are now ready to be modified and persisted again. +

+ +

+ Caveats: +

+
    +
  • We have to reference the result set mapping by name (a String) - there is no type safety involved here
  • +
  • We don't know the type contained in the resulting List - there is a potential for ClassCastException
  • +
  • The results are in fact a list of Object[], with the individual entities listed in the array, which need explicit casting
  • +
+ + + + + + + + + + +
+ Code generation + +

+ While optional, source code generation is one of jOOQ's main assets if you wish to increase developer productivity. jOOQ's code generator takes your database schema and reverse-engineers it into a set of Java classes modelling , , , , , , user-defined types and many more. +

+

+ The essential ideas behind source code generation are these: +

+
    +
  • Increased IDE support: Type your Java code directly against your database schema, with all type information available
  • +
  • Type-safety: When your database schema changes, your generated code will change as well. Removing columns will lead to compilation errors, which you can detect early.
  • +
+

+ The following chapters will show how to configure the code generator and how to generate various artefacts. +

+
+ + +
+ Configuration and setup of the generator + +

+ There are three binaries available with jOOQ, to be downloaded from http://www.jooq.org/download or from Maven central: +

+
    +
  • + jooq-{jooq-version}.jar +
    + The main library that you will include in your application to run jOOQ +
  • +
  • + jooq-meta-{jooq-version}.jar +
    + The utility that you will include in your build to navigate your database schema for code generation. This can be used as a schema crawler as well. +
  • +
  • + jooq-codegen-{jooq-version}.jar +
    + The utility that you will include in your build to generate your database schema +
  • +
+ +

Configure jOOQ's code generator

+

+ You need to tell jOOQ some things about your database connection. Here's an example of how to do it for an Oracle database +

+ + + + + + oracle.jdbc.OracleDriver + jdbc:oracle:thin:@[your jdbc connection parameters] + [your database user] + [your database password] + + + + user[db-user] + password[db-password] + + + + + + + org.jooq.util.oracle.OracleDatabase + + + .* + + + + UNUSED_TABLE # This table (unqualified name) should not be generated + | PREFIX_.* # Objects with a given prefix should not be generated + | SECRET_SCHEMA\.SECRET_TABLE # This table (qualified name) should not be generated + | SECRET_ROUTINE # This routine (unqualified name) ... + + + + [your database schema / owner / name] + + + + + + + + + [org.jooq.your.packagename] + + + [/path/to/your/dir] + + +]]> + +

+ There are also lots of advanced configuration parameters, which will be treated in the Note, you can find the official XSD file for a formal specification at:
+ http://www.jooq.org/xsd/jooq-codegen-{codegen-xsd-version}.xsd +

+ +

Run jOOQ code generation

+

+ Code generation works by calling this class with the above property file as argument. +

+ +org.jooq.util.GenerationTool /jooq-config.xml + +

+ Be sure that these elements are located on the classpath: +

+ +
    +
  • The XML configuration file
  • +
  • jooq-{jooq-version}.jar, jooq-meta-{jooq-version}.jar, jooq-codegen-{jooq-version}.jar
  • +
  • The JDBC driver you configured
  • +
+ +

A command-line example (For Windows, unix/linux/etc will be similar)

+
    +
  • Put the property file, jooq*.jar and the JDBC driver into a directory, e.g. C:\temp\jooq
  • +
  • Go to C:\temp\jooq
  • +
  • Run java -cp jooq-{jooq-version}.jar;jooq-meta-{jooq-version}.jar;jooq-codegen-{jooq-version}.jar;[JDBC-driver].jar;. org.jooq.util.GenerationTool /[XML file]
  • +
+

+ Note that the property file must be passed as a classpath resource +

+ +

Run code generation from Eclipse

+

+ Of course, you can also run code generation from your IDE. In Eclipse, set up a project like this. Note that: +

+
    +
  • this example uses jOOQ's log4j support by adding log4j.xml and log4j.jar to the project classpath.
  • +
  • the actual jooq-{jooq-version}.jar, jooq-meta-{jooq-version}.jar, jooq-codegen-{jooq-version}.jar artefacts may contain version numbers in the file names.
  • +
+ +
+ Eclipse configuration +
+ +

+ Once the project is set up correctly with all required artefacts on the classpath, you can configure an Eclipse Run Configuration for org.jooq.util.GenerationTool. +

+
+ Eclipse configuration +
+ +

+ With the XML file as an argument +

+
+ Eclipse configuration +
+ +

+ And the classpath set up correctly +

+
+ Eclipse configuration +
+ +

+ Finally, run the code generation and see your generated artefacts +

+
+ Eclipse configuration +
+ +

Integrate generation with Maven

+

+ Using the official jOOQ-codegen-maven plugin, you can integrate source code generation in your Maven build process: +

+ + + + + + org.jooq + jooq-codegen-maven + {jooq-version} + + + + + + generate + + + + + + + + postgresql + postgresql + 8.4-702.jdbc4 + + + + + + + + + org.postgresql.Driver + jdbc:postgresql:postgres + postgres + test + + + + + + org.jooq.util.postgres.PostgresDatabase + .* + + public + + + org.jooq.util.maven.example + target/generated-sources/jooq + + + + +]]> + +

+ See a more complete example of a Maven pom.xml File in the . +

+ +

Use jOOQ generated classes in your application

+

+ Be sure, both jooq-{jooq-version}.jar and your generated package (see configuration) are located on your classpath. Once this is done, you can execute SQL statements with your generated classes. +

+
+
+ +
+ Advanced generator configuration + +

+ In the we have seen how jOOQ's source code generator is configured and run within a few steps. In this chapter we'll cover some advanced settings +

+ +

jooq-meta configuration

+

+ Within the <generator/> element, there are other configuration elements: +

+ + + + + + false + + + REC_VERSION + + + REC_TIMESTAMP + + + SCHEMA\.TABLE\.COLUMN(1|2) + + + MY_UNIQUE_KEY_NAME + + + false + + + true + + + [your database schema / owner / name] + + + false + + + + + ... + ... + ... + + [ ... ... ] + + + + SELECT :schema_name || '_' || MAX("version") FROM "schema_version" + + + ... + + + ... +]]> + +

+ Check out the some of the manual's "advanced" sections to find out more about the advanced configuration parameters. +

+
    +
  • +
  • +
+ +

jooq-codegen configuration

+

+ Also, you can add some optional advanced configuration parameters for the generator: +

+ + + + + true + + + true + + + true + + + true + + + true + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + true + + + false +]]> + +

Property interdependencies

+

+ Some of the above properties depend on other properties to work correctly. For instance, when generating immutable pojos, pojos must be generated. jOOQ will enforce such properties even if you tell it otherwise. Here is a list of property interdependencies: +

+ +
    +
  • When daos = true, then jOOQ will set relations = true
  • +
  • When daos = true, then jOOQ will set records = true
  • +
  • When daos = true, then jOOQ will set pojos = true
  • +
  • When immutablePojos = true, then jOOQ will set pojos = true
  • +
+
+
+ +
+ Programmatic generator configuration + +

Configuring your code generator with Java, Groovy, etc.

+

+ In the previous sections, we have covered how to set up jOOQ's code generator using XML, either by running a standalone Java application, or by using Maven. However, it is also possible to use jOOQ's GenerationTool programmatically. The XSD file used for the configuration (http://www.jooq.org/xsd/jooq-codegen-{codegen-xsd-version}.xsd) is processed using XJC to produce Java artefacts. The below example uses those artefacts to produce the equivalent configuration of the previous : +

+ + + +

+ For the above example, you will need all of jooq-{jooq-version}.jar, jooq-meta-{jooq-version}.jar, and jooq-codegen-{jooq-version}.jar, on your classpath. +

+ +

+ Manually loading the XML file +

+ +

+ Alternatively, you can also load parts of the configuration from an XML file using JAXB and programmatically modify other parts using the code generation API: +

+ + + + + org.h2.Driver + ]]> + +

+ Load the above using standard JAXB API: +

+ + + +

+ ... and then, modify parts of your configuration programmatically, for instance the JDBC user / password: +

+
+
+ +
+ Custom generator strategies + +

Using custom generator strategies to override naming schemes

+

+ jOOQ allows you to override default implementations of the code generator or the generator strategy. Specifically, the latter can be very useful if you want to inject custom behaviour into jOOQ's code generator with respect to naming classes, members, methods, and other Java objects. +

+ + + + + org.jooq.util.JavaGenerator + + + + org.jooq.util.DefaultGeneratorStrategy + +]]> + +

+ The following example shows how you can override the DefaultGeneratorStrategy to render table and column names the way they are defined in the database, rather than switching them to camel case: +

+ + getJavaClassImplements(Definition definition, Mode mode) { + return Arrays.asList(Serializable.class.getName(), Cloneable.class.getName()); + } + + /** + * Override this method to define the suffix to apply to routines when + * they are overloaded. + * + * Use this to resolve compile-time conflicts in generated source code, in + * case you make heavy use of procedure overloading + */ + @Override + public String getOverloadSuffix(Definition definition, Mode mode, String overloadIndex) { + return "_OverloadIndex_" + overloadIndex; + } +}]]> + +

An org.jooq.Table example:

+

+ This is an example showing which generator strategy method will be called in what place when generating tables. For improved readability, full qualification is omitted: +

+ + { +// 2: ^^^^ 3: ^^^^^^^^^^ + public static final Book BOOK = new Book(); +// 2: ^^^^ 4: ^^^^ + public final TableField ID = /* ... */ +// 3: ^^^^^^^^^^ 5: ^^ +} + +// 1: strategy.getJavaPackageName(table) +// 2: strategy.getJavaClassName(table) +// 3: strategy.getJavaClassName(table, Mode.RECORD) +// 4: strategy.getJavaIdentifier(table) +// 5: strategy.getJavaIdentifier(column) +]]> + +

An org.jooq.Record example:

+

+ This is an example showing which generator strategy method will be called in what place when generating records. For improved readability, full qualification is omitted: +

+ + { +// 2: ^^^^^^^^^^ 2: ^^^^^^^^^^ + public void setId(Integer value) { /* ... */ } +// 3: ^^^^^ + public Integer getId() { /* ... */ } +// 4: ^^^^^ +} + +// 1: strategy.getJavaPackageName(table, Mode.RECORD) +// 2: strategy.getJavaClassName(table, Mode.RECORD) +// 3: strategy.getJavaSetterName(column, Mode.RECORD) +// 4: strategy.getJavaGetterName(column, Mode.RECORD) +]]> + +

A POJO example:

+

+ This is an example showing which generator strategy method will be called in what place when generating pojos. For improved readability, full qualification is omitted: +

+ + + +

An out-of-the-box strategy to keep names as they are

+

+ By default, jOOQ's generator strategy will convert your database's UNDER_SCORE_NAMES to PascalCaseNames as this is a more common idiom in the Java ecosystem. If, however, you want to retain the names and the casing exactly as it is defined in your database, you can use the org.jooq.util.KeepNamesGeneratorStrategy, which will retain all names exactly as they are. +

+ +

+ More examples can be found here: +

+ +
+
+ +
+ Matcher strategies + +

Using custom matcher strategies

+

+ In the , we have seen how to override generator strategies programmatically. In this chapter, we'll see how such strategies can be configured in the XML or Maven . Instead of specifying a strategy name, you can also specify a <matchers/> element as such: +

+ +

+ NOTE: There had been an incompatible change between jOOQ 3.2 and jOOQ 3.3 in the configuration of these matcher strategies. See Issue #3217 for details. +

+ + + + + + + + + + + MY_SCHEMA + + + --> MatcherRule + --> MatcherRule + com.example.MyOptionalCustomInterface + + + + + + + + + MY_TABLE + + + --> MatcherRule + --> MatcherRule + com.example.MyOptionalCustomInterface + + + --> MatcherRule + com.example.MyOptionalCustomInterface + + + --> MatcherRule + com.example.MyOptionalCustomInterface + + + --> MatcherRule + com.example.MyOptionalCustomInterface + + + --> MatcherRule + com.example.MyOptionalCustomBaseClass + com.example.MyOptionalCustomInterface +
+
+ + + + + + + MY_FIELD + + + --> MatcherRule + --> MatcherRule + --> MatcherRule + --> MatcherRule + + + + + + + + + MY_ROUTINE + + + --> MatcherRule + --> MatcherRule + com.example.MyOptionalCustomInterface + + + + + + + + + MY_SEQUENCE + + + --> MatcherRule + + +
+
+
]]>
+ +

+ The above example used references to "MatcherRule", which is an XSD type that looks like this: +

+ + + + CAMEL + + + PREFIX_$0_SUFFIX +]]> + +

Some examples

+

+ The following example shows a matcher strategy that adds a "T_" prefix to all table classes and to table identifiers: +

+ + + + + + + + + + UPPER + T_$0 + + + PASCAL + T_$0 + +
+
+
+
+]]>
+ +

+ The following example shows a matcher strategy that renames BOOK table identifiers (or table identifiers containing BOOK) into BROCHURE (or tables containing BROCHURE): +

+ + + + + + + ^(.*?)_BOOK_(.*)$ + + UPPER + $1_BROCHURE_$2 + +
+
+
+
+]]>
+ +

+ For more information about each XML tag, please refer to the http://www.jooq.org/xsd/jooq-codegen-{codegen-xsd-version}.xsd XSD file. +

+
+
+ +
+ Custom code sections + +

Power users might choose to re-implement large parts of the org.jooq.util.JavaGenerator class. If you only want to add some custom code sections, however, you can extend the JavaGenerator and override only parts of it.

+ +

An example for generating custom class footers

+ + + +

+ The above example simply adds a class footer to , in this case, overriding the default toString() implementation. +

+ +

An example for generating custom class Javadoc

+ +"); + out.println(" * Table comment: " + table.getComment()); + } + + out.println(" */"); + } +} +]]> + + +

+ Any of the below methods can be overridden: +

+ + + +

+ When you override any of the above, do note that according to , incompatible changes may be introduced between minor releases, even if this should be the exception. +

+
+
+ +
+ Generated global artefacts + +

+ For increased convenience at the use-site, jOOQ generates "global" artefacts at the code generation root location, referencing tables, routines, sequences, etc. In detail, these global artefacts include the following: +

+
    +
  • Keys.java: This file contains all of the required primary key, unique key, foreign key and identity references in the form of static members of type .
  • +
  • Routines.java: This file contains all standalone routines (not in packages) in the form of static factory methods for types.
  • +
  • Sequences.java: This file contains all sequence objects in the form of static members of type .
  • +
  • Tables.java: This file contains all table objects in the form of static member references to the actual singleton object
  • +
  • UDTs.java: This file contains all UDT objects in the form of static member references to the actual singleton object
  • +
+ +

Referencing global artefacts

+

+ When referencing global artefacts from your client application, you would typically static import them as such: +

+ + +
+
+ +
+ Generated tables + +

+ Every table in your database will generate a implementation that looks like this: +

+ + { + + // The singleton instance + public static final Book BOOK = new Book(); + + // Generated columns + public final TableField ID = createField("ID", SQLDataType.INTEGER, this); + public final TableField AUTHOR_ID = createField("AUTHOR_ID", SQLDataType.INTEGER, this); + public final TableField ITLE = createField("TITLE", SQLDataType.VARCHAR, this); + + // Covariant aliasing method, returning a table of the same type as BOOK + @Override + public Book as(java.lang.String alias) { + return new Book(alias); + } + + // [...] +}]]> + +

Flags influencing generated tables

+

+ These flags from the influence generated tables: +

+
    +
  • recordVersionFields: Relevant methods from super classes are overridden to return the VERSION field
  • +
  • recordTimestampFields: Relevant methods from super classes are overridden to return the TIMESTAMP field
  • +
  • syntheticPrimaryKeys: This overrides existing primary key information to allow for "custom" primary key column sets
  • +
  • overridePrimaryKeys: This overrides existing primary key information to allow for unique key to primary key promotion
  • +
  • dateAsTimestamp: This influences all relevant columns
  • +
  • unsignedTypes: This influences all relevant columns
  • +
  • relations: Relevant methods from super classes are overridden to provide primary key, unique key, foreign key and identity information
  • +
  • instanceFields: This flag controls the "static" keyword on table columns, as well as aliasing convenience
  • +
  • records: The generated record type is referenced from tables allowing for type-safe single-table record fetching
  • +
+ +

Flags controlling table generation

+

+ Table generation cannot be deactivated +

+
+
+ +
+ Generated records + +

+ Every table in your database will generate an implementation that looks like this: +

+ + + +// An interface common to records and pojos can be generated, optionally +implements IBook { + + // Every column generates a setter and a getter + @Override + public void setId(Integer value) { + setValue(BOOK.ID, value); + } + + @Id + @Column(name = "ID", unique = true, nullable = false, precision = 7) + @Override + public Integer getId() { + return getValue(BOOK.ID); + } + + // More setters and getters + public void setAuthorId(Integer value) {...} + public Integer getAuthorId() {...} + + // Convenience methods for foreign key methods + public void setAuthorId(AuthorRecord value) { + if (value == null) { + setValue(BOOK.AUTHOR_ID, null); + } + else { + setValue(BOOK.AUTHOR_ID, value.getValue(AUTHOR.ID)); + } + } + + // Navigation methods + public AuthorRecord fetchAuthor() { + return create.selectFrom(AUTHOR).where(AUTHOR.ID.equal(getValue(BOOK.AUTHOR_ID))).fetchOne(); + } + + // [...] +}]]> + +

Flags influencing generated records

+

+ These flags from the influence generated records: +

+
    +
  • syntheticPrimaryKeys: This overrides existing primary key information to allow for "custom" primary key column sets, possibly promoting a TableRecord to an UpdatableRecord
  • +
  • overridePrimaryKeys: This overrides existing primary key information to allow for unique key to primary key promotion, possibly promoting a TableRecord to an UpdatableRecord
  • +
  • dateAsTimestamp: This influences all relevant getters and setters
  • +
  • unsignedTypes: This influences all relevant getters and setters
  • +
  • relations: This is needed as a prerequisite for navigation methods
  • +
  • daos: Records are a pre-requisite for DAOs. If DAOs are generated, records are generated as well
  • +
  • interfaces: If interfaces are generated, records will implement them
  • +
  • jpaAnnotations: JPA annotations are used on generated records
  • +
+ +

Flags controlling record generation

+

+ Record generation can be deactivated using the records flag +

+
+
+ +
+ Generated POJOs + +

+ Every table in your database will generate a POJO implementation that looks like this: +

+ + + +

Flags influencing generated POJOs

+

+ These flags from the influence generated POJOs: +

+
    +
  • dateAsTimestamp: This influences all relevant getters and setters
  • +
  • unsignedTypes: This influences all relevant getters and setters
  • +
  • interfaces: If interfaces are generated, POJOs will implement them
  • +
  • immutablePojos: Immutable POJOs have final members and no setters. All members must be passed to the constructor
  • +
  • daos: POJOs are a pre-requisite for DAOs. If DAOs are generated, POJOs are generated as well
  • +
  • jpaAnnotations: JPA annotations are used on generated records
  • +
  • validationAnnotations: JSR-303 validation annotations are used on generated records
  • +
+ +

Flags controlling POJO generation

+

+ POJO generation can be activated using the pojos flag +

+
+
+ +
+ Generated Interfaces + +

+ Every table in your database will generate an interface that looks like this: +

+ + + +

Flags influencing generated interfaces

+

+ These flags from the influence generated interfaces: +

+
    +
  • dateAsTimestamp: This influences all relevant getters and setters
  • +
  • unsignedTypes: This influences all relevant getters and setters
  • +
+ +

Flags controlling POJO generation

+

+ POJO generation can be activated using the interfaces flag +

+
+
+ +
+ Generated DAOs + +

Generated DAOs

+

+ Every table in your database will generate a implementation that looks like this: +

+ + { + + // Generated constructors + public BookDao() { + super(BOOK, Book.class); + } + + public BookDao(Configuration configuration) { + super(BOOK, Book.class, configuration); + } + + // Every column generates at least one fetch method + public List fetchById(Integer... values) { + return fetch(BOOK.ID, values); + } + + public Book fetchOneById(Integer value) { + return fetchOne(BOOK.ID, value); + } + + public List fetchByAuthorId(Integer... values) { + return fetch(BOOK.AUTHOR_ID, values); + } + + // [...] +}]]> + +

Flags controlling DAO generation

+

+ DAO generation can be activated using the daos flag +

+
+
+ +
+ Generated sequences + +

+ Every sequence in your database will generate a implementation that looks like this: +

+ + S_AUTHOR_ID = new SequenceImpl("S_AUTHOR_ID", TEST, SQLDataType.INTEGER); +}]]> + +

Flags controlling sequence generation

+

+ Sequence generation cannot be deactivated +

+
+
+ +
+ Generated procedures + +

+ Every procedure or function (routine) in your database will generate a implementation that looks like this: +

+ + { + + // All IN, IN OUT, OUT parameters and function return values generate a static member + public static final Parameter AUTHOR_NAME = createParameter("AUTHOR_NAME", SQLDataType.VARCHAR); + public static final Parameter RESULT = createParameter("RESULT", SQLDataType.NUMERIC); + + // A constructor for a new "empty" procedure call + public AuthorExists() { + super("AUTHOR_EXISTS", TEST); + + addInParameter(AUTHOR_NAME); + addOutParameter(RESULT); + } + + // Every IN and IN OUT parameter generates a setter + public void setAuthorName(String value) { + setValue(AUTHOR_NAME, value); + } + + // Every IN OUT, OUT and RETURN_VALUE generates a getter + public BigDecimal getResult() { + return getValue(RESULT); + } + + // [...] +}]]> + +

Package and member procedures or functions

+

+ Procedures or functions contained in packages or UDTs are generated in a sub-package that corresponds to the package or UDT name. +

+ +

Flags controlling routine generation

+

+ Routine generation cannot be deactivated +

+
+
+ +
+ Generated UDTs + +

+ Every UDT in your database will generate a implementation that looks like this: +

+ + { + + // The singleton UDT instance + public static final UAddressType U_ADDRESS_TYPE = new UAddressType(); + + // Every UDT attribute generates a static member + public static final UDTField ZIP = + createField("ZIP", SQLDataType.VARCHAR, U_ADDRESS_TYPE); + public static final UDTField CITY = + createField("CITY", SQLDataType.VARCHAR, U_ADDRESS_TYPE); + public static final UDTField COUNTRY = + createField("COUNTRY", SQLDataType.VARCHAR, U_ADDRESS_TYPE); + + // [...] +}]]> + +

+ Besides the implementation, a implementation is also generated +

+ + { + + // Every attribute generates a getter and a setter + + public void setZip(String value) {...} + public String getZip() {...} + public void setCity(String value) {...} + public String getCity() {...} + public void setCountry(String value) {...} + public String getCountry() {...} + + // [...] +}]]> + +

Flags controlling UDT generation

+

+ UDT generation cannot be deactivated +

+
+
+ + +
+ Data type rewrites + +

+ Sometimes, the actual database data type does not match the SQL data type that you would like to use in Java. This is often the case for ill-supported SQL data types, such as BOOLEAN or UUID. jOOQ's code generator allows you to apply simple data type rewriting. The following configuration will rewrite IS_VALID columns in all tables to be of type BOOLEAN. +

+ + + + + + + + + BOOLEAN + + + .*\.IS_VALID + + + .* + + +]]> + +

+ You must provide at least either an <expressions/> or a <types/> element, or both. +

+ +

+ See the section about for rewriting columns to your own custom data types. +

+
+
+ + +
+ Custom data types and type conversion + +

+ When using a custom type in jOOQ, you need to let jOOQ know about its associated . Ad-hoc usages of such converters has been discussed in the chapter about . However, when mapping a custom type onto a standard JDBC type, a more common use-case is to let jOOQ know about custom types at code generation time (if you're using non-standard JDBC types, like for example JSON or HSTORE, see the ). Use the following configuration elements to specify, that you'd like to use GregorianCalendar for all database fields that start with DATE_OF_ +

+ +

+ There's a simple configuration using only <forcedTypes/> configuration elements, and an advanced configuration using both <forcedTypes/> and <customTypes/> elements, which allow for greater reuse. +

+ +

Simple configuration

+ +

+ The simple configuration is most useful in most situations as it can do only by specifying <forcedTypes/>: +

+ + + + + + + + + java.util.GregorianCalendar + + + com.example.CalendarConverter + + + .*\.DATE_OF_.* + + + .* + + +]]> + +

Advanced configuration

+ +

+ The advanced configuration allows for specifying <customTypes/>, which can be used to reuse configuration elements for user type / converter / binding combinations by giving them a name: +

+ + + + + + + GregorianCalendar + + + java.util.GregorianCalendar + + + com.example.CalendarConverter + + + + + + + + GregorianCalendar + + + .*\.DATE_OF_.* + + + .* + + +]]> + +

+ See also the section about to learn about an alternative use of <forcedTypes/>. +

+

+ The above configuration will lead to AUTHOR.DATE_OF_BIRTH being generated like this: +

+ + { + + // [...] + public final TableField DATE_OF_BIRTH = // [...] + // [...] + +}]]> + +

+ This means that the bound type of <T> will be GregorianCalendar, wherever you reference DATE_OF_BIRTH. jOOQ will use your custom converter when binding variables and when fetching data from : +

+ + result = +create.selectFrom(AUTHOR) + .where(AUTHOR.DATE_OF_BIRTH.greaterThan(new GregorianCalendar(1980, 0, 1))) + .fetch(AUTHOR.DATE_OF_BIRTH);]]> + +
+
+ + +
+ Custom data type binding + +

+ The previous section discussed the case where your custom data type is mapped onto a standard JDBC type as contained in . In some cases, however, you want to map your own type onto a type that is not explicitly supported by JDBC, such as for instance, PostgreSQL's various advanced data types like JSON or HSTORE, or PostGIS types. For this, you can register an for relevant columns in your code generator. Consider the following trivial implementation of a binding for PostgreSQL's JSON data type, which binds the JSON string in PostgreSQL to a Google GSON object: +

+ + = Object (unknown JDBC type), and = JsonElement (user type) +public class PostgresJSONGsonBinding implements Binding { + + // The converter does all the work + @Override + public Converter converter() { + return new Converter() { + @Override + public JsonElement from(Object t) { + return t == null ? JsonNull.INSTANCE : new Gson().fromJson("" + t, JsonElement.class); + } + + @Override + public Object to(JsonElement u) { + return u == null || u == JsonNull.INSTANCE ? null : new Gson().toJson(u); + } + + @Override + public Class fromType() { + return Object.class; + } + + @Override + public Class toType() { + return JsonElement.class; + } + }; + } + + // Rending a bind variable for the binding context's value and casting it to the json type + @Override + public void sql(BindingSQLContext ctx) throws SQLException { + ctx.render().visit(DSL.val(ctx.convert(converter()).value())).sql("::json"); + } + + // Registering VARCHAR types for JDBC CallableStatement OUT parameters + @Override + public void register(BindingRegisterContext ctx) throws SQLException { + ctx.statement().registerOutParameter(ctx.index(), Types.VARCHAR); + } + + // Converting the JsonElement to a String value and setting that on a JDBC PreparedStatement + @Override + public void set(BindingSetStatementContext ctx) throws SQLException { + ctx.statement().setString(ctx.index(), Objects.toString(ctx.convert(converter()).value(), null)); + } + + // Getting a String value from a JDBC ResultSet and converting that to a JsonElement + @Override + public void get(BindingGetResultSetContext ctx) throws SQLException { + ctx.convert(converter()).value(ctx.resultSet().getString(ctx.index())); + } + + // Getting a String value from a JDBC CallableStatement and converting that to a JsonElement + @Override + public void get(BindingGetStatementContext ctx) throws SQLException { + ctx.convert(converter()).value(ctx.statement().getString(ctx.index())); + } + + // Setting a value on a JDBC SQLOutput (useful for Oracle OBJECT types) + @Override + public void set(BindingSetSQLOutputContext ctx) throws SQLException { + throw new SQLFeatureNotSupportedException(); + } + + // Getting a value from a JDBC SQLInput (useful for Oracle OBJECT types) + @Override + public void get(BindingGetSQLInputContext ctx) throws SQLException { + throw new SQLFeatureNotSupportedException(); + } +}]]> + +

Registering bindings to the code generator

+ +

+ The above implementation intercepts all the interaction on a JDBC level, such that jOOQ will never need to know how to crrectly serialise / deserialise your custom data type. Similar to what we've seen in the previous section about , we can now register such a binding to the code generator. Note that you will reuse the same types of XML elements (<customType/> and <forcedType/>): +

+ + + + + + + + com.google.gson.JsonElement + + + com.example.PostgresJSONGsonBinding + + + .*JSON.* + + + .* + + +]]> + +

+ See also the section about to learn about an alternative use of <forcedTypes/>. +

+

+ The above configuration will lead to AUTHOR.CUSTOM_DATA_JSON being generated like this: +

+ + { + + // [...] + public final TableField CUSTOM_DATA_JSON = // [...] + // [...] + +}]]> + + + + + + +
+ Mapping generated schemata and tables + +

+ We've seen previously in the chapter about , that schemata and tables can be mapped at runtime to other names. But you can also hard-wire schema mapping in generated artefacts at code generation time, e.g. when you have 5 developers with their own dedicated developer databases, and a common integration database. In the code generation configuration, you would then write. +

+ + + + + LUKAS_DEV_SCHEMA + + + PROD + +]]> + +
+
+ +
+ Code generation for large schemas + +

+ Databases can become very large in real-world applications. This is not a problem for jOOQ's code generator, but it can be for the Java compiler. jOOQ generates some classes for . These classes can hit two sorts of limits of the compiler / JVM: +

+ +
    +
  • Methods (including static / instance initialisers) are allowed to contain only 64kb of bytecode.
  • +
  • Classes are allowed to contain at most 64k of constant literals
  • +
+ +

+ While there exist workarounds for the above two limitations (delegating initialisations to nested classes, inheriting constant literals from implemented interfaces), the preferred approach is either one of these: +

+ +
    +
  • Distribute your database objects in several schemas. That is probably a good idea anyway for such large databases
  • +
  • to exclude excess database objects
  • +
  • to avoid generating using <globalObjectReferences/>
  • +
  • Remove uncompilable classes after code generation
  • +
+
+
+ +
+ Code generation and version control + +

+ When using jOOQ's code generation capabilities, you will need to make a strategic decision about whether you consider your generated code as +

+
    +
  • Part of your code base
  • +
  • Derived artefacts
  • +
+

+ In this section we'll see that both approaches have their merits and that none of them is clearly better. +

+ +

Part of your code base

+

+ When you consider generated code as part of your code base, you will want to: +

+
    +
  • Check in generated sources in your version control system
  • +
  • Use manual source code generation
  • +
  • Possibly use even partial source code generation
  • +
+

+ This approach is particularly useful when your Java developers are not in full control of or do not have full access to your database schema, or if you have many developers that work simultaneously on the same database schema, which changes all the time. It is also useful to be able to track side-effects of database changes, as your checked-in database schema can be considered when you want to analyse the history of your schema. +

+

+ With this approach, you can also keep track of the change of behaviour in the jOOQ code generator, e.g. when upgrading jOOQ, or when modifying the code generation configuration. +

+

+ The drawback of this approach is that it is more error-prone as the actual schema may go out of sync with the generated schema. +

+ +

Derived artefacts

+

+ When you consider generated code to be derived artefacts, you will want to: +

+
    +
  • Check in only the actual DDL (e.g. controlled via )
  • +
  • Regenerate jOOQ code every time the schema changes
  • +
  • Regenerate jOOQ code on every machine - including continuous integration
  • +
+

+ This approach is particularly useful when you have a smaller database schema that is under full control by your Java developers, who want to profit from the increased quality of being able to regenerate all derived artefacts in every step of your build. +

+

+ The drawback of this approach is that the build may break in perfectly acceptable situations, when parts of your database are temporarily unavailable. +

+ +

Pragmatic combination

+

+ In some situations, you may want to choose a pragmatic combination, where you put only some parts of the generated code under version control. For instance, jOOQ-meta's generated sources are put under version control as few contributors will be able to run the jOOQ-meta code generator against all supported databases. +

+
+
+ +
+ Generating code from JPA annotated entities + +

+ Many jOOQ users use jOOQ as a complementary SQL API in applications that mostly use JPA for their database interactions, e.g. to perform reporting, batch processing, analytics, etc. +

+

+ In such a setup, you might have a pre-existing schema implemented using JPA-annotated entities. Your real database schema might not be accessible while developing, or it is not a first-class citizen in your application (i.e. you follow a Java-first approach). This section explains how you can generate jOOQ classes from such a JPA model. Consider this model: +

+ + books; + + // Getters and setters... +} + +@Entity +@Table(name = "book") +public class Book { + + @Id + public int id; + + @Column(name = "title") + public String title; + + @ManyToOne + public Author author; + + // Getters and setters... +}]]> +

+ Now, instead of connecting the jOOQ code generator to a database that holds a representation of the above schema, you can use jOOQ's JPADatabase and feed that to the code generator. The JPADatabase uses Hibernate internally, to generate an in-memory H2 database from your entities, and reverse-engineers that again back to jOOQ classes. +

+

+ The easiest way forward is to use Maven in order to include the jooq-meta-extensions library (which then includes the H2 and Hibernate dependencies) +

+ + + + org.jooq + jooq-meta-extensions + {jooq-version} +]]> + +

+ With that dependency in place, you can now specify the JPADatabase in your code generator configuration: +

+ + + + org.jooq.util.jpa.JPADatabase + + + + packages + com.example.entities + + + +]]> + +

+ The above will generate all jOOQ artefacts for your AUTHOR and BOOK tables. +

+
+
+ +
+ Generating code from XML files + +

+ By default, jOOQ's code generator takes live database connections as a database meta data source. In many project setups, this might not be optimal, as the live database is not always available. +

+

+ One way to circumvent this issue is by providing jOOQ with a database meta definition file in XML format and by passing this XML file to jOOQ's XMLDatabase. +

+

+ The XMLDatabase can read a standardised XML file that implements the http://www.jooq.org/xsd/jooq-meta-{meta-xsd-version}.xsd schema. Essentially, this schema is an XML representation of the SQL standard INFORMATION_SCHEMA, as implemented by databases like H2, HSQLDB, MySQL, PostgreSQL, or SQL Server. +

+

+ An example schema definition containing simple schema, table, column definitions can be seen below: +

+ + + + + + TEST + + + + + + TEST + AUTHOR +
+ + TEST + BOOK +
+
+ + + + PUBLIC + AUTHOR + ID + NUMBER + 7 + 1 + false + + ... + +
]]>
+ +

+ Constraints can be defined with the following elements: +

+ + + + TEST + PK_AUTHOR + PRIMARY KEY + TEST + AUTHOR + + ... + + + + + TEST + PK_AUTHOR + TEST + AUTHOR + ID + 1 + + ... + + + + + TEST + FK_BOOK_AUTHOR_ID + TEST + PK_AUTHOR + + ... + +]]> + +

+ The above file can be made available to the code generator configuration by using the XMLDatabase as follows: +

+ + + + org.jooq.util.xml.XMLDatabase + + + + + dialect + ORACLE + + + + + xml-file + src/main/resources/database.xml + + + +]]> + +

+ If you already have a different XML format for your database, you can either XSL transform your own format into the one above via an additional Maven plugin, or pass the location of an XSL file to the XMLDatabase by providing an additional property: +

+ + + + org.jooq.util.xml.XMLDatabase + + + ... + + + + xsl-file + src/main/resources/transform-to-jooq-format.xsl + + + +]]> + +

+ This XML configuration can now be checked in and versioned, and modified independently from your live database schema. +

+
+
+ +
+ Running the code generator with Ant + +

Run generation with Ant

+

+ When running code generation with ant's <java/> task, you may have to set fork="true": +

+ + + + + + + + + + + + +]]> + +

Using the Ant Maven plugin

+

+ Sometimes, ant can be useful to work around a limitation (misunderstanding?) of the Maven build. Just as with the above standalone ant usage example, the jOOQ code generator can be called from the maven-antrun-plugin: +

+ + + + maven-antrun-plugin + 1.8 + + + generate-sources + + + + + + + + + run + + + + + + + + + + + org.jooq + jooq-codegen + {jooq-version} + + +]]> +
+
+ +
+ Running the code generator with Gradle + +

Run generation with Gradle

+

+ We recommend using the Gradle plugin by Etienne Studer from Gradle Inc.). It provides a concise DSL that allows you to tune all configuration properties supported by each jOOQ version. +

+
+
+ + + +
+ Tools + +

+ These chapters hold some information about tools to be used with jOOQ +

+
+ + +
+ JDBC mocking for unit testing + +

+ When writing unit tests for your data access layer, you have probably used some generic mocking tool offered by popular providers like Mockito, jmock, mockrunner, or even DBUnit. With jOOQ, you can take advantage of the built-in JDBC mock API that allows you to emulate a simple database on the JDBC level for precisely those SQL/JDBC use cases supported by jOOQ. +

+ +

+ Disclaimer: The general idea of mocking a JDBC connection with this jOOQ API is to provide quick workarounds, injection points, etc. using a very simple JDBC abstraction. It is NOT RECOMMENDED to emulate an entire database (including complex state transitions, transactions, locking, etc.) using this mock API. Once you have this requirement, please consider using an actual database instead for integration testing, rather than implementing your test database inside of a MockDataProvider. +

+ +

Mocking the JDBC API

+

+ JDBC is a very complex API. It takes a lot of time to write a useful and correct mock implementation, implementing at least these interfaces: +

+
    +
  • +
  • +
  • +
  • +
  • +
  • +
+ +

+ Optionally, you may even want to implement interfaces, such as , , , and many others. In addition to the above, you might need to find a way to simultaneously support incompatible JDBC minor versions, such as 4.0, 4.1 +

+ +

Using jOOQ's own mock API

+

+ This work is greatly simplified, when using jOOQ's own mock API. The org.jooq.tools.jdbc package contains all the essential implementations for both JDBC 4.0 and 4.1, which are needed to mock JDBC for jOOQ. In order to write mock tests, provide the jOOQ with a , and implement the : +

+ + result = create.selectFrom(BOOK).where(BOOK.ID.equal(5)).fetch();]]> + +

+ As you can see, the configuration setup is simple. Now, the MockDataProvider acts as your single point of contact with JDBC / jOOQ. It unifies any of these execution modes, transparently: +

+ +
    +
  • Statements without results
  • +
  • Statements without results but with generated keys
  • +
  • Statements with results
  • +
  • Statements with several results
  • +
  • Batch statements with single queries and multiple bind value sets
  • +
  • Batch statements with multiple queries and no bind values
  • +
+ +

+ The above are the execution modes supported by jOOQ. Whether you're using any of jOOQ's various fetching modes (e.g. , , , ) is irrelevant, as those modes are all built on top of the standard JDBC API. +

+ +

Implementing MockDataProvider

+

+ Now, here's how to implement MockDataProvider: +

+ + result = create.newResult(AUTHOR); + result.add(create.newRecord(AUTHOR)); + result.get(0).setValue(AUTHOR.ID, 1); + result.get(0).setValue(AUTHOR.LAST_NAME, "Orwell"); + mock[0] = new MockResult(1, result); + } + + // You can detect batch statements easily + else if (ctx.batch()) { + // [...] + } + + return mock; + } +}]]> + +

+ Essentially, the contains all the necessary information for you to decide, what kind of data you should return. The wraps up two pieces of information: +

+
    +
  • : The number of affected rows
  • +
  • : The result set
  • +
+ +

+ You should return as many MockResult objects as there were query executions (in ) or results (in ). Instead of an awkward JDBC ResultSet, however, you can construct a "friendlier" with your own record types. The jOOQ mock API will use meta data provided with this Result in order to create the necessary JDBC

+ +

+ See the for a list of rules that you should follow. +

+
+
+ +
+ SQL 2 jOOQ Parser + +

+ Together with Gudu Software, we have created an Open Source SQL 2 jOOQ parser that takes native SQL statements as input and generates jOOQ code as output. +

+

+ Gudu Software Ltd has been selling enterprise quality SQL software to hundreds of customers to help them migrate from one database to another using the General SQL Parser. Now you can take advantage of their knowledge to parse your SQL statements and transform them directly into jOOQ statements using a free trial version of SQL 2 jOOQ! +

+ +

It's as simple as this!

+ +
    +
  • Create a JDBC connection
  • +
  • Create a new SQL2jOOQ converter object
  • +
  • Convert your SQL code
  • +
  • Get the result
  • +
+ +

+ See it in action: +

+ + + +

+ If all goes well, the above program yields: +

+ +> result = create.select( Actor.ACTOR.FIRST_NAME, Actor.ACTOR.LAST_NAME ) + .from( Actor.ACTOR ) + .where( Actor.ACTOR.ACTOR_ID.equal( DSL.inline( UShort.valueOf( 1 ) ) ) ).fetch( );]]> + +

+ SQL 2 jOOQ is a joint venture by Gudu Software Limited and Data Geekery GmbH. We will ship, test and maintain this awesome new addition with our own deliverables. So far, SQL 2 jOOQ supports the MySQL and PostgreSQL dialects and it is in an alpha stadium. Please, community, provide as much feedback as possible to make this great tool rock even more! +

+ +

+ Please take note of the fact that the sql2jooq library is Open Source, but it depends on the commercial gsp.jar parser, whose trial licensing terms can be seen here: +

+ +

+ https://github.com/sqlparser/sql2jooq/blob/master/sql2jooq/LICENSE-GSQLPARSER.txt +

+ +

+ For more information about the General SQL Parser, please refer to the product blog. +

+ +

+ Please report any issues, ideas, wishes to the jOOQ user group or the sql2jooq GitHub project. +

+
+
+ +
+ jOOQ Console + +

+ The jOOQ Console is no longer supported or shipped with jOOQ 3.2+. You may still use the jOOQ 3.1 Console with new versions of jOOQ, at your own risk. +

+
+
+
+
+ +
+ Reference + +

+ These chapters hold some general jOOQ reference information +

+
+ + +
+ Supported RDBMS + +

A list of supported databases

+

+ Every RDMBS out there has its own little specialties. jOOQ considers those specialties as much as possible, while trying to standardise the behaviour in jOOQ. In order to increase the quality of jOOQ, some 70 unit tests are run for syntax and variable binding verification, as well as some 400 integration tests with an overall of around 4000 queries for any of these databases: +

+
    +
  • CUBRID 8.4
  • +
  • DB2 9.7
  • +
  • Derby 10.10
  • +
  • Firebird 2.5
  • +
  • H2 1.3
  • +
  • HANA
  • +
  • HSQLDB 2.2
  • +
  • Informix 12.10
  • +
  • Ingres 10.1
  • +
  • MariaDB 5.2
  • +
  • Microsoft Access 2013
  • +
  • MySQL 5.5
  • +
  • Oracle 10g
  • +
  • PostgreSQL 9.0
  • +
  • SQLite with Xerial JDBC driver
  • +
  • SQL Azure
  • +
  • SQL Server 2008 R8
  • +
  • Sybase Adaptive Server Enterprise 15.5
  • +
  • Sybase SQL Anywhere 12
  • +
+ +

+ For an up-to-date list of currently supported RDBMS, please refer to http://www.jooq.org/legal/licensing/#databases. +

+
+
+ +
+ Data types + +

+ There is always a small mismatch between SQL data types and Java data types. This is for two reasons: +

+
    +
  • SQL data types are insufficiently covered by the JDBC API.
  • +
  • Java data types are often less expressive than SQL data types
  • +
+

+ This chapter should document the most important notes about SQL, JDBC and jOOQ data types. +

+
+ + +
+ BLOBs and CLOBs + +

+ jOOQ currently doesn't explicitly support JDBC BLOB and CLOB data types. If you use any of these data types in your database, jOOQ will map them to byte[] and String instead. In simple cases (small data), this simplification is sufficient. In more sophisticated cases, you may have to bypass jOOQ, in order to deal with these data types and their respective resources. True support for LOBs is on the roadmap, though. +

+
+
+ +
+ Unsigned integer types + +

+ Some databases explicitly support unsigned integer data types. In most normal JDBC-based applications, they would just be mapped to their signed counterparts letting bit-wise shifting and tweaking to the user. jOOQ ships with a set of unsigned implementations modelling the following types: +

+
    +
  • : Unsigned byte, an 8-bit unsigned integer
  • +
  • : Unsigned short, a 16-bit unsigned integer
  • +
  • : Unsigned int, a 32-bit unsigned integer
  • +
  • : Unsigned long, a 64-bit unsigned integer
  • +
+

+ Each of these wrapper types extends , wrapping a higher-level integer type, internally: +

+
    +
  • UByte wraps
  • +
  • UShort wraps
  • +
  • UInteger wraps
  • +
  • ULong wraps
  • +
+
+
+ +
+ INTERVAL data types + +

+ jOOQ fills a gap opened by JDBC, which neglects an important SQL data type as defined by the SQL standards: INTERVAL types. SQL knows two different types of intervals: +

+
    +
  • YEAR TO MONTH: This interval type models a number of months and years
  • +
  • DAY TO SECOND: This interval type models a number of days, hours, minutes, seconds and milliseconds
  • +
+ +

+ Both interval types ship with a variant of subtypes, such as DAY TO HOUR, HOUR TO SECOND, etc. jOOQ models these types as Java objects extending : (where Number.intValue() corresponds to the absolute number of months) and (where Number.intValue() corresponds to the absolute number of milliseconds) +

+ +

Interval arithmetic

+

+ In addition to the documented previously, interval arithmetic is also supported by jOOQ. Essentially, the following operations are supported: +

+
    +
  • DATETIME - DATETIME => INTERVAL
  • +
  • DATETIME + or - INTERVAL => DATETIME
  • +
  • INTERVAL + DATETIME => DATETIME
  • +
  • INTERVAL + - INTERVAL => INTERVAL
  • +
  • INTERVAL * or / NUMERIC => INTERVAL
  • +
  • NUMERIC * INTERVAL => INTERVAL
  • +
+
+
+ +
+ XML data types + +

+ XML data types are currently not supported +

+
+
+ +
+ Geospacial data types + +

Geospacial data types

+

+ Geospacial data types are currently not supported +

+
+
+ +
+ CURSOR data types + +

+ Some databases support cursors returned from stored procedures. They are mapped to the following jOOQ data type: +

+ +> cursor;]]> + +

+ In fact, such a cursor will be fetched immediately by jOOQ and wrapped in an object. +

+
+
+ +
+ ARRAY and TABLE data types + +

+ The SQL standard specifies ARRAY data types, that can be mapped to Java arrays as such: +

+ + intArray;]]> + +

+ The above array type is supported by these SQL dialects: +

+
    +
  • H2
  • +
  • HSQLDB
  • +
  • Postgres
  • +
+ +

Oracle typed arrays

+

+ Oracle has strongly-typed arrays and table types (as opposed to the previously seen anonymously typed arrays). These arrays are wrapped by types. +

+
+
+ +
+ Oracle DATE data type + +

+ Oracle's DATE data type does not conform to the SQL standard. It is really a TIMESTAMP(0), i.e. a TIMESTAMP with a fractional second precision of zero. The most appropriate JDBC type for Oracle DATE types is . +

+ +

Performance implications

+ +

+ When binding TIMESTAMP variables to SQL statements, instead of truncating such variables to DATE, the cost based optimiser may choose to widen the database column from DATE to TIMESTAMP using an Oracle INTERNAL_FUNCTION(), which prevents index usage. Details about this behaviour can be seen in this Stack Overflow question. +

+ +

Use a data type binding to work around this issue

+ +

+ The best way to work around this issue is to implement a , which generates the CAST expression for every bind variable: +

+ + ctx) throws SQLException { + render.keyword("cast").sql('(') + .visit(val(ctx.value())) + .sql(' ').keyword("as date").sql(')'); +}]]> + +

Deprecated functionality

+ +

+ Historic versions of jOOQ used to support a <dateAsTimestamp/> flag, which can be used with the out-of-the-box as a : +

+ + + + true + + + + + java.sql.Timestamp + org.jooq.impl.DateAsTimestampBinding + DATE + + +]]> + +

+ For more information, please refer to . +

+
+
+
+
+ +
+ SQL to DSL mapping rules + +

+ jOOQ takes SQL as an external domain-specific language and maps it onto Java, creating an internal domain-specific language. Internal DSLs cannot 100% implement their external language counter parts, as they have to adhere to the syntax rules of their host or target language (i.e. Java). This section explains the various problems and workarounds encountered and implemented in jOOQ. +

+ +

SQL allows for "keywordless" syntax

+

+ SQL syntax does not always need keywords to form expressions. The clause takes various argument assignments: +

+ + +UPDATE t SET a = 1, b = 2 +update(t).set(a, 1).set(b, 2) + + +

+ The above example also shows missing operator overloading capabilities, where "=" is replaced by "," in jOOQ. Another example are , which can be formed with parentheses only in SQL: +

+ + +(a, b) IN ((1, 2), (3, 4)) +row(a, b).in(row(1, 2), row(3, 4)) + + +

+ In this case, ROW is an actual (optional) SQL keyword, implemented by at least PostgreSQL. +

+ +

SQL contains "composed" keywords

+

+ As most languages, SQL does not attribute any meaning to whitespace. However, whitespace is important when forming "composed" keywords, i.e. SQL clauses composed of several keywords. jOOQ follows standard Java method naming conventions to map SQL keywords (case-insensitive) to Java methods (case-sensitive, camel-cased). Some examples: +

+ + +GROUP BY +ORDER BY +WHEN MATCHED THEN UPDATE +groupBy() +orderBy() +whenMatchedThenUpdate() + + +

+ Future versions of jOOQ may use all-uppercased method names in addition to the camel-cased ones (to prevent collisions with Java keywords): +

+ + +GROUP BY +ORDER BY +WHEN MATCHED THEN UPDATE +GROUP_BY() +ORDER_BY() +WHEN_MATCHED_THEN_UPDATE() + + +

SQL contains "superfluous" keywords

+

+ Some SQL keywords aren't really necessary. They are just part of a keyword-rich language, the way Java developers aren't used to anymore. These keywords date from times when languages such as ADA, BASIC, COBOL, FORTRAN, PASCAL were more verbose: +

+
    +
  • BEGIN .. END
  • +
  • REPEAT .. UNTIL
  • +
  • IF .. THEN .. ELSE .. END IF
  • +
+

+ jOOQ omits some of those keywords when it is too tedious to write them in Java. +

+ + +CASE WHEN .. THEN .. END +decode().when(.., ..) + + +

+ The above example omits THEN and END keywords in Java. Future versions of jOOQ may comprise a more complete DSL, including such keywords again though, to provide a more 1:1 match for the SQL language. +

+ +

SQL contains "superfluous" syntactic elements

+

+ Some SQL constructs are hard to map to Java, but they are also not really necessary. SQL often expects syntactic parentheses where they wouldn't really be needed, or where they feel slightly inconsistent with the rest of the SQL language. +

+ + +LISTAGG(a, b) WITHIN GROUP (ORDER BY c) + OVER (PARTITION BY d) +listagg(a, b).withinGroupOrderBy(c) + .over().partitionBy(d) + + +

+ The parentheses used for the WITHIN GROUP (..) and OVER (..) clauses are required in SQL but do not seem to add any immediate value. In some cases, jOOQ omits them, although the above might be optionally re-phrased in the future to form a more SQLesque experience: +

+ + +LISTAGG(a, b) WITHIN GROUP (ORDER BY c) + OVER (PARTITION BY d) +listagg(a, b).withinGroup(orderBy(c)) + .over(partitionBy(d)) + + +

SQL uses some of Java's reserved words

+

+ Some SQL keywords map onto Java Language Keywords if they're mapped using camel-casing. These keywords currently include: +

+
    +
  • CASE
  • +
  • ELSE
  • +
  • FOR
  • +
+ +

+ jOOQ replaces those keywords by "synonyms": +

+ + +CASE .. ELSE +PIVOT .. FOR .. IN .. +decode() .. otherwise() +pivot(..).on(..).in(..) + + +

+ There is more future collision potential with: +

+
    +
  • BOOLEAN
  • +
  • CHAR
  • +
  • DEFAULT
  • +
  • DOUBLE
  • +
  • ENUM
  • +
  • FLOAT
  • +
  • IF
  • +
  • INT
  • +
  • LONG
  • +
  • PACKAGE
  • +
+ +

SQL operators cannot be overloaded in Java

+

+ Most SQL operators have to be mapped to descriptive method names in Java, as Java does not allow operator overloading: +

+ + +, != +|| +SET a = b]]> +equal(), eq() +notEqual(), ne() +concat() +set(a, b) + + +

+ For those users using , operator overloading and implicit conversion can be leveraged to enhance jOOQ: +

+ + +, != +||]]> +, !== +||]]> + + +

SQL's reference before declaration capability

+

+ This is less of a syntactic SQL feature than a semantic one. In SQL, objects can be referenced before (i.e. "lexicographically before") they are declared. This is particularly true for +

+ + + + + + +

+ A more sophisticated example are common table expressions (CTE), which are currently not supported by jOOQ: +

+ +WITH t(a, b) AS ( + SELECT 1, 2 FROM DUAL +) +SELECT t.a, t.b +FROM t + +

+ Common table expressions define a "derived column list", just like can do. The formal record type thus created cannot be typesafely verified by the Java compiler, i.e. it is not possible to formally dereference t.a from t. +

+
+
+ +
+ jOOQ's BNF pseudo-notation + +

+ This chapter will soon contain an overview over jOOQ's API using a pseudo BNF notation. +

+
+
+ +
+ Quality Assurance + +

+ jOOQ is running some of your most mission-critical logic: the interface layer between your Java / Scala application and the database. You have probably chosen jOOQ for any of the following reasons: +

+
    +
  • To evade JDBC's verbosity and error-proneness due to string concatenation and index-based variable binding
  • +
  • To add lots of type-safety to your inline SQL
  • +
  • To increase productivity when writing inline SQL using your favourite IDE's autocompletion capabilities
  • +
+ +

+ With jOOQ being in the core of your application, you want to be sure that you can trust jOOQ. That is why jOOQ is heavily unit and integration tested with a strong focus on integration tests: +

+ +

Unit tests

+

+ Unit tests are performed against dummy JDBC interfaces using http://jmock.org/. These tests verify that various implementations render correct SQL and bind variables correctly. +

+ +

Integration tests

+

+ This is the most important part of the jOOQ test suites. Some 1500 queries are currently run against a standard integration test database. Both the test database and the queries are translated into every one of the 14 supported SQL dialects to ensure that regressions are unlikely to be introduced into the code base. +

+

+ For libraries like jOOQ, integration tests are much more expressive than unit tests, as there are so many subtle differences in SQL dialects. Simple mocks just don't give as much feedback as an actual database instance. +

+

+ jOOQ integration tests run the weirdest and most unrealistic queries. As a side-effect of these extensive integration test suites, many corner-case bugs for JDBC drivers and/or open source databases have been discovered, feature requests submitted through jOOQ and reported mainly to CUBRID, Derby, H2, HSQLDB. +

+ +

Code generation tests

+

+ For every one of the 14 supported integration test databases, source code is generated and the tiniest differences in generated source code can be discovered. In case of compilation errors in generated source code, new test tables/views/columns are added to avoid regressions in this field. +

+ +

API Usability tests and proofs of concept

+

+ jOOQ is used in jOOQ-meta as a proof of concept. This includes complex queries such as the following Postgres query +

+ + +

+ These rather complex queries show that the jOOQ API is fit for advanced SQL use-cases, compared to the rather simple, often unrealistic queries in the integration test suite. +

+ +

Clean API and implementation. Code is kept DRY

+

+ As a general rule of thumb throughout the jOOQ code, everything is kept DRY. Some examples: +

+
    +
  • There is only one place in the entire code base, which consumes values from a JDBC ResultSet
  • +
  • There is only one place in the entire code base, which transforms jOOQ Records into custom POJOs
  • +
+

+ Keeping things DRY leads to longer stack traces, but in turn, also increases the relevance of highly reusable code-blocks. Chances that some parts of the jOOQ code base slips by integration test coverage decrease significantly. +

+
+
+ +
+ Migrating to jOOQ 3.0 + +

+ This section is for all users of jOOQ 2.x who wish to upgrade to the next major release. In the next sub-sections, the most important changes are explained. Some code hints are also added to help you fix compilation errors. +

+ +

Type-safe row value expressions

+

+ Support for has been added in jOOQ 2.6. In jOOQ 3.0, many API parts were thoroughly (but often incompatibly) changed, in order to provide you with even more type-safety. +

+ +

+ Here are some affected API parts: +

+
    +
  • [N] in Row[N] has been raised from 8 to 22. This means that existing row value expressions with degree >= 9 are now type-safe
  • +
  • Subqueries returned from DSL.select(...) now implement Select<Record[N]>, not Select<Record>
  • +
  • IN predicates and comparison predicates taking subselects changed incompatibly
  • +
  • INSERT and MERGE statements now take typesafe VALUES() clauses
  • +
+ +

+ Some hints related to row value expressions: +

+ + record = create.select(BOOK.TITLE, BOOK.ID).from(BOOK).where(ID.eq(1)).fetchOne(); +Result> result = create.select(BOOK.TITLE, BOOK.ID).from(BOOK).fetch(); + +// But Record2 extends Record. You don't have to use the additional typesafety: +Record record = create.select(BOOK.TITLE, BOOK.ID).from(BOOK).where(ID.eq(1)).fetchOne(); +Result result = create.select(BOOK.TITLE, BOOK.ID).from(BOOK).fetch();]]> + +

SelectQuery and SelectXXXStep are now generic

+

+ In order to support type-safe row value expressions and type-safe Record[N] types, SelectQuery is now generic: SelectQuery<R> +

+ +

SimpleSelectQuery and SimpleSelectXXXStep API were removed

+

+ The duplication of the SELECT API is no longer useful, now that SelectQuery and SelectXXXStep are generic. +

+ +

Factory was split into DSL (query building) and DSLContext (query execution)

+

+ The pre-existing Factory class has been split into two parts: +

+ +
    +
  1. The DSL: This class contains only static factory methods. All QueryParts constructed from this class are "unattached", i.e. queries that are constructed through DSL cannot be executed immediately. This is useful for subqueries.
    The DSL class corresponds to the static part of the jOOQ 2.x Factory type
  2. +
  3. The DSLContext: This type holds a reference to a Configuration and can construct executable ("attached") QueryParts.
    The DSLContext type corresponds to the non-static part of the jOOQ 2.x Factory / FactoryOperations type.
  4. +
+ +

+ The FactoryOperations interface has been renamed to DSLContext. An example: +

+ + + +

Quantified comparison predicates

+

+ Field.equalAny(...) and similar methods have been removed in favour of Field.equal(any(...)). This greatly simplified the Field API. An example: +

+ +> subselect = any(select(BOOK.ID).from(BOOK)); +Condition condition = BOOK.ID.equal(subselect);]]> + +

FieldProvider

+

+ The FieldProvider marker interface was removed. Its methods still exist on FieldProvider subtypes. Note, they have changed names from getField() to field() and from getIndex() to indexOf() +

+ +

GroupField

+

+ GroupField has been introduced as a DSL marker interface to denote fields that can be passed to GROUP BY clauses. This includes all org.jooq.Field types. However, fields obtained from ROLLUP(), CUBE(), and GROUPING SETS() functions no longer implement Field. Instead, they only implement GroupField. An example: +

+ + field1a = Factory.rollup(...); // OK +Field field2a = Factory.one(); // OK + +// jOOQ 3.0 +GroupField field1b = DSL.rollup(...); // OK +Field field1c = DSL.rollup(...); // Compilation error +GroupField field2b = DSL.one(); // OK +Field field2c = DSL.one(); // OK]]> + +

NULL predicate

+

+ Beware! Previously, Field.equal(null) was translated internally to an IS NULL predicate. This is no longer the case. Binding Java "null" to a comparison predicate will result in a regular comparison predicate (which never returns true). This was changed for several reasons: +

+ +
    +
  • To most users, this was a surprising "feature".
  • +
  • Other predicates didn't behave in such a way, e.g. the IN predicate, the BETWEEN predicate, or the LIKE predicate.
  • +
  • Variable binding behaved unpredictably, as IS NULL predicates don't bind any variables.
  • +
  • The generated SQL depended on the possible combinations of bind values, which creates unnecessary hard-parses every time a new unique SQL statement is rendered.
  • +
+ +

+ Here is an example how to check if a field has a given value, without applying SQL's ternary NULL logic: +

+ + + +

Configuration

+

+ DSLContext, ExecuteContext, RenderContext, BindContext no longer extend Configuration for "convenience". From jOOQ 3.0 onwards, composition is chosen over inheritance as these objects are not really configurations. Most importantly +

+
    +
  • DSLContext is only a DSL entry point for constructing "attached" QueryParts
  • +
  • ExecuteContext has a well-defined lifecycle, tied to that of a single query execution
  • +
  • RenderContext has a well-defined lifecycle, tied to that of a single rendering operation
  • +
  • BindContext has a well-defined lifecycle, tied to that of a single variable binding operation
  • +
+

+ In order to resolve confusion that used to arise because of different lifecycle durations, these types are now no longer formally connected through inheritance. +

+ +

ConnectionProvider

+

+ In order to allow for simpler connection / data source management, jOOQ externalised connection handling in a new ConnectionProvider type. The previous two connection modes are maintained backwards-compatibly (JDBC standalone connection mode, pooled DataSource mode). Other connection modes can be injected using: +

+ + + +

+ These are some side-effects of the above change +

+ +
    +
  • Connection-related JDBC wrapper utility methods (commit, rollback, etc) have been moved to the new DefaultConnectionProvider. They're no longer available from the DSLContext. This had been confusing to some users who called upon these methods while operating in pool DataSource mode.
  • +
+ +

ExecuteListeners

+

+ ExecuteListeners can no longer be configured via Settings. Instead they have to be injected into the Configuration. This resolves many class loader issues that were encountered before. It also helps listener implementations control their lifecycles themselves. +

+ +

Data type API

+

+ The data type API has been changed drastically in order to enable some new DataType-related features. These changes include: +

+ +
    +
  • [SQLDialect]DataType and SQLDataType no longer implement DataType. They're mere constant containers
  • +
  • Various minor API changes have been done.
  • +
+ +

Object renames

+

+ These objects have been moved / renamed: +

+ +
    +
  • jOOU: a library used to represent unsigned integer types was moved from org.jooq.util.unsigned to org.jooq.util.types (which already contained INTERVAL data types)
  • +
+ +

Feature removals

+

+ Here are some minor features that have been removed in jOOQ 3.0 +

+ +
    +
  • The ant task for code generation was removed, as it was not up to date at all. Code generation through ant can be performed easily by calling jOOQ's GenerationTool through a <java> target.
  • +
  • The navigation methods and "foreign key setters" are no longer generated in Record classes, as they are useful only to few users and the generated code is very collision-prone.
  • +
  • The code generation configuration no longer accepts comma-separated regular expressions. Use the regex pipe | instead.
  • +
  • The code generation configuration can no longer be loaded from .properties files. Only XML configurations are supported.
  • +
  • The master data type feature is no longer supported. This feature was unlikely to behave exactly as users expected. It is better if users write their own code generators to generate master enum data types from their database tables. jOOQ's enum mapping and converter features sufficiently cover interacting with such user-defined types.
  • +
  • The DSL subtypes are no longer instanciable. As DSL now only contains static methods, subclassing is no longer useful. There are still dialect-specific DSL types providing static methods for dialect-specific functions. But the code-generator no longer generates a schema-specific DSL
  • +
  • The concept of a "main key" is no longer supported. The code generator produces UpdatableRecords only if the underlying table has a PRIMARY KEY. The reason for this removal is the fact that "main keys" are not reliable enough. They were chosen arbitrarily among UNIQUE KEYs.
  • +
  • The UpdatableTable type has been removed. While adding significant complexity to the type hierarchy, this type adds not much value over a simple Table.getPrimaryKey() != null check.
  • +
  • The USE statement support has been removed from jOOQ. Its behaviour was ill-defined, while it didn't work the same way (or didn't work at all) in some databases.
  • +
+
+
+ + + +
+ Credits + +

+ jOOQ lives in a very challenging ecosystem. The Java to SQL interface is still one of the most important system interfaces. Yet there are still a lot of open questions, best practices and no "true" standard has been established. This situation gave way to a lot of tools, APIs, utilities which essentially tackle the same problem domain as jOOQ. jOOQ has gotten great inspiration from pre-existing tools and this section should give them some credit. Here is a list of inspirational tools in alphabetical order: +

+
    +
  • Hibernate: The de-facto standard (JPA) with its useful table-to-POJO mapping features have influenced jOOQ's facilities
  • +
  • JaQu: H2's own fluent API for querying databases
  • +
  • JPA: The de-facto standard in the javax.persistence packages, supplied by Oracle. Its annotations are useful to jOOQ as well.
  • +
  • OneWebSQL: A commercial SQL abstraction API with support for DAO source code generation, which was integrated also in jOOQ
  • +
  • QueryDSL: A "LINQ-port" to Java. It has a similar fluent API, a similar code-generation facility, yet quite a different purpose. While jOOQ is all about SQL, QueryDSL (like LINQ) is mostly about querying.
  • +
  • SLICK: A "LINQ-like" database abstraction layer for Scala. Unlike LINQ, its API doesn't really remind of SQL. Instead, it makes SQL look like Scala.
  • +
  • Spring Data: Spring's JdbcTemplate knows RowMappers, which are reflected by jOOQ's or
  • +
+
+
+
+
+ + + diff --git a/jOOQ-scala/src/main/scala/org/jooq/scalaextensions/Conversions.scala b/jOOQ-scala/src/main/scala/org/jooq/scalaextensions/Conversions.scala new file mode 100644 index 0000000000..85c6ce4d12 --- /dev/null +++ b/jOOQ-scala/src/main/scala/org/jooq/scalaextensions/Conversions.scala @@ -0,0 +1,820 @@ +/** + * Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.scalaextensions + +import java.sql.ResultSet + +import org.jooq._ +import org.jooq.impl._ + +// Avoid ambiguity with the internal org.jooq.impl.Array type. +import scala.Array +import scala.collection.convert.WrapAsScala + +/** + * jOOQ type conversions used to enhance the jOOQ Java API with Scala Traits + *

+ * Import this object and all of its attributes to profit from an enhanced jOOQ + * API in Scala client code. Here is an example: + *

+ * import java.sql.DriverManager
+ * import org.jooq._
+ * import org.jooq.impl._
+ * import org.jooq.impl.DSL._
+ * import org.jooq.examples.scala.h2.Tables._
+ * import collection.JavaConversions._
+ * import org.jooq.scalaextensions.Conversions._
+ *
+ * object Test {
+ *   def main(args: Array[String]): Unit = {
+ *     val c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
+ *     val f = DSL.using(c, SQLDialect.H2);
+ *
+ *     for (
+ *       val r <- f
+ *         select (
+ *           T_BOOK.ID * T_BOOK.AUTHOR_ID,
+ *           T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4,
+ *           T_BOOK.TITLE || " abc" || " xy")
+ *           from T_BOOK
+ *           where (T_BOOK.ID === 3) fetch
+ *     ) {
+ *
+ *       println(r)
+ *     }
+ *   }
+ * }
+ * 
+ * + * @author Lukas Eder + * @author Eric Peters + */ +object Conversions { + + // ------------------------------------------------------------------------- + // Enhanced jOOQ types + // ------------------------------------------------------------------------- + + implicit class SQLInterpolation(val sc : StringContext) extends AnyVal { + + @PlainSQL + def sql(args: Any*) : SQL = DSL.sql(string(args), args.asInstanceOf[Seq[AnyRef]] : _*) + + @PlainSQL + def condition(args : Any*) : Condition = DSL.condition(string(args), args.asInstanceOf[Seq[AnyRef]] : _*) + + @PlainSQL + def table(args : Any*) : Table[Record] = DSL.table(string(args), args.asInstanceOf[Seq[AnyRef]] : _*) + + @PlainSQL + def query(args : Any*) : Query = DSL.query(string(args), args.asInstanceOf[Seq[AnyRef]] : _*) + + @PlainSQL + def resultQuery(args : Any*) : ResultQuery[Record] = DSL.resultQuery(string(args), args.asInstanceOf[Seq[AnyRef]] : _*) + + private def string(args : Any*) = { + val pi = sc.parts.iterator + val sb = new StringBuilder(pi.next()) + var i = 0; + + while (pi.hasNext) { + sb += '{' + sb ++= (i toString) + sb += '}' + sb ++= pi.next() + + i = i + 1; + } + + sb.result + } + } + + implicit class ScalaDSLContext (val ctx : DSLContext) { + def fetchAnyOption[R <: Record] (table : Table[R]) : Option[R] = Option(ctx.fetchAny(table)) + def fetchAnyOption[R <: Record] (table : Table[R], condition : Condition) : Option[R] = Option(ctx.fetchAny(table, condition)) + + def fetchOneOption[R <: Record] (query : ResultQuery[R]) : Option[R] = Option(ctx.fetchOne(query)) + def fetchOneOption (rs : ResultSet) : Option[Record] = Option(ctx.fetchOne(rs)) + + def fetchOneOption (rs : ResultSet, types : Class[_]*) : Option[Record] = Option(ctx.fetchOne(rs, types:_*)) + def fetchOneOption (rs : ResultSet, types : DataType[_]*) + (implicit d: DummyImplicit) : Option[Record] = Option(ctx.fetchOne(rs, types:_*)) + def fetchOneOption (rs : ResultSet, fields : Field[_]*) + (implicit d1: DummyImplicit, d2: DummyImplicit) : Option[Record] = Option(ctx.fetchOne(rs, fields:_*)) + def fetchOneOption (sql : String) : Option[Record] = Option(ctx.fetchOne(sql)) + def fetchOneOption (sql : String, bindings : AnyRef*) : Option[Record] = Option(ctx.fetchOne(sql, bindings:_*)) + def fetchOneOption (sql : String, parts : QueryPart*) + (implicit d: DummyImplicit) : Option[Record] = Option(ctx.fetchOne(sql, parts:_*)) + def fetchOneOption[R <: Record] (table : Table[R]) : Option[R] = Option(ctx.fetchOne(table)) + def fetchOneOption[R <: Record] (table : Table[R], condition : Condition) : Option[R] = Option(ctx.fetchOne(table, condition)) + + def fetchValueOption[T, R <: Record1[T]] (query : ResultQuery[R]) : Option[T] = Option(ctx.fetchValue[T, R](query)) + def fetchValueOption (rs : ResultSet) : Option[AnyRef] = Option(ctx.fetchValue(rs)) + def fetchValueOption[T] (rs : ResultSet, newType : Class[T]) : Option[T] = Option(ctx.fetchValue(rs, newType)) + def fetchValueOption[T] (rs : ResultSet, newType : DataType[T]) : Option[T] = Option(ctx.fetchValue(rs, newType)) + def fetchValueOption[T] (rs : ResultSet, field : Field[T]) : Option[T] = Option(ctx.fetchValue(rs, field)) + def fetchValueOption (sql : String) : Option[AnyRef] = Option(ctx.fetchValue(sql)) + def fetchValueOption (sql : String, bindings : AnyRef*) : Option[AnyRef] = Option(ctx.fetchValue(sql, bindings:_*)) + def fetchValueOption (sql : String, parts : QueryPart*) + (implicit d: DummyImplicit) : Option[AnyRef] = Option(ctx.fetchValue(sql, parts:_*)) + } + + implicit class ScalaResultQuery[R <: Record](val query : ResultQuery[R]) { + import _root_.scala.collection.mutable._ + + def fetchAnyOption () : Option[R] = Option(query.fetchAny) + def fetchAnyOption[E] (mapper : RecordMapper[_ >: R, E]) : Option[E] = Option(query.fetchAny(mapper)) + def fetchAnyOption[T] (field : Field[T]) : Option[T] = Option(query.fetchAny(field)) + def fetchAnyOption[T] (field : Field[_], newType : Class[_ <: T]) : Option[T] = Option(query.fetchAny(field, newType)) + def fetchAnyOption[T, U] (field : Field[T], converter : Converter[_ >: T, _ <: U]) : Option[U] = Option(query.fetchAny[T, U](field, converter)) + def fetchAnyOption (fieldIndex : Int) : Option[_] = Option(query.fetchAny(fieldIndex)) + def fetchAnyOption[T] (fieldIndex : Int, newType : Class[_ <: T]) : Option[T] = Option(query.fetchAny(fieldIndex, newType)) + def fetchAnyOption[T, U] (fieldIndex : Int, converter : Converter[_ >: T, _ <: U]) : Option[U] = Option(query.fetchAny(fieldIndex, converter)) + def fetchAnyOption (fieldName : String) : Option[_] = Option(query.fetchAny(fieldName)) + def fetchAnyOption[T] (fieldName : String, newType : Class[_ <: T]) : Option[T] = Option(query.fetchAny(fieldName, newType)) + def fetchAnyOption[T, U] (fieldName : String, converter : Converter[_ >: T, _ <: U]): Option[U] = Option(query.fetchAny(fieldName, converter)) + def fetchAnyOptionArray () : Option[Array[AnyRef]] = Option(query.fetchAnyArray) + def fetchAnyOptionInto[E] (newType : Class[_ <: E]) : Option[E] = Option(query.fetchAnyInto(newType)) + def fetchAnyOptionInto[Z <: Record](table : Table[Z]) : Option[Z] = Option(query.fetchAnyInto(table)) + def fetchAnyOptionMap () : Option[Map[String, AnyRef]] = Option(query.fetchAnyMap).map(WrapAsScala.mapAsScalaMap) + + def fetchOneOption () : Option[R] = Option(query.fetchOne) + def fetchOneOption[E] (mapper : RecordMapper[_ >: R, E]) : Option[E] = Option(query.fetchOne(mapper)) + def fetchOneOption[T] (field : Field[T]) : Option[T] = Option(query.fetchOne(field)) + def fetchOneOption[T] (field : Field[_], newType : Class[_ <: T]) : Option[T] = Option(query.fetchOne(field, newType)) + def fetchOneOption[T, U] (field : Field[T], converter : Converter[_ >: T, _ <: U]) : Option[U] = Option(query.fetchOne[T, U](field, converter)) + def fetchOneOption (fieldIndex : Int) : Option[_] = Option(query.fetchOne(fieldIndex)) + def fetchOneOption[T] (fieldIndex : Int, newType : Class[_ <: T]) : Option[T] = Option(query.fetchOne(fieldIndex, newType)) + def fetchOneOption[T, U] (fieldIndex : Int, converter : Converter[_ >: T, _ <: U]) : Option[U] = Option(query.fetchOne(fieldIndex, converter)) + def fetchOneOption (fieldName : String) : Option[_] = Option(query.fetchOne(fieldName)) + def fetchOneOption[T] (fieldName : String, newType : Class[_ <: T]) : Option[T] = Option(query.fetchOne(fieldName, newType)) + def fetchOneOption[T, U] (fieldName : String, converter : Converter[_ >: T, _ <: U]): Option[U] = Option(query.fetchOne(fieldName, converter)) + def fetchOneOptionArray () : Option[Array[AnyRef]] = Option(query.fetchOneArray) + def fetchOneOptionInto[E] (newType : Class[_ <: E]) : Option[E] = Option(query.fetchOneInto(newType)) + def fetchOneOptionInto[Z <: Record](table : Table[Z]) : Option[Z] = Option(query.fetchOneInto(table)) + def fetchOneOptionMap () : Option[Map[String, AnyRef]] = Option(query.fetchOneMap).map(WrapAsScala.mapAsScalaMap) + } + + // ------------------------------------------------------------------------- + // Traits + // ------------------------------------------------------------------------- + + /** + * A Scala-esque representation of {@link org.jooq.Field}, adding overloaded + * operators for common jOOQ operations to arbitrary fields + */ + implicit class ScalaField[T](val field : Field[T]) extends AnyVal { + + // String operations + // ----------------- + + def ||(value : String) : Field[String] = field.concat(value) + def ||(value : Field[_]) : Field[String] = field.concat(value) + + // Comparison predicates + // --------------------- + + def ===(value : T) : Condition = field.equal(value) + def ===(value : Field[T]) : Condition = field.equal(value) + def ===(value : Select[_ <: Record1[T]]) : Condition = field.equal(value) + def ===(value : QuantifiedSelect[_ <: Record1[T]]) : Condition = field.equal(value) + + def !==(value : T) : Condition = field.notEqual(value) + def !==(value : Field[T]) : Condition = field.notEqual(value) + def !==(value : Select[_ <: Record1[T]]) : Condition = field.notEqual(value) + def !==(value : QuantifiedSelect[_ <: Record1[T]]) : Condition = field.notEqual(value) + + def <>(value : T) : Condition = field.notEqual(value) + def <>(value : Field[T]) : Condition = field.notEqual(value) + def <>(value : Select[_ <: Record1[T]]) : Condition = field.notEqual(value) + def <>(value : QuantifiedSelect[_ <: Record1[T]]) : Condition = field.notEqual(value) + + def >(value : T) : Condition = field.greaterThan(value) + def >(value : Field[T]) : Condition = field.greaterThan(value) + def >(value : Select[_ <: Record1[T]]) : Condition = field.greaterThan(value) + def >(value : QuantifiedSelect[_ <: Record1[T]]) : Condition = field.greaterThan(value) + + def >=(value : T) : Condition = field.greaterOrEqual(value) + def >=(value : Field[T]) : Condition = field.greaterOrEqual(value) + def >=(value : Select[_ <: Record1[T]]) : Condition = field.greaterOrEqual(value) + def >=(value : QuantifiedSelect[_ <: Record1[T]]) : Condition = field.greaterOrEqual(value) + + def <(value : T) : Condition = field.lessThan(value) + def <(value : Field[T]) : Condition = field.lessThan(value) + def <(value : Select[_ <: Record1[T]]) : Condition = field.lessThan(value) + def <(value : QuantifiedSelect[_ <: Record1[T]]) : Condition = field.lessThan(value) + + def <=(value : T) : Condition = field.lessOrEqual(value) + def <=(value : Field[T]) : Condition = field.lessOrEqual(value) + def <=(value : Select[_ <: Record1[T]]) : Condition = field.lessOrEqual(value) + def <=(value : QuantifiedSelect[_ <: Record1[T]]) : Condition = field.lessOrEqual(value) + + def <=>(value : T) : Condition = field.isNotDistinctFrom(value) + def <=>(value : Field[T]) : Condition = field.isNotDistinctFrom(value) + + + + + } + + /** + * A Scala-esque representation of {@link org.jooq.Field}, adding overloaded + * operators for common jOOQ operations to numeric fields + */ + implicit class ScalaNumberField[T <: Number](val field : Field[T]) extends AnyVal { + + // ------------------------------------------------------------------------ + // Arithmetic operations + // ------------------------------------------------------------------------ + + def unary_- = field.neg() + + def +(value : Number) = field.add(value) + def +(value : Field[_ <: Number]) = field.add(value) + + def -(value : Number) = field.sub(value) + def -(value : Field[_ <: Number]) = field.sub(value) + + def *(value : Number) = field.mul(value) + def *(value : Field[_ <: Number]) = field.mul(value) + + def /(value : Number) = field.div(value) + def /(value : Field[_ <: Number]) = field.div(value) + + def %(value : Number) = field.mod(value) + def %(value : Field[_ <: Number]) = field.mod(value) + + // ------------------------------------------------------------------------- + // Bitwise operations + // ------------------------------------------------------------------------- + + def unary_~ = DSL.bitNot(field) + + def &(value : T) = DSL.bitAnd(field, value) + def &(value : Field[T]) = DSL.bitAnd(field, value) + + def |(value : T) = DSL.bitOr (field, value) + def |(value : Field[T]) = DSL.bitOr (field, value) + + def ^(value : T) = DSL.bitXor(field, value) + def ^(value : Field[T]) = DSL.bitXor(field, value) + + def <<(value : T) = DSL.shl(field, value) + def <<(value : Field[T]) = DSL.shl(field, value) + + def >>(value : T) = DSL.shr(field, value) + def >>(value : Field[T]) = DSL.shr(field, value) + } + + // -------------------------------------------------------------------------- + // Conversions from jOOQ Record[N] types to Scala Tuple[N] types + // -------------------------------------------------------------------------- + +// [jooq-tools] START [tuples] + + /** + * Enrich any {@link org.jooq.Record1} with the {@link Tuple1} case class + */ + implicit def asTuple1[T1](r : Record1[T1]): Tuple1[T1] = r match { + case null => null + case _ => Tuple1(r.value1) + } + + /** + * Enrich any {@link org.jooq.Record2} with the {@link Tuple2} case class + */ + implicit def asTuple2[T1, T2](r : Record2[T1, T2]): (T1, T2) = r match { + case null => null + case _ => (r.value1, r.value2) + } + + /** + * Enrich any {@link org.jooq.Record3} with the {@link Tuple3} case class + */ + implicit def asTuple3[T1, T2, T3](r : Record3[T1, T2, T3]): (T1, T2, T3) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3) + } + + /** + * Enrich any {@link org.jooq.Record4} with the {@link Tuple4} case class + */ + implicit def asTuple4[T1, T2, T3, T4](r : Record4[T1, T2, T3, T4]): (T1, T2, T3, T4) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4) + } + + /** + * Enrich any {@link org.jooq.Record5} with the {@link Tuple5} case class + */ + implicit def asTuple5[T1, T2, T3, T4, T5](r : Record5[T1, T2, T3, T4, T5]): (T1, T2, T3, T4, T5) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5) + } + + /** + * Enrich any {@link org.jooq.Record6} with the {@link Tuple6} case class + */ + implicit def asTuple6[T1, T2, T3, T4, T5, T6](r : Record6[T1, T2, T3, T4, T5, T6]): (T1, T2, T3, T4, T5, T6) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6) + } + + /** + * Enrich any {@link org.jooq.Record7} with the {@link Tuple7} case class + */ + implicit def asTuple7[T1, T2, T3, T4, T5, T6, T7](r : Record7[T1, T2, T3, T4, T5, T6, T7]): (T1, T2, T3, T4, T5, T6, T7) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7) + } + + /** + * Enrich any {@link org.jooq.Record8} with the {@link Tuple8} case class + */ + implicit def asTuple8[T1, T2, T3, T4, T5, T6, T7, T8](r : Record8[T1, T2, T3, T4, T5, T6, T7, T8]): (T1, T2, T3, T4, T5, T6, T7, T8) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8) + } + + /** + * Enrich any {@link org.jooq.Record9} with the {@link Tuple9} case class + */ + implicit def asTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9](r : Record9[T1, T2, T3, T4, T5, T6, T7, T8, T9]): (T1, T2, T3, T4, T5, T6, T7, T8, T9) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9) + } + + /** + * Enrich any {@link org.jooq.Record10} with the {@link Tuple10} case class + */ + implicit def asTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](r : Record10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10) + } + + /** + * Enrich any {@link org.jooq.Record11} with the {@link Tuple11} case class + */ + implicit def asTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](r : Record11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11) + } + + /** + * Enrich any {@link org.jooq.Record12} with the {@link Tuple12} case class + */ + implicit def asTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](r : Record12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12) + } + + /** + * Enrich any {@link org.jooq.Record13} with the {@link Tuple13} case class + */ + implicit def asTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](r : Record13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13) + } + + /** + * Enrich any {@link org.jooq.Record14} with the {@link Tuple14} case class + */ + implicit def asTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](r : Record14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14) + } + + /** + * Enrich any {@link org.jooq.Record15} with the {@link Tuple15} case class + */ + implicit def asTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](r : Record15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15) + } + + /** + * Enrich any {@link org.jooq.Record16} with the {@link Tuple16} case class + */ + implicit def asTuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](r : Record16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15, r.value16) + } + + /** + * Enrich any {@link org.jooq.Record17} with the {@link Tuple17} case class + */ + implicit def asTuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](r : Record17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15, r.value16, r.value17) + } + + /** + * Enrich any {@link org.jooq.Record18} with the {@link Tuple18} case class + */ + implicit def asTuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](r : Record18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15, r.value16, r.value17, r.value18) + } + + /** + * Enrich any {@link org.jooq.Record19} with the {@link Tuple19} case class + */ + implicit def asTuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](r : Record19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15, r.value16, r.value17, r.value18, r.value19) + } + + /** + * Enrich any {@link org.jooq.Record20} with the {@link Tuple20} case class + */ + implicit def asTuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](r : Record20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15, r.value16, r.value17, r.value18, r.value19, r.value20) + } + + /** + * Enrich any {@link org.jooq.Record21} with the {@link Tuple21} case class + */ + implicit def asTuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21](r : Record21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15, r.value16, r.value17, r.value18, r.value19, r.value20, r.value21) + } + + /** + * Enrich any {@link org.jooq.Record22} with the {@link Tuple22} case class + */ + implicit def asTuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22](r : Record22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]): (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22) = r match { + case null => null + case _ => (r.value1, r.value2, r.value3, r.value4, r.value5, r.value6, r.value7, r.value8, r.value9, r.value10, r.value11, r.value12, r.value13, r.value14, r.value15, r.value16, r.value17, r.value18, r.value19, r.value20, r.value21, r.value22) + } + +// [jooq-tools] END [tuples] + + /** + * Wrap a Scala R => E function in a jOOQ RecordMapper type. + */ + implicit def asMapper[R <: Record, E](f: R => E): RecordMapper[R, E] = new RecordMapper[R, E] { + def map(record: R) = f(record) + } + +// [jooq-tools] START [mapper] + + /** + * Wrap a Scala Tuple1 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList1[T1, E](f: (T1) => E): RecordMapper[Record1[T1], E] = new RecordMapper[Record1[T1], E] { + def map(record: Record1[T1]) = f(record.value1) + } + + /** + * Wrap a Scala Tuple2 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList2[T1, T2, E](f: (T1, T2) => E): RecordMapper[Record2[T1, T2], E] = new RecordMapper[Record2[T1, T2], E] { + def map(record: Record2[T1, T2]) = f(record.value1, record.value2) + } + + /** + * Wrap a Scala Tuple3 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList3[T1, T2, T3, E](f: (T1, T2, T3) => E): RecordMapper[Record3[T1, T2, T3], E] = new RecordMapper[Record3[T1, T2, T3], E] { + def map(record: Record3[T1, T2, T3]) = f(record.value1, record.value2, record.value3) + } + + /** + * Wrap a Scala Tuple4 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList4[T1, T2, T3, T4, E](f: (T1, T2, T3, T4) => E): RecordMapper[Record4[T1, T2, T3, T4], E] = new RecordMapper[Record4[T1, T2, T3, T4], E] { + def map(record: Record4[T1, T2, T3, T4]) = f(record.value1, record.value2, record.value3, record.value4) + } + + /** + * Wrap a Scala Tuple5 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList5[T1, T2, T3, T4, T5, E](f: (T1, T2, T3, T4, T5) => E): RecordMapper[Record5[T1, T2, T3, T4, T5], E] = new RecordMapper[Record5[T1, T2, T3, T4, T5], E] { + def map(record: Record5[T1, T2, T3, T4, T5]) = f(record.value1, record.value2, record.value3, record.value4, record.value5) + } + + /** + * Wrap a Scala Tuple6 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList6[T1, T2, T3, T4, T5, T6, E](f: (T1, T2, T3, T4, T5, T6) => E): RecordMapper[Record6[T1, T2, T3, T4, T5, T6], E] = new RecordMapper[Record6[T1, T2, T3, T4, T5, T6], E] { + def map(record: Record6[T1, T2, T3, T4, T5, T6]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6) + } + + /** + * Wrap a Scala Tuple7 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList7[T1, T2, T3, T4, T5, T6, T7, E](f: (T1, T2, T3, T4, T5, T6, T7) => E): RecordMapper[Record7[T1, T2, T3, T4, T5, T6, T7], E] = new RecordMapper[Record7[T1, T2, T3, T4, T5, T6, T7], E] { + def map(record: Record7[T1, T2, T3, T4, T5, T6, T7]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7) + } + + /** + * Wrap a Scala Tuple8 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList8[T1, T2, T3, T4, T5, T6, T7, T8, E](f: (T1, T2, T3, T4, T5, T6, T7, T8) => E): RecordMapper[Record8[T1, T2, T3, T4, T5, T6, T7, T8], E] = new RecordMapper[Record8[T1, T2, T3, T4, T5, T6, T7, T8], E] { + def map(record: Record8[T1, T2, T3, T4, T5, T6, T7, T8]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8) + } + + /** + * Wrap a Scala Tuple9 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList9[T1, T2, T3, T4, T5, T6, T7, T8, T9, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9) => E): RecordMapper[Record9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E] = new RecordMapper[Record9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E] { + def map(record: Record9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9) + } + + /** + * Wrap a Scala Tuple10 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) => E): RecordMapper[Record10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E] = new RecordMapper[Record10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E] { + def map(record: Record10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10) + } + + /** + * Wrap a Scala Tuple11 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) => E): RecordMapper[Record11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E] = new RecordMapper[Record11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E] { + def map(record: Record11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11) + } + + /** + * Wrap a Scala Tuple12 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) => E): RecordMapper[Record12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E] = new RecordMapper[Record12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E] { + def map(record: Record12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12) + } + + /** + * Wrap a Scala Tuple13 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) => E): RecordMapper[Record13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E] = new RecordMapper[Record13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E] { + def map(record: Record13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13) + } + + /** + * Wrap a Scala Tuple14 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) => E): RecordMapper[Record14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E] = new RecordMapper[Record14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E] { + def map(record: Record14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14) + } + + /** + * Wrap a Scala Tuple15 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) => E): RecordMapper[Record15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E] = new RecordMapper[Record15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E] { + def map(record: Record15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15) + } + + /** + * Wrap a Scala Tuple16 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) => E): RecordMapper[Record16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16], E] = new RecordMapper[Record16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16], E] { + def map(record: Record16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16) + } + + /** + * Wrap a Scala Tuple17 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) => E): RecordMapper[Record17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17], E] = new RecordMapper[Record17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17], E] { + def map(record: Record17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17) + } + + /** + * Wrap a Scala Tuple18 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) => E): RecordMapper[Record18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18], E] = new RecordMapper[Record18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18], E] { + def map(record: Record18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18) + } + + /** + * Wrap a Scala Tuple19 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) => E): RecordMapper[Record19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19], E] = new RecordMapper[Record19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19], E] { + def map(record: Record19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19) + } + + /** + * Wrap a Scala Tuple20 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20) => E): RecordMapper[Record20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20], E] = new RecordMapper[Record20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20], E] { + def map(record: Record20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19, record.value20) + } + + /** + * Wrap a Scala Tuple21 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21) => E): RecordMapper[Record21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21], E] = new RecordMapper[Record21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21], E] { + def map(record: Record21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19, record.value20, record.value21) + } + + /** + * Wrap a Scala Tuple22 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromArgList22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, E](f: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22) => E): RecordMapper[Record22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22], E] = new RecordMapper[Record22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22], E] { + def map(record: Record22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]) = f(record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19, record.value20, record.value21, record.value22) + } + + /** + * Wrap a Scala Tuple1 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple1[T1, E](f: Tuple1[T1] => E): RecordMapper[Record1[T1], E] = new RecordMapper[Record1[T1], E] { + def map(record: Record1[T1]) = f(Tuple1(record.value1)) + } + + /** + * Wrap a Scala Tuple2 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple2[T1, T2, E](f: ((T1, T2)) => E): RecordMapper[Record2[T1, T2], E] = new RecordMapper[Record2[T1, T2], E] { + def map(record: Record2[T1, T2]) = f((record.value1, record.value2)) + } + + /** + * Wrap a Scala Tuple3 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple3[T1, T2, T3, E](f: ((T1, T2, T3)) => E): RecordMapper[Record3[T1, T2, T3], E] = new RecordMapper[Record3[T1, T2, T3], E] { + def map(record: Record3[T1, T2, T3]) = f((record.value1, record.value2, record.value3)) + } + + /** + * Wrap a Scala Tuple4 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple4[T1, T2, T3, T4, E](f: ((T1, T2, T3, T4)) => E): RecordMapper[Record4[T1, T2, T3, T4], E] = new RecordMapper[Record4[T1, T2, T3, T4], E] { + def map(record: Record4[T1, T2, T3, T4]) = f((record.value1, record.value2, record.value3, record.value4)) + } + + /** + * Wrap a Scala Tuple5 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple5[T1, T2, T3, T4, T5, E](f: ((T1, T2, T3, T4, T5)) => E): RecordMapper[Record5[T1, T2, T3, T4, T5], E] = new RecordMapper[Record5[T1, T2, T3, T4, T5], E] { + def map(record: Record5[T1, T2, T3, T4, T5]) = f((record.value1, record.value2, record.value3, record.value4, record.value5)) + } + + /** + * Wrap a Scala Tuple6 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple6[T1, T2, T3, T4, T5, T6, E](f: ((T1, T2, T3, T4, T5, T6)) => E): RecordMapper[Record6[T1, T2, T3, T4, T5, T6], E] = new RecordMapper[Record6[T1, T2, T3, T4, T5, T6], E] { + def map(record: Record6[T1, T2, T3, T4, T5, T6]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6)) + } + + /** + * Wrap a Scala Tuple7 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple7[T1, T2, T3, T4, T5, T6, T7, E](f: ((T1, T2, T3, T4, T5, T6, T7)) => E): RecordMapper[Record7[T1, T2, T3, T4, T5, T6, T7], E] = new RecordMapper[Record7[T1, T2, T3, T4, T5, T6, T7], E] { + def map(record: Record7[T1, T2, T3, T4, T5, T6, T7]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7)) + } + + /** + * Wrap a Scala Tuple8 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple8[T1, T2, T3, T4, T5, T6, T7, T8, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8)) => E): RecordMapper[Record8[T1, T2, T3, T4, T5, T6, T7, T8], E] = new RecordMapper[Record8[T1, T2, T3, T4, T5, T6, T7, T8], E] { + def map(record: Record8[T1, T2, T3, T4, T5, T6, T7, T8]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8)) + } + + /** + * Wrap a Scala Tuple9 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple9[T1, T2, T3, T4, T5, T6, T7, T8, T9, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9)) => E): RecordMapper[Record9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E] = new RecordMapper[Record9[T1, T2, T3, T4, T5, T6, T7, T8, T9], E] { + def map(record: Record9[T1, T2, T3, T4, T5, T6, T7, T8, T9]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9)) + } + + /** + * Wrap a Scala Tuple10 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)) => E): RecordMapper[Record10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E] = new RecordMapper[Record10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], E] { + def map(record: Record10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10)) + } + + /** + * Wrap a Scala Tuple11 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)) => E): RecordMapper[Record11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E] = new RecordMapper[Record11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], E] { + def map(record: Record11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11)) + } + + /** + * Wrap a Scala Tuple12 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)) => E): RecordMapper[Record12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E] = new RecordMapper[Record12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], E] { + def map(record: Record12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12)) + } + + /** + * Wrap a Scala Tuple13 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13)) => E): RecordMapper[Record13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E] = new RecordMapper[Record13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], E] { + def map(record: Record13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13)) + } + + /** + * Wrap a Scala Tuple14 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14)) => E): RecordMapper[Record14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E] = new RecordMapper[Record14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], E] { + def map(record: Record14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14)) + } + + /** + * Wrap a Scala Tuple15 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15)) => E): RecordMapper[Record15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E] = new RecordMapper[Record15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], E] { + def map(record: Record15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15)) + } + + /** + * Wrap a Scala Tuple16 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16)) => E): RecordMapper[Record16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16], E] = new RecordMapper[Record16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16], E] { + def map(record: Record16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16)) + } + + /** + * Wrap a Scala Tuple17 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17)) => E): RecordMapper[Record17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17], E] = new RecordMapper[Record17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17], E] { + def map(record: Record17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17)) + } + + /** + * Wrap a Scala Tuple18 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18)) => E): RecordMapper[Record18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18], E] = new RecordMapper[Record18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18], E] { + def map(record: Record18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18)) + } + + /** + * Wrap a Scala Tuple19 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19)) => E): RecordMapper[Record19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19], E] = new RecordMapper[Record19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19], E] { + def map(record: Record19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19)) + } + + /** + * Wrap a Scala Tuple20 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20)) => E): RecordMapper[Record20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20], E] = new RecordMapper[Record20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20], E] { + def map(record: Record20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19, record.value20)) + } + + /** + * Wrap a Scala Tuple21 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21)) => E): RecordMapper[Record21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21], E] = new RecordMapper[Record21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21], E] { + def map(record: Record21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19, record.value20, record.value21)) + } + + /** + * Wrap a Scala Tuple22 => E function in a jOOQ RecordMapper type. + */ + implicit def asMapperFromTuple22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, E](f: ((T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22)) => E): RecordMapper[Record22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22], E] = new RecordMapper[Record22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22], E] { + def map(record: Record22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22]) = f((record.value1, record.value2, record.value3, record.value4, record.value5, record.value6, record.value7, record.value8, record.value9, record.value10, record.value11, record.value12, record.value13, record.value14, record.value15, record.value16, record.value17, record.value18, record.value19, record.value20, record.value21, record.value22)) + } + +// [jooq-tools] END [mapper] + +// /** +// * Wrap a Scala R => Unit function in a jOOQ RecordHandler type. +// */ +// implicit def asHandler[R <: Record](f: R => Unit): RecordHandler[R] = new RecordHandler[R] { +// def next(record: R) = f(record) +// } + +// [jooq-tools] START [handler] +// [jooq-tools] END [handler] +}