Skip to content

Commit b60cf0d

Browse files
committed
Java/jOOQ: Add basic demo application
Demo application using CrateDB with jOOQ and the PostgreSQL JDBC driver.
1 parent 64a073c commit b60cf0d

File tree

10 files changed

+473
-0
lines changed

10 files changed

+473
-0
lines changed

by-language/java-jooq/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.gradle/
2+
.idea
3+
*.iws
4+
*.iml
5+
*.ipr
6+
build/
7+
out/
8+
target/

by-language/java-jooq/.java-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
17

by-language/java-jooq/README.rst

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
.. highlight:: sh
2+
3+
############################################################
4+
Java jOOQ demo application for CrateDB using PostgreSQL JDBC
5+
############################################################
6+
7+
*****
8+
About
9+
*****
10+
11+
A demo application using `CrateDB`_ with `jOOQ`_ and the `PostgreSQL
12+
JDBC driver`_.
13+
14+
It is intended as a basic example to demonstrate what currently works, and as a
15+
testing rig for eventually growing a full-fledged CrateDB dialect, or at least
16+
making the code generator work. Contributions are welcome.
17+
18+
19+
Introduction
20+
============
21+
22+
The idea of jOOQ is to generate typesafe code based on the SQL schema.
23+
Then, accessing a database table using the jOOQ DSL API looks like this:
24+
25+
.. code-block:: java
26+
27+
// Fetch records, with filtering and sorting.
28+
Result<Record> result = db.select()
29+
.from(AUTHOR)
30+
.where(AUTHOR.NAME.like("Ja%"))
31+
.orderBy(AUTHOR.NAME)
32+
.fetch();
33+
34+
In some kind, jOOQ is similar to `LINQ`_, `but better <Insight into Language
35+
Integrated Querying_>`_.
36+
37+
38+
Details
39+
=======
40+
41+
The code example will demonstrate a few of the `different use cases for jOOQ`_.
42+
That is, `Dynamic SQL`_, the `jOOQ DSL API`_, and how to use `jOOQ as a SQL
43+
builder without code generation`_.
44+
45+
46+
Caveats
47+
=======
48+
49+
- `jOOQ's code generator`_ takes your database schema and reverse-engineers it
50+
into a set of Java classes. This feature currently does not work with CrateDB
51+
yet. The code provided within the ``src/generated`` directory has not been
52+
derived by reflecting the database schema from CrateDB.
53+
54+
- Most of the jOOQ examples use uppercase letters for the database, table, and
55+
field names. CrateDB currently only handles lowercase letters.
56+
57+
58+
*****
59+
Usage
60+
*****
61+
62+
1. Make sure `Java 17`_ is installed.
63+
2. Run CrateDB::
64+
65+
docker run -it --rm --publish=4200:4200 --publish=5432:5432 \
66+
crate:latest -Cdiscovery.type=single-node
67+
68+
3. Invoke demo application::
69+
70+
./gradlew run
71+
72+
3. Invoke software tests::
73+
74+
./gradlew test
75+
76+
77+
.. _CrateDB: https://github.com/crate/crate
78+
.. _Different use cases for jOOQ: https://www.jooq.org/doc/latest/manual/getting-started/use-cases/
79+
.. _Dynamic SQL: https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/
80+
.. _Gradle: https://gradle.org/
81+
.. _Insight into Language Integrated Querying: https://blog.jooq.org/jooq-tuesdays-ming-yee-iu-gives-insight-into-language-integrated-querying/
82+
.. _Java 17: https://adoptium.net/temurin/releases/?version=17
83+
.. _jOOQ: https://github.com/jOOQ/jOOQ
84+
.. _jOOQ as a SQL builder without code generation: https://www.jooq.org/doc/latest/manual/getting-started/use-cases/jooq-as-a-sql-builder-without-codegeneration/
85+
.. _jOOQ's code generator: https://www.jooq.org/doc/latest/manual/code-generation/
86+
.. _jOOQ DSL API: https://www.jooq.org/doc/latest/manual/sql-building/dsl-api/
87+
.. _LINQ: https://en.wikipedia.org/wiki/Language_Integrated_Query
88+
.. _PostgreSQL JDBC Driver: https://github.com/pgjdbc/pgjdbc

