From 6095d5c1c4d5c3bf87cd95fd635802899a80dc91 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 21 Sep 2011 18:08:20 +0000 Subject: [PATCH] Added some sections to the manual --- jOOQ-website/css/jooq.css | 2 +- jOOQ-website/manual/DSL/EXISTS/index.php | 77 ++- jOOQ-website/manual/DSL/IN/index.php | 15 +- jOOQ-website/manual/DSL/index.php | 18 +- jOOQ-website/manual/JOOQ/Extend/index.php | 3 +- jOOQ-website/manual/JOOQ/Query/index.php | 98 +--- .../manual/JOOQ/UpdatableRecord/index.php | 2 +- .../src/main/resources/html-pages.xsl | 27 + jOOQ-website/src/main/resources/manual.xml | 492 +++++++++--------- 9 files changed, 381 insertions(+), 353 deletions(-) diff --git a/jOOQ-website/css/jooq.css b/jOOQ-website/css/jooq.css index b185521ad7..abc11d8223 100644 --- a/jOOQ-website/css/jooq.css +++ b/jOOQ-website/css/jooq.css @@ -142,7 +142,7 @@ a:hover { } #navigation { - padding-top: 1em; + padding-top: 1.3em; padding-left: 3em; padding-right: 3em; diff --git a/jOOQ-website/manual/DSL/EXISTS/index.php b/jOOQ-website/manual/DSL/EXISTS/index.php index 5de341a23b..ba8cefb0ef 100644 --- a/jOOQ-website/manual/DSL/EXISTS/index.php +++ b/jOOQ-website/manual/DSL/EXISTS/index.php @@ -7,7 +7,9 @@ function printH1() { print "Nested SELECT using the EXISTS operator"; } function getSlogan() { - return ""; + return "The EXISTS operator is a bit different from all other SQL + elements, as it cannot really be applied to any object in a DSL. + "; } function printContent() { global $root; @@ -16,7 +18,78 @@ function printContent() { The jOOQ User Manual : DSL or fluent API. Where SQL meets Java : Nested SELECT using the EXISTS operatorprevious : next -
+
+

The EXISTS operator for use in semi-joins or anti-joins

+

The EXISTS operator is rather independent and can stand any place + where there may be a new condition:

+ + +

This is reflected by the fact that an EXISTS clause is usually + created directly from the Factory. While this is more verbose than all + the other SQL constructs, there is no other way, when static QueryPart + creation is not an option (for now). Here is how it's done in the + Factory:

+ +
+Condition exists(Select<?> query);
+Condition notExists(Select<?> query);
+ +

