Skip to content

Commit

Permalink
docs: EXPOSED-445 Add documentation for DSL & DAO composite primary keys
Browse files Browse the repository at this point in the history
- Refactor hi.tree to use empty wrapper element for tables
- Separate code snippets from generated SQL blocks
- Replace singular bullet points with <tip> element
- Replace options lists with <deflist> element
- Add link for infix notation
  • Loading branch information
bog-walk committed Jul 22, 2024
1 parent d00af5c commit aecabee
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 53 deletions.
3 changes: 2 additions & 1 deletion documentation-website/Writerside/hi.tree
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
<toc-element topic="Getting-Started-with-Exposed.md"/>
<toc-element topic="Exposed-Modules.md"/>
<toc-element topic="Database-and-DataSource.md"/>
<toc-element topic="Table-Definition.md">
<toc-element toc-title="Working with Tables">
<toc-element topic="Table-Definition.md"/>
<toc-element topic="Data-Types.md"/>
<toc-element topic="SQL-Functions.md"/>
</toc-element>
Expand Down
24 changes: 14 additions & 10 deletions documentation-website/Writerside/topics/Deep-Dive-into-DAO.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ class StarWarsFilm(id: EntityID<Int>) : IntEntity(id) {

## Table types
In addition to `IntIdTable`, the following `IdTable` subclasses are available:
* `LongIdTable` - `Long` id column
* `UIntIdTable` - `UInt` id column
* `ULongIdTable` - `ULong` id column
* `UUIDTable` - `UUID` id column
* `CompositeIdTable` - multiple columns make up the table id

<deflist type="medium">
<def title="LongIdTable"><code>Long</code> id column</def>
<def title="UIntIdTable"><code>UInt</code> id column</def>
<def title="ULongIdTable"><code>ULong</code> id column</def>
<def title="UUIDTable"><code>UUID</code> id column</def>
<def title="CompositeIdTable">Multiple columns make up the table id</def>
</deflist>

To define a custom column type as the primary key and id, use a typed `IdTable` directly and override the `id` column:
```kotlin
Expand Down Expand Up @@ -114,7 +117,7 @@ val movies = StarWarsFilm.all()
val movies = StarWarsFilm.find { StarWarsFilms.sequelId eq 8 }
val movie = StarWarsFilm.findById(5)
```
* For a list of available predicates, see [DSL Where expression](Deep-Dive-into-DSL.md#where-expression).
<tip>For a list of available predicates, see <a href="Deep-Dive-into-DSL.md#where-expression">DSL Where expression</a>.</tip>

Read a value from a property similar to any property in a Kotlin class:
```kotlin
Expand Down Expand Up @@ -284,7 +287,7 @@ class User(id: EntityID<Int>) : IntEntity(id) {
}
```

Without using the `infix` call, the `orderBy` method is chained after `referrersOn`:
Without using the [infix notation](https://kotlinlang.org/docs/functions.html#infix-notation), the `orderBy` method is chained after `referrersOn`:

```kotlin
class User(id: EntityID<Int>) : IntEntity(id) {
Expand Down Expand Up @@ -404,7 +407,7 @@ class StarWarsFilm(id: EntityID<Int>) : IntEntity(id) {
var director by Director referencedOn StarWarsFilms
}
```
* For more information on creating table foreign key constraints, see [DSL Foreign Key](Table-Definition.md#foreign-key).
<tip>For more information on creating table foreign key constraints, see <a href="Table-Definition.md#foreign-key">DSL Foreign Key</a>.</tip>

Now you can get the director for a `StarWarsFilm` object, `movie`, in the same way you would get any other field:
```kotlin
Expand All @@ -422,8 +425,9 @@ You can then access this field on a `Director` object, `director`:
```kotlin
director.films // returns all StarWarsFilm objects that reference this director
```
Using other previously mentioned infix functions, like `optionalReferencedOn`, `backReferencedOn`, and `optionalReferrersOn`,
is also supported for referencing or referenced `CompositeEntity` objects, by using the respective overloads that accept an `IdTable` as an argument.
Using other previously mentioned [infix functions](https://kotlinlang.org/docs/functions.html#infix-notation),
like `optionalReferencedOn`, `backReferencedOn`, and `optionalReferrersOn`, is also supported for referencing or referenced `CompositeEntity` objects,
by using the respective overloads that accept an `IdTable` as an argument.
These overloads will automatically resolve the foreign key constraint associated with the composite primary key.

### Eager Loading
Expand Down
105 changes: 63 additions & 42 deletions documentation-website/Writerside/topics/Table-Definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,47 @@ The most primitive table type is `Table`. It is located in the **org.jetbrains.e
To configure a custom name for a table, which will be used in actual SQL queries, pass it to the `name` parameter of the `Table()` constructor.
Otherwise, Exposed will generate it from the full class name or the class name without the suffix 'Table', if present.

For example, to create a simple table with an integer `id` column and a string `name` column, use any of the following code, depending on the table name of choice:
For example, to create a simple table with an integer `id` column and a string `name` column, use any of the following options:

Omit the `name` parameter to generate the table name from the object name:
```kotlin
// Table name will be taken from object name
object Cities : Table() {
val id = integer("id")
val name = varchar("name", 50)
}
// SQL: CREATE TABLE IF NOT EXISTS CITIES (
// ID INT NOT NULL,
// "name" VARCHAR(50) NOT NULL
// )

// Table name will be taken from object name without 'Table' suffix
```
```sql
CREATE TABLE IF NOT EXISTS CITIES (ID INT NOT NULL, "name" VARCHAR(50) NOT NULL)
```
Omit the `name` parameter to generate the table name from the object name, with any 'Table' suffix removed:
```kotlin
object CitiesTable : Table() {
val id = integer("id")
val name = varchar("name", 50)
}
// SQL: CREATE TABLE IF NOT EXISTS CITIES (
// ID INT NOT NULL,
// "name" VARCHAR(50) NOT NULL
// )

// Table name will be taken from provided argument
```
```sql
CREATE TABLE IF NOT EXISTS CITIES (ID INT NOT NULL, "name" VARCHAR(50) NOT NULL)
```
Provide an argument to `name` to generate a specific table name:
```kotlin
object Cities : Table("all_cities") {
val id = integer("id")
val name = varchar("name", 50)
}
// SQL: CREATE TABLE IF NOT EXISTS ALL_CITIES (
// ID INT NOT NULL,
// "name" VARCHAR(50) NOT NULL
// )

// Some databases, like H2, fold unquoted names to upper case.
// To keep case-sensitivity, manually quote the provided argument
```
```sql
CREATE TABLE IF NOT EXISTS ALL_CITIES (ID INT NOT NULL, "name" VARCHAR(50) NOT NULL)
```
Some databases, like H2, fold unquoted identifiers to upper case. To keep table name case-sensitivity, manually quote the provided argument:
```kotlin
object Cities : Table("\"all_cities\"") {
val id = integer("id")
val name = varchar("name", 50)
}
// SQL: CREATE TABLE IF NOT EXISTS "all_cities" (
// ID INT NOT NULL,
// "name" VARCHAR(50) NOT NULL
// )
```
```sql
CREATE TABLE IF NOT EXISTS "all_cities" (ID INT NOT NULL, "name" VARCHAR(50) NOT NULL)
```

Depending on what DBMS you use, the types of columns could be different in actual SQL queries.
Expand All @@ -68,13 +67,12 @@ For example, the `Cities` table could instead be defined as an `IntIdTable`, whi
object Cities : IntIdTable() {
val name = varchar("name", 50)
}
// SQL: CREATE TABLE IF NOT EXISTS CITIES (
// ID INT AUTO_INCREMENT PRIMARY KEY,
// "name" VARCHAR(50) NOT NULL
// )
```
```sql
CREATE TABLE IF NOT EXISTS CITIES (ID INT AUTO_INCREMENT PRIMARY KEY, "name" VARCHAR(50) NOT NULL)
```

* For more information on `IdTable` types, see [DAO Table Types](Deep-Dive-into-DAO.md#table-types).
<tip>For more information on <code>IdTable</code> types, see <a href="Deep-Dive-into-DAO.md#table-types">DAO Table Types</a>.</tip>

## Constraints

Expand Down Expand Up @@ -200,15 +198,19 @@ of `NOT NULL` and `UNIQUE` constraints. To change the column set, add columns,
For example, to define the `name` column as the primary key, use the following code. The "Cities_name" string
will be used as the constraint name in the actual SQL query, if provided; otherwise a name will be generated based on the table's name.
```kotlin
// SQL: CONSTRAINT Cities_name PRIMARY KEY ("name")
override val primaryKey = PrimaryKey(name, name = "Cities_name")
```
```sql
CONSTRAINT Cities_name PRIMARY KEY ("name")
```

It is also possible to define a primary key on a table using multiple columns:
```kotlin
// SQL: CONSTRAINT pk_Cities PRIMARY KEY (ID, "name")
override val primaryKey = PrimaryKey(id, name)
```
```sql
CONSTRAINT pk_Cities PRIMARY KEY (ID, "name")
```

Except for `CompositeIdTable`, each available class in Exposed that inherits from `IdTable` has the `primaryKey` field automatically defined.
For example, the `IntIdTable` by default has an auto-incrementing integer column, `id`, which is defined as the primary key.
Expand All @@ -225,6 +227,7 @@ object Towns : CompositeIdTable("towns") {
override val primaryKey = PrimaryKey(areaCode, latitude, longitude)
}
```
<tip>For more information on <code>CompositeIdTable</code> types, see <a href="Deep-Dive-into-DAO.md#table-types">DAO Table Types</a>.</tip>

### Foreign Key

Expand All @@ -235,19 +238,37 @@ use `foreignKey()` directly within an `init` block.

`reference()` and `optReference()` methods have several parameters:

* `name: String` is a name for the foreign key column, which will be used in actual SQL queries.
* `ref: Column<T>` is a target column from another parent table.
* `onDelete: ReferenceOption? = null` is an action for when a linked row from a parent table will be deleted.
* `onUpdate: ReferenceOption? = null` is an action for when a value in a referenced column will be changed.
* `fkName: String? = null` is a name for the foreign key constraint.
`name: String`
: A name for the foreign key column, which will be used in actual SQL queries.

`ref: Column<T>`
: A target column from another parent table.

`onDelete: ReferenceOption? = null`
: An action for when a linked row from a parent table will be deleted.

`onUpdate: ReferenceOption? = null`
: An action for when a value in a referenced column will be changed.

`fkName: String? = null`
: A name for the foreign key constraint.

Enum class `ReferenceOption` has five values:

* `RESTRICT` is an option that restricts changes on a referenced column, and the default option for most dialects.
* `NO_ACTION` is the same as RESTRICT in some, but not all, databases, and the default option for Oracle and SQL Server dialects.
* `CASCADE` is an option that allows updating or deleting the referring rows.
* `SET_NULL` is an option that sets the referring column values to null.
* `SET_DEFAULT` is an option that sets the referring column values to the default value.
`RESTRICT`
: An option that restricts changes on a referenced column, and the default option for most dialects.

`NO_ACTION`
: The same as RESTRICT in some, but not all, databases, and the default option for Oracle and SQL Server dialects.

`CASCADE`
: An option that allows updating or deleting the referring rows.

`SET_NULL`
: An option that sets the referring column values to null.

`SET_DEFAULT`
: An option that sets the referring column values to the default value.

Consider the following `Citizens` table. This table has the `name` and `city` columns. If the `Cities` table has
configured the `name` column as the primary key, the `Citizens` table can refer to it by its `city` column, which is a foreign key. To
Expand Down

0 comments on commit aecabee

Please sign in to comment.