by-language/java-jooq/build.gradle

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* A demo application using CrateDB with jOOQ and the PostgreSQL JDBC driver.
3+
*/
4+
5+
buildscript {
6+
repositories {
7+
mavenCentral()
8+
}
9+
}
10+
11+
plugins {
12+
id 'application'
13+
id 'com.adarshr.test-logger' version '3.2.0'
14+
id 'idea'
15+
id 'java'
16+
}
17+
18+
repositories {
19+
mavenCentral()
20+
mavenLocal()
21+
}
22+
23+
dependencies {
24+
implementation 'org.jooq:jooq:3.17.7'
25+
implementation 'org.postgresql:postgresql:42.5.1'
26+
implementation 'org.slf4j:slf4j-api:2.0.6'
27+
implementation 'org.slf4j:slf4j-simple:2.0.6'
28+
testImplementation 'junit:junit:4.13.2'
29+
}
30+
31+
java {
32+
toolchain {
33+
languageVersion = JavaLanguageVersion.of(17)
34+
}
35+
}
36+
37+
jar {
38+
archiveBaseName = 'cratedb-demo-java-jooq'
39+
archiveVersion = '0.0.1-SNAPSHOT'
40+
}
41+
42+
application {
43+
mainClass = 'io.crate.demo.jooq.Application'
44+
}
45+
46+
sourceSets {
47+
main {
48+
java.srcDirs += [
49+
"src/generated/java",
50+
"src/main/java",
51+
]
52+
}
53+
}
54+
55+
test {
56+
dependsOn 'cleanTest'
57+
}
58+
59+
idea.module.inheritOutputDirs = true
60+
processResources.destinationDir = compileJava.destinationDir
61+
compileJava.dependsOn processResources
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package io.crate.demo.jooq;
2+
3+
import org.jooq.*;
4+
import org.jooq.Record;
5+
import org.jooq.conf.Settings;
6+
import org.jooq.impl.DSL;
7+
8+
import java.io.IOException;
9+
import java.sql.Connection;
10+
import java.sql.DriverManager;
11+
import java.sql.SQLException;
12+
import java.util.Properties;
13+
14+
import io.crate.demo.jooq.model.tables.records.AuthorRecord;
15+
16+
import static io.crate.demo.jooq.model.Tables.*;
17+
import static org.jooq.impl.DSL.field;
18+
import static org.jooq.impl.DSL.table;
19+
20+
21+
/**
22+
* A demo application using CrateDB with jOOQ and the PostgreSQL JDBC driver.
23+
*
24+
* - https://github.com/crate/crate
25+
* - https://github.com/jOOQ/jOOQ
26+
* - https://github.com/pgjdbc/pgjdbc
27+
*/
28+
public class Application {
29+
30+
public static void main(String[] args) throws IOException, SQLException {
31+
Application app = new Application();
32+
33+
Tools.title("Example with generated code");
34+
app.exampleWithGeneratedCode();
35+
36+
Tools.title("Example with dynamic schema");
37+
app.exampleWithDynamicSchema();
38+
39+
System.out.println("Ready.");
40+
}
41+
42+
/**
43+
* Create a new jOOQ DefaultDSLContext instance, wrapping the database connection.
44+
*
45+
* It will use database connection settings from the `application.properties` file,
46+
* and will also enable SQL command logging for demonstration purposes.
47+
*/
48+
public DSLContext getDSLContext() throws SQLException {
49+
50+
// Disable the jOOQ self-ad/banner and its tip of the day.
51+
System.setProperty("org.jooq.no-logo", "true");
52+
System.setProperty("org.jooq.no-tips", "true");
53+
54+
// Read settings from `application.properties` file.
55+
Properties app_settings = Tools.readSettingsFile("application.properties");
56+
57+
// Read database settings.
58+
String DS_URL = app_settings.getProperty("application.datasource.url");
59+
String DS_USERNAME = app_settings.getProperty("application.datasource.username");
60+
String DS_PASSWORD = app_settings.getProperty("application.datasource.password");
61+
62+
// Connect to the database, with given settings and parameters, and select the PostgreSQL dialect.
63+
Settings db_settings = new Settings();
64+
db_settings.setExecuteLogging(true);
65+
Connection connection = DriverManager.getConnection(DS_URL, DS_USERNAME, DS_PASSWORD);
66+
return DSL.using(connection, SQLDialect.POSTGRES, db_settings);
67+
}
68+
69+
/**
70+
* jOOQ as a SQL builder with code generation [1]
71+
*
72+
* > Use jOOQ's code generation features in order to compile your SQL
73+
* > statements using a Java compiler against an actual database schema.
74+
* >
75+
* > This adds a lot of power and expressiveness to just simply
76+
* > constructing SQL using the query DSL and custom strings and
77+
* > literals, as you can be sure that all database artefacts actually
78+
* > exist in the database, and that their type is correct.
79+
* > We strongly recommend using this approach.
80+
*
81+
* [1] https://www.jooq.org/doc/latest/manual/getting-started/use-cases/jooq-as-a-sql-builder-with-code-generation/
82+
*
83+
* TODO: Code generation is currently not possible with CrateDB, because,
84+
* with the PostgreSQL dialect, jOOQ issues a CTE using the
85+
* `WITH RECURSIVE` directive to reflect the database schema.
86+
* For this example, the "generated" code has been written manually.
87+
*
88+
*/
89+
public void exampleWithGeneratedCode() throws IOException, SQLException {
90+
91+
DSLContext db = getDSLContext();
92+
93+
// Create table.
94+
String bootstrap_sql = Tools.readTextFile("bootstrap.sql");
95+
db.query(bootstrap_sql).execute();
96+
97+
// Truncate table.
98+
db.delete(AUTHOR).where(DSL.trueCondition()).execute();
99+
db.query(String.format("REFRESH TABLE %s", AUTHOR)).execute();
100+
101+
// Insert records.
102+
InsertSetMoreStep<AuthorRecord> new_record1 = db.insertInto(AUTHOR).set(AUTHOR.ID, 1).set(AUTHOR.NAME, "John Doe");
103+
InsertSetMoreStep<AuthorRecord> new_record2 = db.insertInto(AUTHOR).set(AUTHOR.ID, 2).set(AUTHOR.NAME, "Jane Doe");
104+
InsertSetMoreStep<AuthorRecord> new_record3 = db.insertInto(AUTHOR).set(AUTHOR.ID, 3).set(AUTHOR.NAME, "Jack Black");
105+
new_record1.execute();
106+
new_record2.execute();
107+
new_record3.execute();
108+
db.query(String.format("REFRESH TABLE %s", AUTHOR)).execute();
109+
110+
// Fetch records, with filtering and sorting.
111+
Result<Record> result = db.select()
112+
.from(AUTHOR)
113+
.where(AUTHOR.NAME.like("Ja%"))
114+
.orderBy(AUTHOR.NAME)
115+
.fetch();
116+
117+
// Display result.
118+
// System.out.println("Result:");
119+
// System.out.println(result);
120+
121+
// Iterate and display records.
122+
System.out.println("By record:");
123+
for (Record record : result) {
124+
Integer id = record.getValue(AUTHOR.ID);
125+
String name = record.getValue(AUTHOR.NAME);
126+
System.out.println("id: " + id + ", name: " + name);
127+
}
128+
System.out.println();
129+
130+
}
131+
132+
/**
133+
* jOOQ as a standalone SQL builder without code generation [1]
134+
*
135+
* If you have a dynamic schema, you don't have to use the code generator.
136+
* This is the simplest of all use cases, allowing for construction of
137+
* valid SQL for any database. In this use case, you will not use jOOQ's
138+
* code generator and maybe not even jOOQ's query execution facilities.
139+
*
140+
* Instead, you'll use jOOQ's query DSL API to wrap strings, literals and
141+
* other user-defined objects into an object-oriented, type-safe AST
142+
* modelling your SQL statements.
143+
*
144+
* [1] https://www.jooq.org/doc/latest/manual/getting-started/use-cases/jooq-as-a-sql-builder-without-codegeneration/
145+
*
146+
*/
147+
public void exampleWithDynamicSchema() throws IOException, SQLException {
148+
149+
DSLContext db = getDSLContext();
150+
151+
Table<Record> BOOK = table("\"testdrive\".\"book\"");
152+
Field<Object> BOOK_ID = field("id");
153+
Field<Object> BOOK_TITLE = field("title");
154+
155+
// Create table.
156+
String bootstrap_sql = Tools.readTextFile("bootstrap.sql");
157+
db.query(bootstrap_sql).execute();
158+
159+
// Truncate table.
160+
db.delete(BOOK).where(DSL.trueCondition()).execute();
161+
db.query(String.format("REFRESH TABLE %s", BOOK)).execute();
162+
163+
// Insert records.
164+
InsertSetMoreStep<Record> new_record1 = db.insertInto(BOOK).set(BOOK_ID, 1).set(BOOK_TITLE, "Foo");
165+
InsertSetMoreStep<Record> new_record2 = db.insertInto(BOOK).set(BOOK_ID, 2).set(BOOK_TITLE, "Bar");
166+
new_record1.execute();
167+
new_record2.execute();
168+
db.query(String.format("REFRESH TABLE %s", BOOK)).execute();
169+
170+
// Fetch records, with filtering and sorting.
171+
Result<Record> result = db.select()
172+
.from(BOOK)
173+
.where(BOOK_TITLE.like("B%"))
174+
.orderBy(BOOK_TITLE)
175+
.fetch();
176+
177+
// Display result.
178+
// System.out.println("Result:");
179+
// System.out.println(result);
180+
181+
// Iterate and display records.
182+
System.out.println("By record:");
183+
for (Record record : result) {
184+
// TODO: How can we know about the index positions of the corresponding columns?
185+
Integer id = (Integer) record.getValue(0);
186+
String title = (String) record.getValue(1);
187+
System.out.println("id: " + id + ", title: " + title);
188+
}
189+
System.out.println();
190+
191+
}
192+
193+
}

0 commit comments

Comments
 (0)