When you create such a Condition, it can then be connected to any + other condition using AND, OR operators (see also the manual's section + on + Conditions). Because of this verbosity, there are also quite a few + convenience methods, where they might be useful. For instance in the + org.jooq.Condition itself:

+ +
+Condition andExists(Select<?> select);
+Condition andNotExists(Select<?> select);
+Condition orExists(Select<?> select);
+Condition orNotExists(Select<?> select);
+ +

Or in the org.jooq.SelectWhereStep:

+ +
+SelectConditionStep whereExists(Select<?> select);
+SelectConditionStep whereNotExists(Select<?> select);
+ +

Or in the org.jooq.SelectConditionStep:

+ +
+SelectConditionStep andExists(Select<?> select);
+SelectConditionStep andNotExists(Select<?> select);
+SelectConditionStep orExists(Select<?> select);
+SelectConditionStep orNotExists(Select<?> select);
+ +

An example of how to use it is quickly given. Get all authors that haven't written any books:

+ + + + +
+
+SELECT *
+  FROM T_AUTHOR
+ WHERE NOT EXISTS (SELECT 1 
+                     FROM T_BOOK 
+                    WHERE T_BOOK.AUTHOR_ID = T_AUTHOR.ID)
+
+
+create.select()
+      .from(T_AUTHOR)
+      .whereNotExists(create.select(1)
+                  .from(T_BOOK)
+                  .where(TBook.AUTHOR_ID.equal(T_AUTHOR.ID)));
+
+
diff --git a/jOOQ-website/manual/DSL/IN/index.php b/jOOQ-website/manual/DSL/IN/index.php index c65e1e3347..dea56763de 100644 --- a/jOOQ-website/manual/DSL/IN/index.php +++ b/jOOQ-website/manual/DSL/IN/index.php @@ -20,7 +20,7 @@ function printContent() {
The jOOQ User Manual : DSL or fluent API. Where SQL meets Java : Nested SELECT using the EXISTS operatorprevious : next
The jOOQ User Manual : DSL or fluent API. Where SQL meets Java : Nested SELECT using the IN operatorprevious : next
-

The IN operator for use in semi-joins

+

The IN operator for use in semi-joins or anti-joins

In addition to a list of constant values, the IN operator in org.jooq.Field @@ -37,11 +37,9 @@ function printContent() { course, this is possible with a plain JOIN as well, but let's say we want to use the IN operator. Then you have two possibilities:

- - +
- - - - - -
+
 SELECT *
   FROM T_BOOK
@@ -55,8 +53,7 @@ SELECT T_BOOK.*
   FROM T_BOOK
   JOIN T_AUTHOR ON (T_BOOK.AUTHOR_ID = T_AUTHOR.ID
                 AND T_AUTHOR.BORN    = 1920)
-
+
 create.select()
       .from(T_BOOK)
@@ -71,10 +68,8 @@ create.select(T_BOOK.getFields())
       .join(T_AUTHOR).on(TBook.AUTHOR_ID.equal(TAuthor.ID)
                      .and(TAuthor.BORN.equal(1920)));
+
diff --git a/jOOQ-website/manual/DSL/index.php b/jOOQ-website/manual/DSL/index.php index 7c5f1d9b8c..b38f40a932 100644 --- a/jOOQ-website/manual/DSL/index.php +++ b/jOOQ-website/manual/DSL/index.php @@ -25,18 +25,9 @@ function printContent() {

Here is an example to show you what that means. When you want to write a query like this in SQL:

-
The jOOQ User Manual : DSL or fluent API. Where SQL meets Java : Nested SELECT using the IN operatorprevious : next
- +
- - - - - - - - - - - -
Here is an example to show you what that means. When you want to write a query like this in SQL: Then, using jOOQ's DSL API, you can write the same query as such:
+
 -- Select all books by authors born after 1920, 
 -- named "Paulo" from a catalogue:
@@ -46,8 +37,7 @@ SELECT *
  WHERE a.year_of_birth > 1920 
    AND a.first_name = 'Paulo'
  ORDER BY b.title
-
+
 Result<Record> result = 
 create.select()
@@ -58,9 +48,7 @@ create.select()
       .orderBy(TBook.TITLE)
       .fetch();

You couldn't come much closer to SQL itself in Java, without re-writing the compiler.

diff --git a/jOOQ-website/manual/JOOQ/Extend/index.php b/jOOQ-website/manual/JOOQ/Extend/index.php index 1ec767c00b..52b37793a3 100644 --- a/jOOQ-website/manual/JOOQ/Extend/index.php +++ b/jOOQ-website/manual/JOOQ/Extend/index.php @@ -47,8 +47,7 @@ public void toSQL(RenderContext context); // variable binding to them in the correct order, passing the bind context. // // Every QueryPart must ensure, that it starts binding its variables at context.nextIndex(). -public void bind(BindContext context) throws SQLException; - +public void bind(BindContext context) throws SQLException;

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 diff --git a/jOOQ-website/manual/JOOQ/Query/index.php b/jOOQ-website/manual/JOOQ/Query/index.php index beaf0a1f80..a64a36a64b 100644 --- a/jOOQ-website/manual/JOOQ/Query/index.php +++ b/jOOQ-website/manual/JOOQ/Query/index.php @@ -84,18 +84,9 @@ function printContent() {

Example: SQL query and DSL query

- - +
- - - - - - - - - - - -
A sample SQL statement...and its equivalent in jOOQ's DSL API
+
 -- Select all books by authors born after 1920, named "Paulo" 
 -- from a catalogue consisting of authors and books:
@@ -108,8 +99,7 @@ SELECT *
  WHERE a.year_of_birth > 1920 
    AND a.first_name = 'Paulo'
  ORDER BY b.title
-
+
 // Instanciate your factory using a JDBC connection.
 Factory create = new Factory(connection, SQLDialect.ORACLE);
@@ -123,9 +113,7 @@ Result<Record> result = create.select()
     .and(FIRST_NAME.equal("Paulo")))
     .orderBy(TITLE).fetch();

@@ -194,27 +182,16 @@ q.addOrderBy(TBook.TITLE); The INSERT VALUES and the INSERT SELECT syntax

Example: SQL query and DSL query

- - +
- - - - - - - - - - - -
A typical INSERT query looks like this...and how it's done with jOOQ
+
 INSERT INTO T_AUTHOR 
     (ID, FIRST_NAME, LAST_NAME)
 VALUES 
     (100, 'Hermann', 'Hesse'),
     (101, 'Alfred', 'Döblin');
- -
+
 create.insertInto(T_AUTHOR, 
           TAuthor.ID, TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
@@ -222,9 +199,7 @@ create.insertInto(T_AUTHOR,
       .values(101, "Alfred", "Döblin")
       .execute();

The DSL syntax tries to stay close to actual SQL. In detail, @@ -340,27 +315,16 @@ i.execute(); multi-table updates will be implemented in the near future.

Example: SQL query and DSL query

- - +
- - - - - - - - - - - -
A typical UPDATE query looks like this...and how it's done with jOOQ
+
 
 UPDATE T_AUTHOR
    SET FIRST_NAME = 'Hermann',
        LAST_NAME = 'Hesse'
  WHERE ID = 3;
- -
+
 create.update(T_AUTHOR)
       .set(TAuthor.FIRST_NAME, "Hermann")
@@ -368,9 +332,7 @@ create.update(T_AUTHOR)
       .where(TAuthor.ID.equal(3))
       .execute();

Example: Non-DSL Query

@@ -389,33 +351,20 @@ u.execute(); multi-table deletes will be implemented in the near future.

Example: SQL query and DSL query

- - +
- - - - - - - - - - - -
A typical DELETE query looks like this...and how it's done with jOOQ
+
 
 DELETE T_AUTHOR
  WHERE ID = 100;
- -
+
 create.delete(T_AUTHOR)
       .where(TAuthor.ID.equal(100))
       .execute();

Example: Non-DSL Query

@@ -442,11 +391,9 @@ d.execute(); not supported. Here is an example:

- - +
- - - - -
+
 -- Check if there is already an author called 'Hitchcock'
 -- If there is, rename him to John. If there isn't add him.
@@ -456,9 +403,7 @@ USING (SELECT 1 FROM DUAL)
 ON (LAST_NAME = 'Hitchcock')
 WHEN MATCHED THEN UPDATE SET FIRST_NAME = 'John'
 WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
- -
+
 create.mergeInto(T_AUTHOR)
       .using(create().selectOne())
@@ -469,9 +414,7 @@ create.mergeInto(T_AUTHOR)
       .values("Hitchcock")
       .execute();
@@ -479,20 +422,15 @@ create.mergeInto(T_AUTHOR)

The syntax is trivial:

- +
- - - - -
+
TRUNCATE TABLE T_AUTHOR;
- -
+
create.truncate(T_AUTHOR).execute();

This is not supported by Ingres and SQLite. jOOQ will execute a DELETE FROM T_AUTHOR statement instead.

diff --git a/jOOQ-website/manual/JOOQ/UpdatableRecord/index.php b/jOOQ-website/manual/JOOQ/UpdatableRecord/index.php index e4b9e21193..d43a395f08 100644 --- a/jOOQ-website/manual/JOOQ/UpdatableRecord/index.php +++ b/jOOQ-website/manual/JOOQ/UpdatableRecord/index.php @@ -76,7 +76,7 @@ void refreshUsing(TableField<R, ?>... keys); store records to an unconstrained view. An example lifecycle of a book without any keys can then be implemented as such:

-
+							
 // Create a new record and insert it into the database
 TBookRecord book = create.newRecord(T_BOOK);
 book.setTitle("My first book");
diff --git a/jOOQ-website/src/main/resources/html-pages.xsl b/jOOQ-website/src/main/resources/html-pages.xsl
index de19a8c3a4..7f97c2f02b 100644
--- a/jOOQ-website/src/main/resources/html-pages.xsl
+++ b/jOOQ-website/src/main/resources/html-pages.xsl
@@ -275,6 +275,33 @@ function printContent() {
 					
 				
 			
+			
+				
+					
+				
+
+ +
+					
+				
+
+ +
+					
+				
+
+ + + + + + +
+ + + +
+
diff --git a/jOOQ-website/src/main/resources/manual.xml b/jOOQ-website/src/main/resources/manual.xml index dcdd831186..b7ac854b44 100644 --- a/jOOQ-website/src/main/resources/manual.xml +++ b/jOOQ-website/src/main/resources/manual.xml @@ -89,7 +89,7 @@

Example CREATE TABLE statements

-
+							
 CREATE TABLE t_language (
   id NUMBER(7) NOT NULL PRIMARY KEY,
   cd CHAR(2) NOT NULL,
@@ -132,7 +132,7 @@ CREATE TABLE t_book_to_book_store (
     REFERENCES t_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 @@ -189,13 +189,13 @@ CREATE TABLE t_book_to_book_store ( only using the MySQL dialect, you can choose to create a new Factory using any of the following types:

-
+							
 // A general, dialect-unspecific factory
 Factory create = new Factory(connection, SQLDialect.MYSQL);
 
 // A MySQL-specific factory
 MySQLFactory create = new MySQLFactory(connection);
-							
+

The advantage of using a dialect-specific Factory lies in the fact, that you have access to more proprietary RDMBS functionality. This may @@ -268,7 +268,7 @@ MySQLFactory create = new MySQLFactory(connection);

The Table

The formal definition of a starts with

-
public interface Table<R extends Record> // [...]
+ public interface Table<R extends Record> // [...]

This means that every table is associated with a subtype of the @@ -304,7 +304,7 @@ MySQLFactory create = new MySQLFactory(connection);

The Field

The formal definition of a Field starts with

-
public interface Field<T> // [...]
+ public interface Field<T> // [...]

Fields are generically parameterised with a Java type <T> @@ -342,7 +342,7 @@ MySQLFactory create = new MySQLFactory(connection);

The Field itself is a very broad concept. Other tools, or databases refer to it as expression or column. When you just want to

-
SELECT 1 FROM DUAL
+ SELECT 1 FROM DUAL

Then 1 is considered a Field or more explicitly, a , @@ -352,7 +352,7 @@ MySQLFactory create = new MySQLFactory(connection);

More advanced uses become clear quickly, when you do things like

-
SELECT 1 + 1 FROM DUAL
+ SELECT 1 + 1 FROM DUAL

Where 1 + 1 itself is a Field or more explicitly, an @@ -381,7 +381,7 @@ MySQLFactory create = new MySQLFactory(connection);

TableFields join both <R> and <T> generic parameters into their specification:

-
public interface TableField<R extends Record, T> // [...]
+ public interface TableField<R extends Record, T> // [...]

This can be used for additional type safety in the future, or by client code.

@@ -425,7 +425,7 @@ MySQLFactory create = new MySQLFactory(connection); very much like the , by providing a very simple API:

-
+							
 // Check whether there are any more records to be fetched
 boolean hasNext() throws SQLException;
 
@@ -433,7 +433,7 @@ boolean hasNext() throws SQLException;
 R fetchOne() throws SQLException;
 
 // Close the underlying JDBC ResultSet. Don't forget to call this, before disposing the Cursor.
-void close() throws SQLException;
+void close() throws SQLException;

The Record

@@ -444,7 +444,7 @@ void close() throws SQLException;

its data and adds convenience methods for data type conversion. These are the main access ways:

-
+							
 // If you can keep a reference of the selected field, then you can get the corresponding value type-safely
 <T> T getValue(Field<T> field);
 
@@ -454,7 +454,7 @@ Object getValue(String fieldName);
 
 // If you know the index of the selected field within the tuple,
 // then you can get its value without any type information
-Object getValue(int index);
+Object getValue(int index);

In some cases, you will not be able to reference the selected Fields both when you create the SELECT statement and when you fetch data from @@ -463,7 +463,7 @@ Object getValue(int index);

you know what type you want to get, you can always use the Record's convenience methods for type conversion, however. Some examples:

-
+							
 // These methods will try to convert a value to a BigDecimal.
 // This will work for all numeric types and for CHAR/VARCHAR types, if they contain numeric values:
 BigDecimal getValueAsBigDecimal(String fieldName);
@@ -471,7 +471,7 @@ BigDecimal getValueAsBigDecimal(int fieldIndex);
 
 // This method can perform arbitrary conversions
 <T> T getValue(String fieldName, Class<? extends T> type);
-<T> T getValue(int fieldIndex, Class<? extends T> type);
+<T> T getValue(int fieldIndex, Class<? extends T> type); @@ -487,7 +487,7 @@ BigDecimal getValueAsBigDecimal(int fieldIndex);

As of jOOQ 1.5, the UpdatableRecord essentially contains three additional methods CRUD (Create Read Update Delete) operations:

-
+							
 // Store any changes made to this record to the database.
 // The record executes an INSERT if the PRIMARY KEY is NULL or has been changed. Otherwise, an UPDATE is performed.
 int store();
@@ -496,9 +496,9 @@ int store();
 int delete();
 
 // Reflects changes made in the database to this Record
-void refresh();
+void refresh();

An example lifecycle of a book can be implemented as such:

-
+							
 // Create a new record and insert it into the database
 TBookRecord book = create.newRecord(T_BOOK);
 book.setTitle("My first book");
@@ -509,7 +509,7 @@ book.setPublishedIn(2010);
 book.store();
 
 // Delete it
-book.delete();
+book.delete();

These operations are very simple utilities. They do not reflect the functionality offered by Hibernate or other persistence managers.

@@ -523,7 +523,7 @@ book.delete(); key fields. The API looks like this:

-
+							
 // INSERT or UPDATE the record using the provided keys
 int storeUsing(TableField<R, ?>... keys)
 
@@ -531,7 +531,7 @@ int storeUsing(TableField<R, ?>... keys)
 int deleteUsing(TableField<R, ?>... keys);
 
 // Reflects changes made in the database to this Record
-void refreshUsing(TableField<R, ?>... keys);
+void refreshUsing(TableField<R, ?>... keys);

This is useful if your RDBMS does not support referential constraints (e.g. MySQL's @@ -539,7 +539,7 @@ void refreshUsing(TableField<R, ?>... keys); store records to an unconstrained view. An example lifecycle of a book without any keys can then be implemented as such:

-
+							
 // Create a new record and insert it into the database
 TBookRecord book = create.newRecord(T_BOOK);
 book.setTitle("My first book");
@@ -550,7 +550,7 @@ book.setPublishedIn(2010);
 book.storeUsing(TBook.ID);
 
 // Delete it
-book.deleteUsing(TBook.ID);
+book.deleteUsing(TBook.ID); @@ -613,13 +613,8 @@ book.deleteUsing(TBook.ID);

Example: SQL query and DSL query

- - - - - - - - - -
A sample SQL statement...and its equivalent in jOOQ's DSL API
+							
+								
 -- Select all books by authors born after 1920, named "Paulo" 
 -- from a catalogue consisting of authors and books:
 
@@ -630,8 +625,8 @@ SELECT *
     ON a.id = b.author_id 
  WHERE a.year_of_birth > 1920 
    AND a.first_name = 'Paulo'
- ORDER BY b.title
+ ORDER BY b.title
+ 								
 // Instanciate your factory using a JDBC connection.
 Factory create = new Factory(connection, SQLDialect.ORACLE);
 
@@ -642,9 +637,8 @@ Result<Record> result = create.select()
     .on(ID.equal(AUTHOR_ID))
     .where(YEAR_OF_BIRTH.greaterThan(1920)
     .and(FIRST_NAME.equal("Paulo")))
-    .orderBy(TITLE).fetch();
+ .orderBy(TITLE).fetch(); +

In the above example, some generated artefacts are used for querying. @@ -674,7 +668,7 @@ Result<Record> result = create.select() want to add Query parts in the order SQL expects them), you can use this syntax:

-
+							
 // Re-use the factory to create a SelectQuery. This example will not make use of static imports...
 SelectQuery q = create.selectQuery();
 q.addFrom(TAuthor.T_AUTHOR);
@@ -686,7 +680,7 @@ q.addJoin(TBook.T_BOOK, TAuthor.ID.equal(TBook.AUTHOR_ID));
 // The AND operator between Conditions is implicit here
 q.addConditions(TAuthor.YEAR_OF_BIRTH.greaterThan(1920));
 q.addConditions(TAuthor.FIRST_NAME.equal("Paulo"));
-q.addOrderBy(TBook.TITLE);
+q.addOrderBy(TBook.TITLE);

Fetching data

@@ -709,27 +703,18 @@ q.addOrderBy(TBook.TITLE); The INSERT VALUES and the INSERT SELECT syntax

Example: SQL query and DSL query

- - - - - - - - - -
A typical INSERT query looks like this...and how it's done with jOOQ
+							
 INSERT INTO T_AUTHOR 
     (ID, FIRST_NAME, LAST_NAME)
 VALUES 
     (100, 'Hermann', 'Hesse'),
-    (101, 'Alfred', 'Döblin');
-
+    (101, 'Alfred', 'Döblin');
+								
 create.insertInto(T_AUTHOR, 
           TAuthor.ID, TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
       .values(100, "Hermann", "Hesse")
       .values(101, "Alfred", "Döblin")
-      .execute();
+ .execute();

The DSL syntax tries to stay close to actual SQL. In detail, however, Java is limited in its possibilities. That's why the @@ -747,7 +732,7 @@ create.insertInto(T_AUTHOR, 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(T_AUTHOR)
       .set(TAuthor.ID, 100)
       .set(TAuthor.FIRST_NAME, "Hermann")
@@ -756,7 +741,7 @@ create.insertInto(T_AUTHOR)
       .set(TAuthor.ID, 101)
       .set(TAuthor.FIRST_NAME, "Alfred")
       .set(TAuthor.LAST_NAME, "Döblin")
-      .execute();
+ .execute();

As you can see, this syntax is a bit more verbose, but also more type-safe, as every field can be matched with its value.

@@ -766,14 +751,14 @@ create.insertInto(T_AUTHOR) which is supported by jOOQ and simulated in other RDBMS, where this is possible. 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(T_AUTHOR, TAuthor.ID, TAuthor.LAST_NAME)
       .values(3, "Koontz")
       .onDuplicateKeyUpdate()
       .set(TAuthor.LAST_NAME, "Koontz")
-      .execute();
+ .execute();

Example: INSERT .. RETURNING clause

The Postgres database has native support for an INSERT .. RETURNING @@ -782,7 +767,7 @@ create.insertInto(T_AUTHOR, TAuthor.ID, TAuthor.LAST_NAME) method. Take this example:

-
+							
 // Add another author, with a generated ID
 Record<?> record =
 create.insertInto(T_AUTHOR, TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
@@ -799,11 +784,11 @@ create.insertInto(T_AUTHOR, TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
       .values("Friedrich", "Schiller")
       // You can request any field. Also trigger-generated values
       .returning(TAuthor.ID, TAuthor.CREATION_DATE)
-      .fetch();
+ .fetch();

Example: Non-DSL Query

You can always use the more verbose regular syntax of the InsertQuery, if you need more control:

-
+      						
 // Insert a new author into the T_AUTHOR table
 InsertQuery<TAuthorRecord> i = create.insertQuery(T_AUTHOR);
 i.addValue(TAuthor.ID, 100);
@@ -814,18 +799,18 @@ i.newRecord();
 i.addValue(TAuthor.ID, 101);
 i.addValue(TAuthor.FIRST_NAME, "Alfred");
 i.addValue(TAuthor.LAST_NAME, "Döblin");
-i.execute();
+i.execute();

Example: INSERT Query combined with SELECT statements

The InsertQuery.addValue() method is overloaded, such that you can also provide a Field, potentially containing an expression:

-
+							
 // Insert a new author into the T_AUTHOR table
 InsertQuery<TAuthorRecord> i = create.insertQuery(T_AUTHOR);
 i.addValue(TAuthor.ID, create.select(TAuthor.ID.max().add(1)).from(T_AUTHOR).asField())
 i.addValue(TAuthor.FIRST_NAME, "Hermann");
 i.addValue(TAuthor.LAST_NAME, "Hesse");
-i.execute();
+i.execute();

Note that especially MySQL (and some other RDBMS) has some limitations regarding that syntax. You may not be able to select from the same table you're inserting into

@@ -833,10 +818,10 @@ i.execute();

Example: INSERT SELECT syntax support

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

-
+							
 Insert i = create.insertInto(T_AUTHOR_ARCHIVE,
            create.selectFrom(T_AUTHOR).where(TAuthor.DECEASED.equal(1)));
-i.execute();
+i.execute();

UPDATE Statements

@@ -844,37 +829,27 @@ i.execute(); multi-table updates will be implemented in the near future.

Example: SQL query and DSL query

- - - - - - - - - -
A typical UPDATE query looks like this...and how it's done with jOOQ
+							
 
 UPDATE T_AUTHOR
    SET FIRST_NAME = 'Hermann',
        LAST_NAME = 'Hesse'
- WHERE ID = 3;
-
+ WHERE ID = 3;
 create.update(T_AUTHOR)
       .set(TAuthor.FIRST_NAME, "Hermann")
       .set(TAuthor.LAST_NAME, "Hesse")
       .where(TAuthor.ID.equal(3))
-      .execute();
+ .execute();

Example: Non-DSL Query

Using the class, this is how you could express an UPDATE statement:

-
+							
 UpdateQuery<TAuthorRecord> u = create.updateQuery(T_AUTHOR);
 u.addValue(TAuthor.FIRST_NAME, "Hermann");
 u.addValue(TAuthor.FIRST_NAME, "Hesse");
 u.addConditions(TAuthor.ID.equal(3));
-u.execute();
+u.execute();

DELETE Statements

@@ -882,31 +857,21 @@ u.execute(); multi-table deletes will be implemented in the near future.

Example: SQL query and DSL query

- - - - - - - - - -
A typical DELETE query looks like this...and how it's done with jOOQ
+							
 
 DELETE T_AUTHOR
- WHERE ID = 100;
-
+ WHERE ID = 100;
 create.delete(T_AUTHOR)
       .where(TAuthor.ID.equal(100))
-      .execute();
+ .execute();

Example: Non-DSL Query

Using the class, this is how you could express a DELETE statement:

-
+							
 DeleteQuery<TAuthorRecord> d = create.deleteQuery(T_AUTHOR);
 d.addConditions(TAuthor.ID.equal(100));
-d.execute();
+d.execute();

MERGE Statement

@@ -924,9 +889,7 @@ d.execute(); not supported. Here is an example:

- - - - - -
+							
 -- Check if there is already an author called 'Hitchcock'
 -- If there is, rename him to John. If there isn't add him.
 
@@ -934,9 +897,7 @@ MERGE INTO T_AUTHOR
 USING (SELECT 1 FROM DUAL)
 ON (LAST_NAME = 'Hitchcock')
 WHEN MATCHED THEN UPDATE SET FIRST_NAME = 'John'
-WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
-
+WHEN NOT MATCHED THEN INSERT (LAST_NAME) VALUES ('Hitchcock')
 create.mergeInto(T_AUTHOR)
       .using(create().selectOne())
       .on(TAuthor.LAST_NAME.equal("Hitchcock"))
@@ -944,22 +905,18 @@ create.mergeInto(T_AUTHOR)
       .set(TAuthor.FIRST_NAME, "John")
       .whenNotMatchedThenInsert(TAuthor.LAST_NAME)
       .values("Hitchcock")
-      .execute();
+ .execute();

TRUNCATE Statement

The syntax is trivial:

- - - - - -
TRUNCATE TABLE T_AUTHOR;
-
create.truncate(T_AUTHOR).execute();
+ + + TRUNCATE TABLE T_AUTHOR; + create.truncate(T_AUTHOR).execute(); +

This is not supported by Ingres and SQLite. jOOQ will execute a DELETE FROM T_AUTHOR statement instead.

@@ -976,7 +933,7 @@ create.mergeInto(T_AUTHOR)

The ResultQuery provides many convenience methods

-
+							
 public interface ResultQuery<R extends Record> {
 
   // These methods allow for fetching a jOOQ Result
@@ -1050,7 +1007,7 @@ public interface ResultQuery<R extends Record> {
   // Similar to how Avajé Ebean works.
   FutureResult<R> fetchLater();
   FutureResult<R> fetchLater(ExecutorService executor);
-}
+}
@@ -1079,7 +1036,7 @@ public interface ResultQuery<R extends Record> { , you will have to "import" or "re-attach" them to a Factory

-
+							
 // Deserialise a SELECT statement
 ObjectInputStream in = new ObjectInputStream(...);
 Select<?> select = (Select<?>) in.readObject();
@@ -1089,18 +1046,18 @@ select.execute();
 
 // In order to execute the above select, attach it first
 Factory create = new Factory(connection, SQLDialect.ORACLE);
-create.attach(select);
+create.attach(select);

Automatically attaching QueryParts

In simple cases, you can register a ConfigurationProvider in jOOQ's ConfigurationRegistry

-
+							
 // Create your own custom ConfigurationProvider that will make
 // your default Factory available to jOOQ
 ConfigurationProvider provider = new CustomConfigurationProvider();
 
 // Statically register the provider to jOOQ's ConfigurationRegistry
-ConfigurationRegistry.setProvider(provider);
+ConfigurationRegistry.setProvider(provider);

Once you have executed these steps, all subsequent deserialisations will try to access a Configuration (containing a JDBC Connection) from @@ -1127,20 +1084,20 @@ ConfigurationRegistry.setProvider(provider);

Write your own QueryPart implementations

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:

-
+							
 public abstract class CustomField<T> extends AbstractField<T> {
   // [...]
 }
 public abstract class CustomCondition extends AbstractCondition {
   // [...]
-}
+}

These two classes are declared public and covered by integration tests. When you extend these classes, you will have to provide your own implementations for the ' bind() and toSQL() methods:

-
+							
 // This method must produce valid SQL. If your QueryPart contains other QueryParts, you may delegate SQL code generation to them
 // in the correct order, passing the render context.
 //
@@ -1152,8 +1109,7 @@ public void toSQL(RenderContext context);
 // variable binding to them in the correct order, passing the bind context.
 //
 // Every QueryPart must ensure, that it starts binding its variables at context.nextIndex().
-public void bind(BindContext context) throws SQLException;
-
+public void bind(BindContext context) throws SQLException;

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 @@ -1350,7 +1306,7 @@ org.jooq.util.GenerationTool /jooq-config.properties

Run generation with ant

You can also use an ant task to generate your classes:

-
+							
 <!-- Task definition -->
 <taskdef name="generate-classes" classname="org.jooq.util.GenerationTask">
   <classpath>
@@ -1374,7 +1330,7 @@ org.jooq.util.GenerationTool /jooq-config.properties
jdbcpassword="" generatortargetpackage="org.jooq.test.generatedclasses" generatortargetdirectory="${basedir}/src"/> -</target> +</target>

Use jOOQ generated classes in your application

Be sure, both jOOQ.jar and your generated package (see @@ -1418,9 +1374,9 @@ org.jooq.util.GenerationTool /jooq-config.properties

The schema can be used to dynamically discover generate database artefacts. Tables, sequences, and other items are accessible from the schema. For example:

-
+							
 public final java.util.List<org.jooq.Sequence> getSequences();
-public final java.util.List<org.jooq.Table<?>> getTables();
+public final java.util.List<org.jooq.Table<?>> getTables(); @@ -1455,7 +1411,7 @@ public final java.util.List<org.jooq.Table<?>> getTables();

The Table as an entity meta model

-
+							
 public class TAuthor extends UpdatableTableImpl<TAuthorRecord> {
 
     // The singleton instance of the Table
@@ -1467,7 +1423,7 @@ public class TAuthor extends UpdatableTableImpl<TAuthorRecord> {
     public static final TableField<TAuthorRecord, String> LAST_NAME =      // [...]
     public static final TableField<TAuthorRecord, Date> DATE_OF_BIRTH =    // [...]
     public static final TableField<TAuthorRecord, Integer> YEAR_OF_BIRTH = // [...]
-}
+}

The Table's associated TableRecord

If you use the @@ -1477,7 +1433,7 @@ public class TAuthor extends UpdatableTableImpl<TAuthorRecord> { extends Record>'s associated Record type <R>. In the case of the above TAuthor Table, this will be a TAuthorRecord.

-
+							
 public class TAuthorRecord extends UpdatableRecordImpl<TAuthorRecord> {
 
     // Getters and setters for the various fields
@@ -1492,7 +1448,7 @@ public class TAuthorRecord extends UpdatableRecordImpl<TAuthorRecord> {
 
     // Navigation methods for foreign keys
     public List<TBookRecord> getTBooks() throws SQLException { // [...]
-}
+} @@ -1523,7 +1479,7 @@ public class TAuthorRecord extends UpdatableRecordImpl<TAuthorRecord> {

"Standalone" stored procedures and functions

Let's say you have these stored procedures and functions in your Oracle database

-
+							
 -- Check whether there is an author in T_AUTHOR by that name
 CREATE OR REPLACE FUNCTION f_author_exists (author_name VARCHAR2) RETURN NUMBER;
 
@@ -1531,7 +1487,7 @@ CREATE OR REPLACE FUNCTION f_author_exists (author_name VARCHAR2) RETURN NUMBER;
 CREATE OR REPLACE PROCEDURE p_author_exists (author_name VARCHAR2, result OUT NUMBER);
 
 -- Check whether there is an author in T_AUTHOR by that name and get his ID
-CREATE OR REPLACE PROCEDURE p_author_exists_2 (author_name VARCHAR2, result OUT NUMBER, id OUT NUMBER);
+CREATE OR REPLACE PROCEDURE p_author_exists_2 (author_name VARCHAR2, result OUT NUMBER, id OUT NUMBER);

jOOQ will essentially generate two artefacts for every procedure/function:

Let's see what these things look like, in Java. The classes (simplified for the example):

-
+							
 // The function has a generic type parameter <T> bound to its return value
 public class FAuthorExists extends StoredFunctionImpl<BigDecimal> {
 
@@ -1577,18 +1533,18 @@ public class PAuthorExists_2 extends StoredProcedureImpl {
     // the getters...
     public BigDecimal getResult() { // [...]
     public BigDecimal getId() { // [...]
-}
+}

An example invocation of such a stored procedure might look like this:

-
+							
 PAuthorExists p = new PAuthorExists();
 p.setAuthorName("Paulo");
 p.execute(configuration);
-assertEquals(BigDecimal.ONE, p.getResult());
+assertEquals(BigDecimal.ONE, p.getResult());

If you use the generated convenience methods, however, things are much simpler, still:

-
+							
 // Every schema has a single Functions class with convenience methods
 public final class Functions {
 
@@ -1611,11 +1567,11 @@ public final class Procedures {
     // Procedures with more than 1 OUT parameter return the procedure
     // object (see above example)
     public static PAuthorExists_2 pAuthorExists_2(Configuration configuration, String authorName) { // [...]
-}
+}

An sample invocation, equivalent to the previous example:

-
-assertEquals(BigDecimal.ONE, Procedures.pAuthorExists(configuration, "Paulo"));
+ +assertEquals(BigDecimal.ONE, Procedures.pAuthorExists(configuration, "Paulo"));

jOOQ's understanding of procedures vs functions

@@ -1732,7 +1688,7 @@ assertEquals(BigDecimal.ONE, Procedures.pAuthorExists(configuration, "Paulo"));<

In Oracle, you would define UDTs like this:

-
+							
 CREATE TYPE u_street_type AS OBJECT (
   street VARCHAR2(100),
   no VARCHAR2(30)
@@ -1745,17 +1701,17 @@ CREATE TYPE u_address_type AS OBJECT (
   country VARCHAR2(50),
   since DATE,
   code NUMBER(7)
-)
+)

These types could then be used in tables and/or stored procedures like such:

-
+							
 CREATE TABLE t_author (
   id NUMBER(7) NOT NULL PRIMARY KEY,
   -- [...]
   address u_address_type
 )
 
-CREATE OR REPLACE PROCEDURE p_check_address (address IN OUT u_address_type);
+CREATE OR REPLACE PROCEDURE p_check_address (address IN OUT u_address_type);

Standard JDBC UDT support encourages JDBC-driver developers to implement @@ -1774,7 +1730,7 @@ CREATE OR REPLACE PROCEDURE p_check_address (address IN OUT u_address_type); and as such:

-
+							
 // There is an analogy between UDT/Table and UDTRecord/TableRecord...
 public class UAddressType extends UDTImpl<UAddressTypeRecord> {
 
@@ -1789,24 +1745,24 @@ public class UAddressType extends UDTImpl<UAddressTypeRecord> {
     public static final UDTField<UAddressTypeRecord, String> COUNTRY =           // [...]
     public static final UDTField<UAddressTypeRecord, Date> SINCE =               // [...]
     public static final UDTField<UAddressTypeRecord, Integer> CODE =             // [...]
-}
+}

Now, when you interact with entities or procedures that hold UDT's, that's very simple as well. Here is an example:

-
+							
 // Fetch any author from the T_AUTHOR table
 TAuthorRecord author = create.selectFrom(T_AUTHOR).fetchAny();
 
 // Print out the author's address's house number
-System.out.println(author.getAddress().getStreet().getNo());
+System.out.println(author.getAddress().getStreet().getNo());

A similar thing can be achieved when interacting with the example stored procedure:

-
+							
 // Create a new UDTRecord of type U_ADDRESS_TYPE
 UAddressTypeRecord address = new UAddressTypeRecord();
 address.setCountry("Switzerland");
 
 // Call the stored procedure with IN OUT parameter of type U_ADDRESS_TYPE
-address = Procedures.pCheckAddress(connection, address);
+address = Procedures.pCheckAddress(connection, address);

ARRAY types

@@ -1841,17 +1797,17 @@ address = Procedures.pCheckAddress(connection, address);

Example: General ARRAY types

An example usage of ARRAYs is given here for the Postgres dialect

-
+							
 CREATE TABLE t_arrays (
   id integer not null primary key,
   string_array VARCHAR(20)[],
   number_array INTEGER[]
 )
 
-CREATE FUNCTION f_arrays(in_array IN text[]) RETURNS text[]
+CREATE FUNCTION f_arrays(in_array IN text[]) RETURNS text[]

When generating source code from the above entities, these artefacts will be created in Java:

-
+							
 public class TArrays extends UpdatableTableImpl<TArraysRecord> {
 
     // The generic type parameter <T> is bound to an array of a matching type
@@ -1864,7 +1820,7 @@ public final class Functions {
     public static String[] fArrays(Connection connection, String[] inArray) throws SQLException { // [...]
     public static Field<String[]> fArrays(String[] inArray) {                                     // [...]
     public static Field<String[]> fArrays(Field<String[]> inArray) {                              // [...]
-}
+}

Example: Oracle VARRAY types

In Oracle, a VARRAY type is something slightly different than in @@ -1875,7 +1831,7 @@ public final class Functions { generated objects from those types as well. The example above would read like this in Oracle:

-
+							
 CREATE TYPE u_string_array AS VARRAY(4) OF VARCHAR2(20)
 CREATE TYPE u_number_array AS VARRAY(4) OF NUMBER(7)
 
@@ -1886,7 +1842,7 @@ CREATE TABLE t_arrays (
 )
 
 CREATE OR REPLACE FUNCTION f_arrays (in_array u_string_array)
-RETURN u_string_array
+RETURN u_string_array

Note that it becomes clear immediately, that a mapping from U_STRING_ARRAY to String[] is obvious. But a mapping from String[] to @@ -1894,7 +1850,7 @@ RETURN u_string_array and other artefacts in Oracle:

-
+							
 public class UStringArrayRecord extends ArrayRecordImpl<String> {  // [...]
 public class UNumberArrayRecord extends ArrayRecordImpl<Integer> { // [...]
 
@@ -1907,7 +1863,7 @@ public final class Functions {
     public static UStringArrayRecord fArrays3(Connection connection, UStringArrayRecord inArray) { // [...]
     public static Field<UStringArrayRecord> fArrays3(UStringArrayRecord inArray) {                 // [...]
     public static Field<UStringArrayRecord> fArrays3(Field<UStringArrayRecord> inArray) {          // [...]
-}
+}

ENUM types

@@ -1922,7 +1878,7 @@ public final class Functions {

Some examples:

-
+							
 -- An example enum type
 CREATE TYPE u_book_status AS ENUM ('SOLD OUT', 'ON STOCK', 'ORDERED')
 
@@ -1932,19 +1888,19 @@ CREATE TABLE t_book (
 
   -- [...]
   status u_book_status
-)
+)

The above Postgres ENUM type will be generated as

-
+							
 public enum UBookStatus implements EnumType {
     ORDERED("ORDERED"),
     ON_STOCK("ON STOCK"),
     SOLD_OUT("SOLD OUT");
 
     // [...]
-}
+}

Intuitively, the generated classes for the T_BOOK table in Postgres would look like this:

-
+							
 // The meta-model class
 public class TBook extends UpdatableTableImpl<TBookRecord> {
 
@@ -1961,7 +1917,7 @@ public class TBookRecord extends UpdatableRecordImpl<TBookRecord> {
     // deal with the generated UBookStatus
     public void setStatus(UBookStatus value) { // [...]
     public UBookStatus getStatus() {           // [...]
-}
+}

Note that jOOQ allows you to simulate ENUM types where this makes sense in your data model. See the section on @@ -1982,31 +1938,31 @@ public class TBookRecord extends UpdatableRecordImpl<TBookRecord> {

Sequences implement the interface, providing essentially this functionality:

-
+							
 // Get a field for the CURRVAL sequence property
 Field<BigInteger> currval();
 
 // Get a field for the NEXTVAL sequence property
-Field<BigInteger> nextval();
+Field<BigInteger> nextval();

So if you have a sequence like this in Oracle:

-
CREATE SEQUENCE s_author_id
+ CREATE SEQUENCE s_author_id

This is what jOOQ will generate:

-
+							
 public final class Sequences {
 
     // A static sequence instance
     public static final Sequence S_AUTHOR_ID = // [...]
-}
+}

Which you can use in a select statement as such:

-
+							
 Field<BigInteger> s = Sequences.S_AUTHOR_ID.nextval();
-BigInteger nextID   = create.select(s).fetchOne(s);
+BigInteger nextID = create.select(s).fetchOne(s);

Or directly fetch currval() and nextval() from the sequence using the Factory:

-
+							
 BigInteger currval = create.currval(Sequences.S_AUTHOR_ID);
-BigInteger nextval = create.nextval(Sequences.S_AUTHOR_ID);
+BigInteger nextval = create.nextval(Sequences.S_AUTHOR_ID); @@ -2026,13 +1982,7 @@ BigInteger nextval = create.nextval(Sequences.S_AUTHOR_ID); just like .NET's C# does with LINQ to SQL.

Here is an example to show you what that means. When you want to write a query like this in SQL:

- - - - - - - - - -
Here is an example to show you what that means. When you want to write a query like this in SQL: Then, using jOOQ's DSL API, you can write the same query as such:
+					
 -- Select all books by authors born after 1920, 
 -- named "Paulo" from a catalogue:
 SELECT * 
@@ -2040,8 +1990,7 @@ SELECT *
   JOIN t_book b ON a.id = b.author_id 
  WHERE a.year_of_birth > 1920 
    AND a.first_name = 'Paulo'
- ORDER BY b.title
+ ORDER BY b.title
 Result<Record> result = 
 create.select()
       .from(T_AUTHOR)
@@ -2049,9 +1998,7 @@ create.select()
       .where(TAuthor.YEAR_OF_BIRTH.greaterThan(1920)
       .and(TAuthor.FIRST_NAME.equal("Paulo")))
       .orderBy(TBook.TITLE)
-      .fetch();
+ .fetch();

You couldn't come much closer to SQL itself in Java, without re-writing the compiler.

@@ -2074,7 +2021,7 @@ create.select() some extensions, is provided by a query like this:

-
+							
 -- get all authors' first and last names, and the number 
 -- of books they've written in German, if they have written
 -- more than five books in German in the last three years 
@@ -2092,14 +2039,14 @@ GROUP BY T_AUTHOR.FIRST_NAME, T_AUTHOR.LAST_NAME
 ORDER BY T_AUTHOR.LAST_NAME ASC NULLS FIRST
    LIMIT 2 
   OFFSET 1
-     FOR UPDATE
+ FOR UPDATE

So that's daily business. How to do it with jOOQ: When you first create a SELECT statement using the Factory's select() methods

-
+     						
 SelectFromStep select(Field<?>... fields);
 
 // Example:
-create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count());
+create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count());

jOOQ will return an "intermediary" type to you, representing the @@ -2117,12 +2064,12 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count()); clauses. Let's say you do decide to add a FROM clause, then you can use this method for instance:

-
+							
 SelectJoinStep from(TableLike<?>... table);
 
 // The example, continued:
 create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
-      .from(T_AUTHOR);
+ .from(T_AUTHOR);

After adding the table-like structures (mostly just Tables) to select from, you may optionally choose to add a JOIN clause, as the @@ -2131,7 +2078,7 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count()) extends . But let's say we add a JOIN:

-
+							
 // These join types are supported
 SelectOnStep                    join(Table<?> table);
 SelectOnStep           leftOuterJoin(Table<?> table);
@@ -2145,7 +2092,7 @@ SelectJoinStep naturalRightOuterJoin(Table<?> table);
 // The example, continued:
 create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .from(T_AUTHOR)
-      .join(T_BOOK);
+ .join(T_BOOK);

Now, if you do add a JOIN clause, you have to specify the JOIN .. ON condition before you can add more clauses. That's not an optional step @@ -2153,7 +2100,7 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count()) is a top-level interface.

-
+							
 // These join conditions are supported
 SelectJoinStep    on(Condition... conditions);
 SelectJoinStep using(Field<?>... fields);
@@ -2161,7 +2108,7 @@ SelectJoinStep using(Field<?>... fields);
 // The example, continued:
 create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .from(T_AUTHOR)
-      .join(T_BOOK).on(TBook.AUTHOR_ID.equal(TAuthor.ID));
+ .join(T_BOOK).on(TBook.AUTHOR_ID.equal(TAuthor.ID));

See the section about @@ -2171,14 +2118,14 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count()) re-iterate and add another JOIN clause, just like in SQL. Or we go on to the next step, adding conditions in the :

-
+							
 SelectConditionStep where(Condition... conditions);
 
 // The example, continued:
 create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .from(T_AUTHOR)
       .join(T_BOOK).on(TBook.AUTHOR_ID.equal(TAuthor.ID))
-      .where(TBook.LANGUAGE.equal("DE"));
+ .where(TBook.LANGUAGE.equal("DE"));

Now the returned type is a special one, where @@ -2188,7 +2135,7 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count()) unlimited. Note that of course you can also just add a single combined condition, if that is more readable or suitable for your use-case. Here's how we add another condition:

-
+							
 SelectConditionStep and(Condition condition);
 
 // The example, continued:
@@ -2196,13 +2143,13 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .from(T_AUTHOR)
       .join(T_BOOK).on(TBook.AUTHOR_ID.equal(TAuthor.ID))
       .where(TBook.LANGUAGE.equal("DE"))
-      .and(TBook.PUBLISHED.greaterThan(parseDate('2008-01-01')));
+ .and(TBook.PUBLISHED.greaterThan(parseDate('2008-01-01')));

Let's assume we have that method parseDate() creating a for us. Then we'll continue adding the GROUP BY clause

-
+							
 SelectHavingStep groupBy(Field<?>... fields);
 
 // The example, continued:
@@ -2211,10 +2158,10 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .join(T_BOOK).on(TBook.AUTHOR_ID.equal(TAuthor.ID))
       .where(TBook.LANGUAGE.equal("DE"))
       .and(TBook.PUBLISHED.greaterThan(parseDate('2008-01-01')))
-      .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME);
+ .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME);

and the HAVING clause:

-
+      						
 SelectOrderByStep having(Condition... conditions);
 
 // The example, continued:
@@ -2224,13 +2171,13 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .where(TBook.LANGUAGE.equal("DE"))
       .and(TBook.PUBLISHED.greaterThan(parseDate('2008-01-01')))
       .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
-      .having(create.count().greaterThan(5));
+ .having(create.count().greaterThan(5));

and the ORDER BY clause. Some RDBMS support NULLS FIRST and NULLS LAST extensions to the ORDER BY clause. If this is not supported by the RDBMS, then the behaviour is simulated with an additional CASE WHEN ... IS NULL THEN 1 ELSE 0 END clause.

-
+							
 SelectLimitStep orderBy(Field<?>... fields);
 
 // The example, continued:
@@ -2241,7 +2188,7 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .and(TBook.PUBLISHED.greaterThan(parseDate('2008-01-01')))
       .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
       .having(create.count().greaterThan(5))
-      .orderBy(TAuthor.LAST_NAME.asc().nullsFirst());
+ .orderBy(TAuthor.LAST_NAME.asc().nullsFirst());

and finally the LIMIT clause. Most dialects have a means of limiting the number of result records (except Oracle). Some even support having @@ -2250,7 +2197,7 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count()) will simulate the LIMIT clause using nested selects and filtering on ROWNUM (for Oracle), or on ROW_NUMBER() (for DB2 and SQL Server):

-
+							
 SelectFinalStep limit(int offset, int numberOfRows);
 
 // The example, continued:
@@ -2262,13 +2209,13 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME)
       .having(create.count().greaterThan(5))
       .orderBy(TAuthor.LAST_NAME.asc().nullsFirst())
-      .limit(1, 2);
+ .limit(1, 2);

In the final step, there are some proprietary extensions available only in some RDBMS. One of those extensions are the FOR UPDATE (supported in most RDBMS) and FOR SHARE clauses (supported only in MySQL and Postgres):

-
+							
 SelectFinalStep forUpdate();
 
 // The example, continued:
@@ -2281,7 +2228,7 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count())
       .having(create.count().greaterThan(5))
       .orderBy(TAuthor.LAST_NAME.asc().nullsFirst())
       .limit(1, 2)
-      .forUpdate();
+ .forUpdate();

Now the most relevant super-type of the object we have just created is @@ -2294,7 +2241,7 @@ create.select(TAuthor.FIRST_NAME, TAuthor.LAST_NAME, create.count()) section about the :

-
+							
 // Just execute the query.
 int execute() throws SQLException;
 
@@ -2308,7 +2255,7 @@ Record fetchAny() throws SQLException;
 // An Exception is thrown if more records were available
 Record fetchOne() throws SQLException;
 
-// [...]
+// [...]

SELECT from single physical tables

@@ -2320,16 +2267,16 @@ Record fetchOne() throws SQLException; already made at the very first step, when you create the SELECT statement with the Factory:

-
public <R extends Record> SimpleSelectWhereStep<R> selectFrom(Table<R> table);
+ public <R extends Record> SimpleSelectWhereStep<R> selectFrom(Table<R> 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:

-
+							
 TBook book = create.selectFrom(T_BOOK)
                    .where(TBook.LANGUAGE.equal("DE"))
                    .orderBy(TBook.TITLE)
-                   .fetchAny();
+ .fetchAny(); @@ -2352,7 +2299,7 @@ TBook book = create.selectFrom(T_BOOK) which is typically a participant of a condition, and the itself:

-
+							
 public interface Condition {
     Condition and(Condition other);
     Condition and(String sql);
@@ -2367,7 +2314,7 @@ public interface Condition {
     Condition orExists(Select<?> select);
     Condition orNotExists(Select<?> select);
     Condition not();
-}
+}

The above example describes the essence of boolean logic in jOOQ. As soon as you have a Condition object, you can connect that to other @@ -2378,7 +2325,7 @@ public interface Condition { cases. Here are some important API elements in the Field interface:

-
+							
 public interface Field<T> {
     Condition isNull();
     Condition isNotNull();
@@ -2407,7 +2354,7 @@ public interface Field<T> {
     Condition lessOrEqual(T value);
     Condition greaterThan(T value);
     Condition greaterOrEqual(T value);
-}
+}

As you see in the partially displayed API above, you can compare a Field either with other Fields, with constant values (which is a @@ -2416,16 +2363,16 @@ public interface Field<T> { .

Combining the API of Field and Condition you can express complex predicates like this:

-
+							
 (T_BOOK.TYPE_CODE IN (1, 2, 5, 8, 13, 21)       AND T_BOOK.LANGUAGE = 'DE') OR
 (T_BOOK.TYPE_CODE IN (2, 3, 5, 7, 11, 13)       AND T_BOOK.LANGUAGE = 'FR') OR
-(T_BOOK.TYPE_CODE IN (SELECT CODE FROM T_TYPES) AND T_BOOK.LANGUAGE = 'EN')
+(T_BOOK.TYPE_CODE IN (SELECT CODE FROM T_TYPES) AND T_BOOK.LANGUAGE = 'EN')

Just write:

-
+							
 TBook.TYPE_CODE.in(1, 2, 5, 8, 13, 21)                      .and(TBook.LANGUAGE.equal("DE")).or(
 TBook.TYPE_CODE.in(2, 3, 5, 7, 11, 13)                      .and(TBook.LANGUAGE.equal("FR")).or(
-TBook.TYPE_CODE.in(create.select(TTypes.CODE).from(T_TYPES)).and(TBook.LANGUAGE.equal("EN"))));
+TBook.TYPE_CODE.in(create.select(TTypes.CODE).from(T_TYPES)).and(TBook.LANGUAGE.equal("EN")))); @@ -2440,24 +2387,24 @@ TBook.TYPE_CODE.in(create.select(TTypes.CODE).from(T_TYPES)).and(TBook.LANGUAGE.

Aliasing Tables

A typical example of what you might want to do in SQL is this:

-
+							
 SELECT a.ID, b.ID
   FROM T_AUTHOR a
-  JOIN T_BOOK b on a.ID = b.AUTHOR_ID
+ JOIN T_BOOK b on a.ID = b.AUTHOR_ID

In this example, we are aliasing Tables, calling them a and b. Here is how you can create Table aliases in jOOQ:

-
+  							
 Table<TBookRecord> book = T_BOOK.as("b");
-Table<TAuthorRecord> author = T_AUTHOR.as("a");
+Table<TAuthorRecord> author = T_AUTHOR.as("a");

Now, if you want to reference any fields from those Tables, you may not use the original T_BOOK or T_AUTHOR meta-model objects anymore. Instead, you have to get the fields from the new book and author Table aliases:

-
+							
 Field<Integer> bookID = book.getField(TBook.ID);
-Field<Integer> authorID = author.getField(TAuthor.ID);
+Field<Integer> authorID = author.getField(TAuthor.ID);

Unfortunately, this tends to be a bit verbose. The static meta-model @@ -2466,10 +2413,10 @@ Field<Integer> authorID = author.getField(TAuthor.ID); . For now, this is how the above SQL statement would read in jOOQ:

-
+							
 create.select(authorID, bookID)
       .from(author)
-      .join(book).on(authorID.equal(book.getField(TBook.AUTHOR_ID)));
+ .join(book).on(authorID.equal(book.getField(TBook.AUTHOR_ID)));

Aliasing nested selects as tables

@@ -2482,23 +2429,23 @@ create.select(authorID, bookID)

Fields can also be aliased independently from Tables. Most often, this is done when using functions or aggregate operators. Here is an example:

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

Here is how it's done with jOOQ:

-
+							
 Record record = create.select(
          TAuthor.FIRST_NAME.concat(" ", TAuthor.LAST_NAME).as("author"),
          create.count().as("books"))
       .from(T_AUTHOR)
       .join(T_BOOK).on(TAuthor.ID.equal(TBook.AUTHOR_ID))
-      .groupBy(TAuthor.FIRST_NAME, TAuthor.LAST_NAME).fetchAny();
+ .groupBy(TAuthor.FIRST_NAME, TAuthor.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"));
+System.out.println("Books : " + record.getValue("books"));
@@ -2510,7 +2457,7 @@ System.out.println("Books : " + record.getValue("books")); records. The IN operator can also be used for semi-joins, though -

The IN operator for use in semi-joins

+

The IN operator for use in semi-joins or anti-joins

In addition to a list of constant values, the IN operator in @@ -2527,9 +2474,7 @@ System.out.println("Books : " + record.getValue("books")); course, this is possible with a plain JOIN as well, but let's say we want to use the IN operator. Then you have two possibilities:

- - - - - -
+							
 SELECT *
   FROM T_BOOK
  WHERE T_BOOK.AUTHOR_ID IN (
@@ -2541,8 +2486,7 @@ SELECT *
 SELECT T_BOOK.*
   FROM T_BOOK
   JOIN T_AUTHOR ON (T_BOOK.AUTHOR_ID = T_AUTHOR.ID
-                AND T_AUTHOR.BORN    = 1920)
+                AND T_AUTHOR.BORN    = 1920)
 create.select()
       .from(T_BOOK)
       .where(TBook.AUTHOR_ID.in(
@@ -2554,15 +2498,79 @@ create.select()
 create.select(T_BOOK.getFields())
       .from(T_BOOK)
       .join(T_AUTHOR).on(TBook.AUTHOR_ID.equal(TAuthor.ID)
-                     .and(TAuthor.BORN.equal(1920)));
+ .and(TAuthor.BORN.equal(1920)));
Nested SELECT using the EXISTS operator + The EXISTS operator is a bit different from all other SQL + elements, as it cannot really be applied to any object in a DSL. + + +

The EXISTS operator for use in semi-joins or anti-joins

+

The EXISTS operator is rather independent and can stand any place + where there may be a new condition:

+
    +
  • It may be placed right after a WHERE keyword
  • +
  • It may be the right-hand-side of a boolean operator
  • +
  • It may be placed right after a ON or HAVING keyword (although, this is less likely to be done...)
  • +
+ +

This is reflected by the fact that an EXISTS clause is usually + created directly from the Factory. While this is more verbose than all + the other SQL constructs, there is no other way, when static QueryPart + creation is not an option (for now). Here is how it's done in the + Factory:

+ + +Condition exists(Select<?> query); +Condition notExists(Select<?> query); + +

When you create such a Condition, it can then be connected to any + other condition using AND, OR operators (see also the manual's section + on + ). Because of this verbosity, there are also quite a few + convenience methods, where they might be useful. For instance in the + itself:

+ + +Condition andExists(Select<?> select); +Condition andNotExists(Select<?> select); +Condition orExists(Select<?> select); +Condition orNotExists(Select<?> select); + +

Or in the :

+ + +SelectConditionStep whereExists(Select<?> select); +SelectConditionStep whereNotExists(Select<?> select); + +

Or in the :

+ + +SelectConditionStep andExists(Select<?> select); +SelectConditionStep andNotExists(Select<?> select); +SelectConditionStep orExists(Select<?> select); +SelectConditionStep orNotExists(Select<?> select); + +

An example of how to use it is quickly given. Get all authors that haven't written any books:

+ + +SELECT * + FROM T_AUTHOR + WHERE NOT EXISTS (SELECT 1 + FROM T_BOOK + WHERE T_BOOK.AUTHOR_ID = T_AUTHOR.ID) + +create.select() + .from(T_AUTHOR) + .whereNotExists(create.select(1) + .from(T_BOOK) + .where(TBook.AUTHOR_ID.equal(T_AUTHOR.ID))); + +