Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions documentation-website/Writerside/hi.tree
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<toc-element topic="DSL-CRUD-operations.topic"/>
<toc-element topic="Working-with-Sequence.topic"/>
<toc-element topic="DSL-Querying-data.topic"/>
<toc-element topic="DSL-Statement-Builder.md"/>
</toc-element>
<toc-element toc-title="Deep Dive into DAO">
<toc-element topic="DAO-Table-Types.topic"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.example

import org.example.examples.AliasExamples
import org.example.examples.BuildStatementExamples
import org.example.examples.CreateExamples
import org.example.examples.CustomSelectExamples
import org.example.examples.DeleteExamples
Expand All @@ -10,6 +11,7 @@ import org.example.examples.UpdateExamples
import org.example.tables.*
import org.jetbrains.exposed.v1.core.DatabaseConfig
import org.jetbrains.exposed.v1.core.StdOutSqlLogger
import org.jetbrains.exposed.v1.core.Transaction
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.SchemaUtils
import org.jetbrains.exposed.v1.jdbc.addLogger
Expand Down Expand Up @@ -44,6 +46,7 @@ fun main() {
runQueryingExamples()
runAliasExamples()
runDeleteExamples()
runBuildStatementExamples()
}

transaction(sqliteDb) {
Expand Down Expand Up @@ -119,3 +122,11 @@ fun runCustomSelectExamples() {
val customSelectExamples = CustomSelectExamples()
customSelectExamples.useCustomQueryWithHint()
}

fun Transaction.runBuildStatementExamples() {
val buildStatementExamples = BuildStatementExamples()
with(buildStatementExamples) {
prepareQuerySql()
prepareInsertSql()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.example.examples

import org.example.tables.StarWarsFilmsTable
import org.jetbrains.exposed.v1.core.Transaction
import org.jetbrains.exposed.v1.core.statements.buildStatement
import org.jetbrains.exposed.v1.jdbc.*

/*
Important: The contents of this file are referenced by line number in `DSL-Statement-Builder.md`.
If you add, remove, or modify any lines, ensure you update the corresponding
line numbers in the `code-block` element of the referenced file.
*/

@Suppress("MagicNumber")
class BuildStatementExamples {
fun Transaction.prepareQuerySql() {
val filmQuery = StarWarsFilmsTable
.selectAll()
.where { StarWarsFilmsTable.sequelId lessEq 3 }

val querySql: String = filmQuery
.orWhere { StarWarsFilmsTable.sequelId greater 6 }
.prepareSQL(this)
println(querySql)

/*
SELECT STARWARSFILMS.ID, STARWARSFILMS.SEQUEL_ID, STARWARSFILMS."name", STARWARSFILMS.DIRECTOR
FROM STARWARSFILMS
WHERE (STARWARSFILMS.SEQUEL_ID <= ?) OR (STARWARSFILMS.SEQUEL_ID > ?)
*/

val fullQuerySql = filmQuery
.orWhere { StarWarsFilmsTable.sequelId greater 6 }
.prepareSQL(this, prepared = false)
println(fullQuerySql)

/*
SELECT STARWARSFILMS.ID, STARWARSFILMS.SEQUEL_ID, STARWARSFILMS."name", STARWARSFILMS.DIRECTOR
FROM STARWARSFILMS
WHERE (STARWARSFILMS.SEQUEL_ID <= 3) OR (STARWARSFILMS.SEQUEL_ID > 6)
*/
}

fun Transaction.prepareInsertSql() {
val insertFilm = buildStatement {
StarWarsFilmsTable.insert {
it[sequelId] = 8
it[name] = "The Last Jedi"
it[director] = "Rian Johnson"
}
}

val preparedSql: String = insertFilm.prepareSQL(this, prepared = true)
println(preparedSql)

/*
INSERT INTO STARWARSFILMS (SEQUEL_ID, "name", DIRECTOR) VALUES (?, ?, ?)
*/

val fullSql: String = insertFilm.prepareSQL(this, prepared = false)
println(fullSql)

/*
INSERT INTO STARWARSFILMS (SEQUEL_ID, "name", DIRECTOR) VALUES (8, 'The Last Jedi', 'Rian Johnson')
*/
}
}
110 changes: 110 additions & 0 deletions documentation-website/Writerside/topics/DSL-Statement-Builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<show-structure for="chapter,procedure" depth="2"/>

# Building SQL Statements

Exposed DSL provides various functions to perform database operations, such as [](DSL-CRUD-operations.topic).
If you need to access the SQL generated by this DSL without executing it,
Exposed provides this functionality through [`Statement.prepareSQL()`](https://jetbrains.github.io/Exposed/api/exposed-core/org.jetbrains.exposed.v1.core.statements/-statement/prepare-s-q-l.html).

The following examples refer to the [`StarWarsFilmsTable`](DSL-Table-Types.topic) defined previously, and any generated
SQL is based on output from the H2 database.

<note>
This functionality still requires a connection and transaction context, since Exposed adapts its statement and query
building dynamically based on the underlying database, primarily for identifier and syntax specifics.
</note>

## Read operations

<tldr>
<p>API References: <a href="https://jetbrains.github.io/Exposed/api/exposed-jdbc/org.jetbrains.exposed.v1.jdbc/-query/index.html"><code>Query</code> (JDBC)</a>,
<a href="https://jetbrains.github.io/Exposed/api/exposed-r2dbc/org.jetbrains.exposed.v1.r2dbc/-query/index.html"><code>Query</code> (R2DBC)</a>
</p>
</tldr>

An instance of a `Query` is not executed until its results are consumed, for example through iteration.
For this reason, queries can be built and stored for later use:

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-symbol="filmQuery"/>

You can then call `.prepareSQL()` to check the SQL string representation of the query that would be sent to the database:

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-symbol="querySql"/>

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-lines="27-29"/>

By default, a parameterized SQL string is prepared and returned. To generate a SQL string without parameter placeholders,
set the `prepared` argument to `false`:

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-symbol="fullQuerySql"/>

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-lines="38-40"/>

## Other operations

When calling a function like [`.insert()`](DSL-CRUD-operations.topic#insert) on a table, Exposed automatically sends the generated SQL
to the database to create a new row. To avoid automatic execution, you can instantiate the underlying statement class directly, namely
[`InsertStatement`](https://jetbrains.github.io/Exposed/api/exposed-core/org.jetbrains.exposed.v1.core.statements/-insert-statement/index.html).

Since version 1.0.0, you can create instances of these underlying statements without automatic execution, by using
the same DSL inside a [`buildStatement {}`](https://jetbrains.github.io/Exposed/api/exposed-core/org.jetbrains.exposed.v1.core.statements/build-statement.html) block:

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-symbol="insertFilm"/>

As for [queries](#read-operations), you can access the SQL string to be executed using `.prepareSQL()`:

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-symbol="preparedSql"/>

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-lines="57"/>

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-symbol="fullSql"/>

<code-block lang="kotlin"
src="exposed-dsl/src/main/kotlin/org/example/examples/BuildStatementExamples.kt"
include-lines="64"/>

### Executing a statement

<tldr>
<p>API References: <a href="https://jetbrains.github.io/Exposed/api/exposed-jdbc/org.jetbrains.exposed.v1.jdbc/-jdbc-transaction/exec.html"><code>exec</code> (JDBC)</a>,
<a href="https://jetbrains.github.io/Exposed/api/exposed-r2dbc/org.jetbrains.exposed.v1.r2dbc/-r2dbc-transaction/exec.html"><code>exec</code> (R2DBC)</a>
</p>
</tldr>

A stored `Statement` can be sent to the database by first passing it to an executable class, either a subclass
of [`BlockingExecutable`](https://jetbrains.github.io/Exposed/api/exposed-jdbc/org.jetbrains.exposed.v1.jdbc.statements/-blocking-executable/index.html) (JDBC)
or [`SuspendExecutable`](https://jetbrains.github.io/Exposed/api/exposed-r2dbc/org.jetbrains.exposed.v1.r2dbc.statements/-suspend-executable/index.html) (R2DBC).
The executable can then be sent to the database using `exec()` in a transaction block.

This can be done manually, if the appropriate class is known or a custom statement or executable class is being used:

```kotlin
exec(InsertBlockingExecutable(insertFilm))
```

Alternatively, if the statement is created using the Exposed API, you can use `Statement.toExecutable()`
([JDBC](https://jetbrains.github.io/Exposed/api/exposed-jdbc/org.jetbrains.exposed.v1.jdbc.statements/to-executable.html),
[R2DBC](https://jetbrains.github.io/Exposed/api/exposed-r2dbc/org.jetbrains.exposed.v1.r2dbc.statements/to-executable.html))
resolve the appropriate executable class instance for the calling statement type:

```kotlin
exec(insertFilm.toExecutable())
```
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,7 @@ necessary even if the query is never executed.

### How do I get a plain SQL query which will be executed?

To get the SQL representation of a query without executing it, use the
[`prepareSQL()`](https://jetbrains.github.io/Exposed/api/exposed-core/org.jetbrains.exposed.v1.core/-abstract-query/prepare-s-q-l.html)
method:

```kotlin
val plainSQL = StarWarsFilmsTable.selectAll()
.where{ StarWarsFilmsTable.sequelId eq ActorsTable.sequelId }
.prepareSQL(QueryBuilder(false))
```
In this example `QueryBuiler` is used with `false` to return a non-parameterized string.
You can use `Statement.prepareSQL()`, and potentially `buildStatement()`. For more details, see [](DSL-Statement-Builder.md).

### Is it possible to update a field relative to current field value?

Expand Down
4 changes: 3 additions & 1 deletion documentation-website/Writerside/topics/Migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ function helps identify columns that are no longer present in your current table
```
{src="exposed-migrations/src/main/kotlin/org/example/App.kt" include-symbol="dropStatements"}

For indices and sequences, you can use the `MigrationUtils.dropUnmappedIndices()` and `MigrationUtils.dropUnmappedSequences()` methods.
For indices and sequences, you can use the [`MigrationUtils.dropUnmappedIndices()`](https://jetbrains.github.io/Exposed/api/exposed-migration/org.jetbrains.exposed.v1.migration/-migration-utils/drop-unmapped-indices.html)
and [`MigrationUtils.dropUnmappedSequences()`](https://jetbrains.github.io/Exposed/api/exposed-migration/org.jetbrains.exposed.v1.migration/-migration-utils/drop-unmapped-sequences.html)
methods.

## Logging

Expand Down
Loading