From 3be1ecaa753dd6f176eb5cab064e3dc50e898b93 Mon Sep 17 00:00:00 2001
From: Vik Nikolova
Date: Thu, 10 Oct 2024 09:22:51 +0200
Subject: [PATCH] Docs: Extend entities definition docs and reference code from
snippets (#2264)
* extend docs for defining entity instances and add links to the CRUD operations topic
* extend exposed-dao code example and add README files
* replace code blocks with references from code snippets and move the entity definition section
* reference code from snippets in the DAO CRUD operations topic
---
documentation-website/Writerside/hi.tree | 1 +
.../Writerside/snippets/README.md | 85 ++++++++
.../Writerside/snippets/exposed-dao/README.md | 25 +++
.../src/main/kotlin/org/example/App.kt | 57 +++++-
.../main/kotlin/org/example/StarWarsFilms.kt | 18 --
.../example/entities/DirectorCustomEntity.kt | 12 ++
.../org/example/entities/DirectorEntity.kt | 13 ++
.../example/entities/StarWarsFilmEntity.kt | 14 ++
.../entities/StarWarsFilmWithRankEntity.kt | 25 +++
.../kotlin/org/example/entities/UserEntity.kt | 12 ++
.../org/example/entities/UserRatingEntity.kt | 14 ++
.../org/example/examples/CreateExamples.kt | 42 ++++
.../org/example/examples/DeleteExamples.kt | 19 ++
.../org/example/examples/ReadExamples.kt | 126 ++++++++++++
.../org/example/examples/UpdateExamples.kt | 33 +++
.../kotlin/org/example/tables/CitiesTable.kt | 9 +
.../example/tables/DirectorsCustomTable.kt | 15 ++
.../org/example/tables/DirectorsTable.kt | 15 ++
.../tables/DirectorsWithGuildRefTable.kt | 17 ++
.../kotlin/org/example/tables/GuildsTable.kt | 5 +
.../org/example/tables/StarWarsFilmsTable.kt | 23 +++
.../tables/StarWarsFilmsWithRankTable.kt | 15 ++
.../org/example/tables/UserRatingsTable.kt | 9 +
.../kotlin/org/example/tables/UsersTable.kt | 10 +
.../topics/DAO-CRUD-Operations.topic | 188 +++++-------------
.../topics/DAO-Entity-definition.topic | 50 +++++
.../Writerside/topics/DAO-Table-Types.topic | 117 +++++------
27 files changed, 738 insertions(+), 231 deletions(-)
create mode 100644 documentation-website/Writerside/snippets/README.md
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/README.md
delete mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/StarWarsFilms.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorCustomEntity.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorEntity.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmEntity.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmWithRankEntity.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserEntity.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserRatingEntity.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/CreateExamples.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/DeleteExamples.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/ReadExamples.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/UpdateExamples.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/CitiesTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsCustomTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsWithGuildRefTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/GuildsTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsWithRankTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UserRatingsTable.kt
create mode 100644 documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UsersTable.kt
create mode 100644 documentation-website/Writerside/topics/DAO-Entity-definition.topic
diff --git a/documentation-website/Writerside/hi.tree b/documentation-website/Writerside/hi.tree
index 2a984611fa..2529375ba8 100644
--- a/documentation-website/Writerside/hi.tree
+++ b/documentation-website/Writerside/hi.tree
@@ -29,6 +29,7 @@
+
diff --git a/documentation-website/Writerside/snippets/README.md b/documentation-website/Writerside/snippets/README.md
new file mode 100644
index 0000000000..32b3f55935
--- /dev/null
+++ b/documentation-website/Writerside/snippets/README.md
@@ -0,0 +1,85 @@
+# Exposed Docs code examples
+
+The `snippets` folder contains a Gradle project with runnable code examples that show how to work with the Exposed library.
+Code from these examples is referenced in corresponding documentation sections.
+
+## Run samples
+
+Each example has its own `README` file with instructions on how to run it.
+To run an example, you can use a **run** Gradle task that depends on an example location.
+For example, to run the `exposed-dao` example, open a new terminal window from the `snippets` folder and execute the following command:
+
+```bash
+./gradlew :exposed-dao:run
+```
+
+Wait until IntelliJ IDEA builds and runs an example.
+
+## Reference code snippets
+
+### Reference files and symbols
+
+To display a specific source file in a topic, use the [`code-block`](https://www.jetbrains.com/help/writerside/semantic-markup-reference.html#code-block)
+element with the `src` attribute. Whenever possible, reference the entire files or use the `include-symbol` attribute
+to specify a class, method, or another symbol from the source file to include in the code block.
+
+
+#### XML
+
+````xml
+
+
+
+
+
+````
+
+#### Markdown
+
+````
+```kotlin
+```
+{src="exposed-dao/src/main/kotlin/org/example/StarWarsFilms.kt"}
+````
+
+### Reference by line numbers
+
+In cases where the example code is not assigned or is commented out, use the `include-lines` attribute to specify the line
+numbers from the source file to include in the code block:
+
+#### XML
+
+```xml
+
+
+```
+#### Markdown
+
+````
+```kotlin
+```
+{src="exposed-dao/src/main/kotlin/org/example/StarWarsFilms.kt" include-lines="9-13"}
+````
+
+When using this approach, be sure to document it for future reference by adding a note about the explicitly referenced
+lines, as shown in the example below:
+
+```kotlin
+/*
+ ...
+
+ Important: The SQL query is referenced by line number in `TOPIC_NAME.topic`.
+ If you add, remove, or modify any lines before the SELECT statement, ensure you update the corresponding
+ line numbers in the `code-block` element of the referenced file.
+
+ CREATE TABLE IF NOT EXISTS STARWARSFILMS
+ (ID INT AUTO_INCREMENT PRIMARY KEY,
+ SEQUEL_ID INT NOT NULL,
+ "name" VARCHAR(50) NOT NULL,
+ DIRECTOR VARCHAR(50) NOT NULL);
+ */
+object StarWarsFilmsTable : IntIdTable() {
+ //...
+}
+```
+
diff --git a/documentation-website/Writerside/snippets/exposed-dao/README.md b/documentation-website/Writerside/snippets/exposed-dao/README.md
new file mode 100644
index 0000000000..9eff5ab5d1
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/README.md
@@ -0,0 +1,25 @@
+# Exposed DAO API examples
+
+A Gradle application that shows how to work with Exposed DAO API.
+The files are referenced in the DAO's [CRUD operations](../../topics/DAO-CRUD-Operations.topic),
+[Table types](../../topics/DAO-Table-Types.topic) and [Entity definition](../../topics/DAO-Entity-definition.topic)
+topics.
+
+## Build
+
+To build the application, in a terminal window navigate to the `snippets` folder and run the following command:
+
+```shell
+./gradlew :exposed-dao:build
+```
+
+## Run
+
+To run the application, in a terminal window navigate to the `snippets` folder and run the following command:
+
+```shell
+./gradlew :exposed-dao:run
+```
+
+This will run queries to create new tables and run all functions in the `/examples` folder.
+To only run a specific example, modify the `App.kt` file and re-run the project.
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/App.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/App.kt
index e7e29adf2a..b8ffb0abb9 100644
--- a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/App.kt
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/App.kt
@@ -1,13 +1,64 @@
package org.example
-import org.jetbrains.exposed.sql.*
+import org.example.examples.*
+import org.example.tables.*
+import org.jetbrains.exposed.sql.Database
+import org.jetbrains.exposed.sql.DatabaseConfig
+import org.jetbrains.exposed.sql.SchemaUtils
+import org.jetbrains.exposed.sql.StdOutSqlLogger
+import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.transactions.transaction
fun main() {
- Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ Database.connect(
+ "jdbc:h2:mem:test",
+ "org.h2.Driver",
+ databaseConfig = DatabaseConfig { useNestedTransactions = true }
+ )
transaction {
addLogger(StdOutSqlLogger)
- SchemaUtils.create(StarWarsFilms)
+ createTables()
+ runCreateExamples()
+ runReadExamples()
+ runUpdateExamples()
+ runDeleteExamples()
}
}
+
+fun createTables() {
+ SchemaUtils.create(StarWarsFilmsTable)
+ SchemaUtils.create(DirectorsTable)
+ SchemaUtils.create(UsersTable)
+ SchemaUtils.create(UserRatingsTable)
+ SchemaUtils.create(GuildsTable)
+ SchemaUtils.create(CitiesTable)
+ SchemaUtils.create(StarWarsFilmsWithRankTable)
+}
+
+fun runCreateExamples() {
+ val createExamples = CreateExamples()
+ createExamples.createFilms()
+ createExamples.createNewWithCompositeId()
+}
+
+fun runReadExamples() {
+ val readExamples = ReadExamples()
+ readExamples.readAll()
+ readExamples.readWithJoin()
+ readExamples.find()
+ readExamples.findByCompositeId()
+ readExamples.queriesAsExpressions()
+ readExamples.readComputedField()
+}
+
+fun runUpdateExamples() {
+ val updateExamples = UpdateExamples()
+ updateExamples.updateFilms()
+ updateExamples.updateFilmProperty()
+}
+
+fun runDeleteExamples() {
+ val deleteExamples = DeleteExamples()
+ deleteExamples.deleteFilm()
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/StarWarsFilms.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/StarWarsFilms.kt
deleted file mode 100644
index f3fcd45232..0000000000
--- a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/StarWarsFilms.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.example
-
-import org.jetbrains.exposed.dao.id.IntIdTable
-
-const val MAX_VARCHAR_LENGTH = 50
-
-/*
-CREATE TABLE IF NOT EXISTS STARWARSFILMS
-(ID INT AUTO_INCREMENT PRIMARY KEY,
-SEQUEL_ID INT NOT NULL,
-"name" VARCHAR(50) NOT NULL,
-DIRECTOR VARCHAR(50) NOT NULL);
-*/
-object StarWarsFilms : IntIdTable() {
- val sequelId = integer("sequel_id").uniqueIndex()
- val name = varchar("name", MAX_VARCHAR_LENGTH)
- val director = varchar("director", MAX_VARCHAR_LENGTH)
-}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorCustomEntity.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorCustomEntity.kt
new file mode 100644
index 0000000000..2070503d6a
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorCustomEntity.kt
@@ -0,0 +1,12 @@
+package org.example.entities
+
+import org.example.tables.DirectorsCustomTable
+import org.jetbrains.exposed.dao.Entity
+import org.jetbrains.exposed.dao.EntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+
+class DirectorCustomEntity(id: EntityID) : Entity(id) {
+ companion object : EntityClass(DirectorsCustomTable)
+
+ var name by DirectorsCustomTable.name
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorEntity.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorEntity.kt
new file mode 100644
index 0000000000..4d858a0f9a
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/DirectorEntity.kt
@@ -0,0 +1,13 @@
+package org.example.entities
+
+import org.example.tables.DirectorsTable
+import org.jetbrains.exposed.dao.CompositeEntity
+import org.jetbrains.exposed.dao.CompositeEntityClass
+import org.jetbrains.exposed.dao.id.CompositeID
+import org.jetbrains.exposed.dao.id.EntityID
+
+class DirectorEntity(id: EntityID) : CompositeEntity(id) {
+ companion object : CompositeEntityClass(DirectorsTable)
+
+ var genre by DirectorsTable.genre
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmEntity.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmEntity.kt
new file mode 100644
index 0000000000..fae6592e43
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmEntity.kt
@@ -0,0 +1,14 @@
+package org.example.entities
+
+import org.example.tables.StarWarsFilmsTable
+import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+
+class StarWarsFilmEntity(id: EntityID) : IntEntity(id) {
+ companion object : IntEntityClass(StarWarsFilmsTable)
+
+ var sequelId by StarWarsFilmsTable.sequelId
+ var name by StarWarsFilmsTable.name
+ var director by StarWarsFilmsTable.director
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmWithRankEntity.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmWithRankEntity.kt
new file mode 100644
index 0000000000..85d9096bbe
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/StarWarsFilmWithRankEntity.kt
@@ -0,0 +1,25 @@
+package org.example.entities
+
+import org.example.tables.StarWarsFilmsWithRankTable
+import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+import org.jetbrains.exposed.sql.Op
+import org.jetbrains.exposed.sql.Query
+
+class StarWarsFilmWithRankEntity(id: EntityID) : IntEntity(id) {
+ var sequelId by StarWarsFilmsWithRankTable.sequelId
+ var name by StarWarsFilmsWithRankTable.name
+ var rating by StarWarsFilmsWithRankTable.rating
+
+ val rank: Long
+ get() = readValues[StarWarsFilmsWithRankTable.rank]
+
+ companion object : IntEntityClass(StarWarsFilmsWithRankTable) {
+ override fun searchQuery(op: Op): Query {
+ return super.searchQuery(op).adjustSelect {
+ select(columns + StarWarsFilmsWithRankTable.rank)
+ }
+ }
+ }
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserEntity.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserEntity.kt
new file mode 100644
index 0000000000..c3f7ede64e
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserEntity.kt
@@ -0,0 +1,12 @@
+package org.example.entities
+
+import org.example.tables.UsersTable
+import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+
+class UserEntity(id: EntityID) : IntEntity(id) {
+ companion object : IntEntityClass(UsersTable)
+
+ var name by UsersTable.name
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserRatingEntity.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserRatingEntity.kt
new file mode 100644
index 0000000000..601c68c6b9
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/entities/UserRatingEntity.kt
@@ -0,0 +1,14 @@
+package org.example.entities
+
+import org.example.tables.UserRatingsTable
+import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+
+class UserRatingEntity(id: EntityID) : IntEntity(id) {
+ companion object : IntEntityClass(UserRatingsTable)
+
+ var value by UserRatingsTable.value
+ var film by StarWarsFilmEntity referencedOn UserRatingsTable.film // use referencedOn for normal references
+ var user by UserEntity referencedOn UserRatingsTable.user
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/CreateExamples.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/CreateExamples.kt
new file mode 100644
index 0000000000..a87711d956
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/CreateExamples.kt
@@ -0,0 +1,42 @@
+package org.example.examples
+
+import org.example.entities.DirectorEntity
+import org.example.entities.StarWarsFilmEntity
+import org.example.tables.DirectorsTable
+import org.example.tables.Genre
+import org.jetbrains.exposed.dao.id.CompositeID
+import java.util.*
+
+const val MOVIE_SEQUEL_ID = 8
+const val MOVIE2_SEQUEL_ID = 9
+
+class CreateExamples {
+ fun createFilms() {
+ val movie = StarWarsFilmEntity.new {
+ name = "The Last Jedi"
+ sequelId = MOVIE_SEQUEL_ID
+ director = "Rian Johnson"
+ }
+ println("Created a new record with name " + movie.name)
+
+ // Create a new record with id
+ val movie2 = StarWarsFilmEntity.new(id = 2) {
+ name = "The Rise of Skywalker"
+ sequelId = MOVIE2_SEQUEL_ID
+ director = "J.J. Abrams"
+ }
+ println("Created a new record with id " + movie2.id)
+ }
+
+ // Create a new record with a composite id
+ fun createNewWithCompositeId() {
+ val directorId = CompositeID {
+ it[DirectorsTable.name] = "J.J. Abrams"
+ it[DirectorsTable.guildId] = UUID.randomUUID()
+ }
+
+ val director = DirectorEntity.new(directorId) {
+ genre = Genre.SCI_FI
+ }
+ }
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/DeleteExamples.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/DeleteExamples.kt
new file mode 100644
index 0000000000..ef7420f92d
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/DeleteExamples.kt
@@ -0,0 +1,19 @@
+package org.example.examples
+
+import org.example.entities.StarWarsFilmEntity
+
+class DeleteExamples {
+/*
+ Delete a record.
+
+ Important: The `movie.delete` statement is referenced by line number in `DAO-CRUD-operations.topic`.
+ If you add, remove, or modify any lines prior to this one, ensure you update the corresponding
+ line numbers in the `code-block` element of the referenced file.
+*/
+ fun deleteFilm() {
+ val movie = StarWarsFilmEntity.findById(2)
+ if (movie != null) {
+ movie.delete()
+ }
+ }
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/ReadExamples.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/ReadExamples.kt
new file mode 100644
index 0000000000..e4670c52e0
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/ReadExamples.kt
@@ -0,0 +1,126 @@
+package org.example.examples
+
+import org.example.entities.*
+import org.example.tables.*
+import org.jetbrains.exposed.dao.id.CompositeID
+import org.jetbrains.exposed.sql.SortOrder
+import org.jetbrains.exposed.sql.and
+import org.jetbrains.exposed.sql.count
+import org.jetbrains.exposed.sql.insert
+import org.jetbrains.exposed.sql.selectAll
+import org.jetbrains.exposed.sql.transactions.transaction
+import org.jetbrains.exposed.sql.wrapAsExpression
+import java.util.*
+
+const val MOVIE_SEQUELID = 8
+const val MIN_MOVIE_RATING = 5
+const val MOVIE_RATING = 4.2
+
+class ReadExamples {
+
+ fun readAll() {
+ // Read all movies
+ val allMovies = StarWarsFilmEntity.all()
+ allMovies.forEach({ println(it.name) })
+
+ // Sort results in ascending order
+ val moviesByAscOrder = StarWarsFilmEntity.all().sortedBy { it.sequelId }
+ moviesByAscOrder.map { println(it.sequelId) }
+
+ // Sort results in descending order
+ val moviesByDescOrder = StarWarsFilmEntity.all().sortedByDescending { it.sequelId }
+ moviesByDescOrder.map { println(it.sequelId) }
+ }
+
+ fun find() {
+ // Get an entity by its id value
+ val movie = StarWarsFilmEntity.findById(2)
+
+ if (movie != null) {
+ // Read a property value
+ val movieName = movie.name
+ println("Created a new movie with name $movieName")
+
+ // Read the id value
+ val movieId: Int = movie.id.value
+ println("The id of the new movie is $movieId")
+ }
+
+ // Read all with a condition
+ val specificMovie = StarWarsFilmEntity.find { StarWarsFilmsTable.sequelId eq MOVIE_SEQUELID }
+ specificMovie.forEach({ println("Found a movie with sequelId " + MOVIE_SEQUELID + " and name " + it.name) })
+ }
+
+ // Read an entity with a join to another table
+ fun readWithJoin() {
+ val query = UsersTable.innerJoin(UserRatingsTable).innerJoin(StarWarsFilmsTable)
+ .select(UsersTable.columns)
+ .where {
+ StarWarsFilmsTable.sequelId eq MOVIE_SEQUELID and (UserRatingsTable.value greater MIN_MOVIE_RATING)
+ }.withDistinct()
+
+ val users = UserEntity.wrapRows(query).toList()
+ users.map { println(it.name) }
+ }
+
+ /*
+ Find records by composite id.
+
+ Important: The SQL query is referenced by line number in `DAO-CRUD-operations.topic`.
+ If you add, remove, or modify any lines before the SELECT statement, ensure you update the corresponding
+ line numbers in the `code-block` element of the referenced file.
+
+ SELECT DIRECTORS."name", DIRECTORS.GUILD_ID, DIRECTORS.GENRE
+ FROM DIRECTORS
+ WHERE (DIRECTORS."name" = 'J.J. Abrams')
+ AND (DIRECTORS.GUILD_ID = '2cc64f4f-1a2c-41ce-bda1-ee492f787f4b')
+ */
+ fun findByCompositeId() {
+ val directorId = CompositeID {
+ it[DirectorsTable.name] = "J.J. Abrams"
+ it[DirectorsTable.guildId] = UUID.randomUUID()
+ }
+
+ DirectorEntity.new(directorId) {
+ genre = Genre.SCI_FI
+ }
+
+ val director = DirectorEntity.findById(directorId)
+ println("Found director $director")
+ val directors = DirectorEntity.find { DirectorsTable.id eq directorId }
+ directors.forEach({ println(it.genre) })
+ }
+
+ fun queriesAsExpressions() {
+ // Use a query as an expression to sort cities by the number of users in each city
+ CitiesTable.insert {
+ it[name] = "Amsterdam"
+ }
+
+ val expression = wrapAsExpression(
+ UsersTable.select(UsersTable.id.count())
+ .where { CitiesTable.id eq UsersTable.cityId }
+ )
+ val cities = CitiesTable.selectAll()
+ .orderBy(expression, SortOrder.DESC)
+ .toList()
+
+ cities.map { println(it[CitiesTable.name]) }
+ }
+
+ fun readComputedField() {
+ transaction {
+ StarWarsFilmWithRankEntity.new {
+ sequelId = MOVIE_SEQUELID
+ name = "The Last Jedi"
+ rating = MOVIE_RATING
+ }
+ }
+
+ transaction {
+ StarWarsFilmWithRankEntity
+ .find { StarWarsFilmsWithRankTable.name like "The%" }
+ .map { it.name to it.rank }
+ }
+ }
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/UpdateExamples.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/UpdateExamples.kt
new file mode 100644
index 0000000000..d4ef3b8cf0
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/examples/UpdateExamples.kt
@@ -0,0 +1,33 @@
+package org.example.examples
+
+import org.example.entities.StarWarsFilmEntity
+import org.example.tables.StarWarsFilmsTable
+import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
+
+class UpdateExamples {
+ fun updateFilmProperty() {
+ val movie = StarWarsFilmEntity.findById(2)
+ if (movie != null) {
+ /*
+ Important: The `movie.name` statement is referenced by line number in `DAO-CRUD-operations.topic`.
+ If you add, remove, or modify any lines prior to this one, ensure you update the corresponding
+ line numbers in the `code-block` element of the referenced file.
+ */
+ movie.name = "Episode VIII – The Last Jedi"
+ println("The movie has been renamed to ${movie.name}")
+ }
+ }
+ fun updateFilms() {
+ // Find by id and update
+ val updatedMovie = StarWarsFilmEntity.findByIdAndUpdate(2) {
+ it.name = "Episode VIII – The Last Jedi"
+ }
+ println(updatedMovie?.name)
+
+ // Find a single record by a condition and update
+ val updatedMovie2 = StarWarsFilmEntity.findSingleByAndUpdate(StarWarsFilmsTable.name eq "The Last Jedi") {
+ it.name = "Episode VIII – The Last Jedi"
+ }
+ println(updatedMovie2?.name)
+ }
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/CitiesTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/CitiesTable.kt
new file mode 100644
index 0000000000..171dc956e3
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/CitiesTable.kt
@@ -0,0 +1,9 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.IntIdTable
+
+const val MAX_CITY_NAME_LENGTH = 50
+
+object CitiesTable : IntIdTable() {
+ val name = varchar("name", MAX_CITY_NAME_LENGTH)
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsCustomTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsCustomTable.kt
new file mode 100644
index 0000000000..d966dd5d47
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsCustomTable.kt
@@ -0,0 +1,15 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.EntityID
+import org.jetbrains.exposed.dao.id.IdTable
+import org.jetbrains.exposed.sql.Column
+
+const val MAX_ID_LENGTH = 32
+const val MAX_DIRECTOR_NAME_LENGTH = 32
+
+object DirectorsCustomTable : IdTable() {
+ override val id: Column> = varchar("id", MAX_ID_LENGTH).entityId()
+ val name = varchar("name", MAX_DIRECTOR_NAME_LENGTH)
+
+ override val primaryKey = PrimaryKey(id)
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsTable.kt
new file mode 100644
index 0000000000..7dc9a7357e
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsTable.kt
@@ -0,0 +1,15 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.CompositeIdTable
+
+enum class Genre { HORROR, DRAMA, THRILLER, SCI_FI }
+
+const val NAME_LENGTH = 50
+
+object DirectorsTable : CompositeIdTable("directors") {
+ val name = varchar("name", NAME_LENGTH).entityId()
+ val guildId = uuid("guild_id").autoGenerate().entityId()
+ val genre = enumeration("genre")
+
+ override val primaryKey = PrimaryKey(name, guildId)
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsWithGuildRefTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsWithGuildRefTable.kt
new file mode 100644
index 0000000000..84e5d0b14b
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/DirectorsWithGuildRefTable.kt
@@ -0,0 +1,17 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.CompositeIdTable
+
+const val DIRECTOR_NAME_LENGTH = 50
+
+object DirectorsWithGuildRefTable : CompositeIdTable() {
+ val name = varchar("name", DIRECTOR_NAME_LENGTH).entityId()
+ val guildId = reference("guild_id", GuildsTable)
+ val genre = enumeration("genre")
+
+ init {
+ addIdColumn(guildId)
+ }
+
+ override val primaryKey = PrimaryKey(name, guildId)
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/GuildsTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/GuildsTable.kt
new file mode 100644
index 0000000000..7d946c6181
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/GuildsTable.kt
@@ -0,0 +1,5 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.UUIDTable
+
+object GuildsTable : UUIDTable("guilds")
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsTable.kt
new file mode 100644
index 0000000000..4c40e06e44
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsTable.kt
@@ -0,0 +1,23 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.IntIdTable
+
+const val MAX_VARCHAR_LENGTH = 50
+
+/*
+ Important: This file is referenced by line number in `DAO-Table-Types.topic`.
+ If you add, remove, or modify any lines, ensure you update the corresponding
+ line numbers in the `code-block` element of the referenced file.
+
+ CREATE TABLE IF NOT EXISTS STARWARSFILMS
+ (ID INT AUTO_INCREMENT PRIMARY KEY,
+ SEQUEL_ID INT NOT NULL,
+ "name" VARCHAR(50) NOT NULL,
+ DIRECTOR VARCHAR(50) NOT NULL);
+*/
+
+object StarWarsFilmsTable : IntIdTable() {
+ val sequelId = integer("sequel_id").uniqueIndex()
+ val name = varchar("name", MAX_VARCHAR_LENGTH)
+ val director = varchar("director", MAX_VARCHAR_LENGTH)
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsWithRankTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsWithRankTable.kt
new file mode 100644
index 0000000000..8e09e76bda
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/StarWarsFilmsWithRankTable.kt
@@ -0,0 +1,15 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.IntIdTable
+import org.jetbrains.exposed.sql.Rank
+import org.jetbrains.exposed.sql.SortOrder
+
+const val MAX_NAME_LENGTH = 32
+
+object StarWarsFilmsWithRankTable : IntIdTable() {
+ val sequelId = integer("sequel_id").uniqueIndex()
+ val name = varchar("name", MAX_NAME_LENGTH)
+ val rating = double("rating")
+
+ val rank = Rank().over().orderBy(rating, SortOrder.DESC)
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UserRatingsTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UserRatingsTable.kt
new file mode 100644
index 0000000000..7d85903e9d
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UserRatingsTable.kt
@@ -0,0 +1,9 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.IntIdTable
+
+object UserRatingsTable : IntIdTable() {
+ val value = long("value")
+ val film = reference("film", StarWarsFilmsTable)
+ val user = reference("user", UsersTable)
+}
diff --git a/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UsersTable.kt b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UsersTable.kt
new file mode 100644
index 0000000000..05de541ff7
--- /dev/null
+++ b/documentation-website/Writerside/snippets/exposed-dao/src/main/kotlin/org/example/tables/UsersTable.kt
@@ -0,0 +1,10 @@
+package org.example.tables
+
+import org.jetbrains.exposed.dao.id.IntIdTable
+
+const val MAX_USER_NAME_LENGTH = 50
+
+object UsersTable : IntIdTable() {
+ val name = varchar("name", MAX_USER_NAME_LENGTH)
+ val cityId = reference("cityId", CitiesTable.id)
+}
diff --git a/documentation-website/Writerside/topics/DAO-CRUD-Operations.topic b/documentation-website/Writerside/topics/DAO-CRUD-Operations.topic
index 975a60c788..fa82797c42 100644
--- a/documentation-website/Writerside/topics/DAO-CRUD-Operations.topic
+++ b/documentation-website/Writerside/topics/DAO-CRUD-Operations.topic
@@ -3,144 +3,96 @@
SYSTEM "https://resources.jetbrains.com/writerside/1.0/xhtml-entities.dtd">
+ title="CRUD operations" id="DAO-CRUD-Operations">
CRUD (Create, Read, Update, Delete) are the four basic operations supported by any database. This section
demonstrates how to perform SQL CRUD operations using Exposed's DAO (Data Access Object) API.
+
+ These operations can be performed directly through the methods and properties of the Entity
class
+ associated with the table. For more information, see .
+
To create a new table row, use the new
function on the entity class:
-
- val movie = StarWarsFilm.new {
- name = "The Last Jedi"
- sequelId = 8
- director = "Rian Johnson"
- }
-
+
- In the above example StarWarsFilm
would be the entity instance linked to the StarWarsFilms
table.
- For more information, see .
+ In the above example StarWarsFilmEntity
would be the entity instance linked to
+ the StarWarsFilmsTable
table.
To provide a manual id
value to a new entity, pass the value as an argument to the id
parameter:
-
- StarWarsFilm.new(id = 2) {
- name = "The Rise of Skywalker"
- sequelId = 9
- director = "J.J. Abrams"
- }
-
+
If the entity is a CompositeEntity
, the id value can be constructed by creating a component column-to-value association using
CompositeID
:
-
- val newId = CompositeID {
- it[Directors.name] = "J.J. Abrams"
- it[Directors.guildId] = UUID.randomUUID()
- }
-
- Director.new(newId) {
- genre = Genre.SCI_FI
- }
-
+
To read a value from a property, simply access it as you would with any property in a Kotlin class:
-
- val name = movie.name
-
+
An entity's id
property is wrapped as an instance of the EntityID
class.
To access the actual wrapped value, for example the stored Int
from a StarWarsFilm
entity, use EntityID.value
:
-
- val id: Int = movie.id.value
-
+
To retrieve entities, use one of the following methods:
To get all the entity instances associated with this entity class, use the
all()
function:
-
- val movies = StarWarsFilm.all()
-
+
To get all the entity instances that conform to the conditional expression, use the
find()
function:
-
- val movies = StarWarsFilm.find { StarWarsFilms.sequelId eq 8 }
-
+
To get an entity by its id value, use the
findById()
function:
-
- val movie = StarWarsFilm.findById(5)
-
+
-
- For a list of available predicates, see
- DSL Where expression.
-
If the entity is a CompositeEntity
, its id
property can be used to refer to
all composite columns and to get entities,
much like the id
column of its associated CompositeIdTable
:
-
-
- val directorId = CompositeID {
- it[Directors.name] = "George Lucas"
- it[Directors.guildId] = "..."
- }
-
- // these will both deconstruct in SQL to the 2 component columns
- val director = Director.findById(directorId)
- val directors = Director.find { Directors.id eq directorId }
-
+
+
+ The SQL query would result in something like the following:
+
+
+
+ For a list of available predicates, see
+ DSL Where expression.
+
Suppose that you want to find all users who rated the second Star Wars film with a score greater than 5.
First, you would write that query using Exposed DSL.
-
- val query = Users.innerJoin(UserRatings).innerJoin(StarWarsFilm)
- .select(Users.columns)
- .where {
- StarWarsFilms.sequelId eq 2 and (UserRatings.value gt 5)
- }.withDistinct()
-
+
Once the query is defined, you can wrap the result in the User
entity using the
wrapRows()
function to generate entities from the retrieved data:
-
- val users = User.wrapRows(query).toList()
-
+
-
-
-
To sort results in ascending order, use sortedBy
:
-
- val movies = StarWarsFilm.all().sortedBy { it.sequelId }
-
+
To sort results in descending order, use sortedByDescending
:
-
- val movies = StarWarsFilm.all().sortedByDescending { it.sequelId }
-
+
@@ -148,9 +100,7 @@
You can update the value of a property just as you would with any property in a Kotlin class:
-
- movie.name = "Episode VIII – The Last Jedi"
-
+
Exposed doesn't make an immediate update when you set a new value for Entity
, it just stores it on the inner map.
"Flushing" values to the database occurs at the end of the transaction, or before the next SELECT *
from the database.
@@ -159,22 +109,14 @@
To search for an entity by its id and apply an update, use the findByIdAndUpdate()
function:
-
- val updatedMovie = StarWarsFilm.findByIdAndUpdate(5) {
- it.name = "Episode VIII – The Last Jedi"
- }
-
+
To search for a single entity by a query and apply an update, use the findSingleByAndUpdate()
function:
-
- val updatedMovie2 = StarWarsFilm.findSingleByAndUpdate(StarWarsFilms.name eq "The Last Jedi") {
- it.name = "Episode VIII – The Last Jedi"
- }
-
+
@@ -182,9 +124,7 @@
To delete a record, use the delete()
function:
-
- movie.delete()
-
+
@@ -193,17 +133,7 @@
then order the result by that count.To do so, however, you need to convert the Query
to an
Expression
. This can be done using the wrapAsExpression()
function:
-
- val expression = wrapAsExpression<Int>(Users
- .select(Users.id.count())
- .where {
- Cities.id eq Users.cityId
- })
- val cities = Cities
- .selectAll()
- .orderBy(expression, SortOrder.DESC)
- .toList()
-
+
@@ -211,46 +141,18 @@
the entity class can override any open
function in EntityClass
. However, to achieve this functionality, you only need to override
searchQuery()
. The results of the function can then be accessed through a property of the entity class:
-
- ) : IntEntity(id) {
- var sequelId by StarWarsFilms.sequelId
- var name by StarWarsFilms.name
- var rating by StarWarsFilms.rating
-
- val rank: Long
- get() = readValues[StarWarsFilms.rank]
-
- companion object : IntEntityClass(StarWarsFilms) {
- override fun searchQuery(op: Op): Query {
- return super.searchQuery(op).adjustSelect {
- select(columns + StarWarsFilms.rank).set
- }
- }
- }
- }
-
- transaction {
- StarWarsFilm.new {
- sequelId = 8
- name = "The Last Jedi"
- rating = 4.2
- }
- // more insertions ...
- entityCache.clear()
-
- // fetch entities with value (or store entities then read value)
- StarWarsFilm.find { StarWarsFilms.name like "The%" }.map { it.name to it.rank }
- }
- ]]>
+
+
+
+
+
+
+
+
+
+ Then, creating and fetching entities would look like this:
+
+
diff --git a/documentation-website/Writerside/topics/DAO-Entity-definition.topic b/documentation-website/Writerside/topics/DAO-Entity-definition.topic
new file mode 100644
index 0000000000..0f699044c7
--- /dev/null
+++ b/documentation-website/Writerside/topics/DAO-Entity-definition.topic
@@ -0,0 +1,50 @@
+
+
+
+
+
+ Representing database tables as Kotlin objects ensures type safety and allows you to work with database
+ records just like regular Kotlin objects, taking full advantage of Kotlin's language features.
+
+
+ When using the DAO approach, IdTable
needs to be associated with an Entity
, because every database record
+ in this table needs to be mapped to an Entity
instance, identified by its primary key.
+
+
+ An entity instance is defined as a class.
+ In the following example, StarWarsFilmEntity
is the entity class linked to the table StarWarsFilmsTable
:
+
+
+
+
+
+
+
+
+
+
+
+ Since StarWarsFilmsTable
is an IntIdTable
, the StarWarsFilmsEntity
class extends from IntEntity
,
+ which indicates that the id
and primary key of StarWarsFilmsTable
is of type Int
.
+
+
+ The companion object
block defines an EntityClass
which is responsible for maintaining
+ the relation between the StarWarsFilmsEntity
class and the actual table object, StarWarsFilmsTable
.
+
+
+ Each column in the table is represented as a property in the class, where the by
keyword
+ ensures the data is fetched or updated from the corresponding column when accessed.
+
+
+
+ Once the entity class is defined, instances of this class allow you to manipulate individual records
+ from the corresponding table. This could involve
+ creating a new record,
+ retrieving a row based on its primary key,
+ updating values, or
+ deleting records.
+
+
diff --git a/documentation-website/Writerside/topics/DAO-Table-Types.topic b/documentation-website/Writerside/topics/DAO-Table-Types.topic
index 4c6a91aef7..b317bc66b1 100644
--- a/documentation-website/Writerside/topics/DAO-Table-Types.topic
+++ b/documentation-website/Writerside/topics/DAO-Table-Types.topic
@@ -6,7 +6,8 @@
title="Table types" id="DAO-Table-Types" help-id="DAO-Table-types">
-
+
+ Auto-incrementing id
column tables
Apart from the core Table
class, Exposed provides the base
@@ -50,7 +51,8 @@
The following example represents a table with custom columns sequel_id
, name
,
and director
:
-
+
The IntIdTable
class automatically generates an auto-incrementing integer id
@@ -59,83 +61,64 @@
When the table is created, it corresponds to the following
SQL query:
-
+
For more information on defining and configuring tables in Exposed, see .
-
-
- An entity instance or a row in the table is defined as a class instance:
-
- ) : IntEntity(id) {
- companion object : IntEntityClass(StarWarsFilms)
-
- var sequelId by StarWarsFilms.sequelId
- var name by StarWarsFilms.name
- var director by StarWarsFilms.director
- }
- ]]>
-
- To define multiple columns as part of the primary key and id, use CompositeIdTable
and mark each composite column using
- entityId()
.
- Each component column will be available for CRUD operations either individually (as for any standard column) or all together as part of the
- id
column:
-
- ("genre")
-
- override val primaryKey = PrimaryKey(name, guildId)
- }
-
- class Director(id: EntityID) : CompositeEntity(id) {
- companion object : CompositeEntityClass(Directors)
+
+ To define multiple columns as part of the primary key and id, use
+ CompositeIdTable
+ and mark each composite column using entityId()
.
+ Each component column will be available for CRUD operations either individually (as for any standard column)
+ or all together as part of the id
column:
+
+
+
+
+
+
+
+
+
- var genre by Directors.genre
- }
- ]]>
- ("genre")
-
- init {
- addIdColumn(guildId)
- }
-
- override val primaryKey = PrimaryKey(name, guildId)
- }
- ]]>
+
+
+
+
+
+
+
+
- To define a custom column type as the primary key and id, use a typed IdTable
directly and override the id
column:
-
- ("directors") {
- override val id: Column> = varchar("id", 32).entityId()
- val name = varchar("name", 50)
-
- override val primaryKey = PrimaryKey(id)
- }
-
- class Director(id: EntityID) : Entity(id) {
- companion object : EntityClass(Directors)
-
- var name by Directors.name
- }
- ]]>
+
+ To define a custom column type as the primary key and id, use a typed IdTable
directly and
+ override the id
column, as shown in the following example:
+
+
+
+
+
+
+
+
+
+
+ In the definition of DirectorsCustomTable
, the id
field is of type Column<EntityID<String>>
,
+ which will hold String
values with a length of up to 32 characters. Using the
+ override
keyword indicates that this id
column is overriding the default
+ id
column provided by IdTable
.
+
+
+ Once all columns are defined, the id
column is explicitly set as the primary key for the table,
+ using the override
keyword once again.
+