Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: EXPOSED-672 Update the DAO's Relationships topic and add a new example project #2377

Merged
merged 4 commits into from
Jan 31, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Exposed DAO Relationships examples

A Gradle application that shows how to create relationships between entities with Exposed DAO API.
The files are referenced in the DAO's [Relationships](../../topics/DAO-Relationships.topic)
topic.

## Build

To build the application, in a terminal window navigate to the `snippets` folder and run the following command:

```shell
./gradlew :exposed-dao-relationships:build
```

## Run

To run the application, in a terminal window navigate to the `snippets` folder and run the following command:

```shell
./gradlew :exposed-dao-relationships: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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Kotlin application project to get you started.
* For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.8/userguide/building_java_projects.html in the Gradle documentation.
*/

plugins {
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
alias(libs.plugins.jvm)

// Apply the application plugin to add support for building a CLI application in Java.
application
}

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}

dependencies {
// Use the Kotlin JUnit 5 integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")

// Use the JUnit 5 integration.
testImplementation(libs.junit.jupiter.engine)

testRuntimeOnly("org.junit.platform:junit-platform-launcher")

// This dependency is used by the application.
implementation(libs.guava)
implementation(libs.exposed.core)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.dao)
implementation(libs.exposed.kotlin.datetime)
implementation(libs.h2)
implementation(libs.slf4j)
}

// Apply a specific Java toolchain to ease working on different environments.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

application {
// Define the main class for the application.
mainClass = "org.example.AppKt"
}

tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.example

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
import java.util.*

fun main() {
Database.connect(
"jdbc:h2:mem:test",
"org.h2.Driver",
databaseConfig = DatabaseConfig { useNestedTransactions = true }
)

transaction {
addLogger(StdOutSqlLogger)
SchemaUtils.create(ActorsTable)
SchemaUtils.create(StarWarsFilmsTable)
SchemaUtils.create(StarWarsFilmActorsTable)
SchemaUtils.create(UserRatingsTable)
SchemaUtils.create(UsersTable)
runOneToManyExample()
runManyToManyExample()
runParentChildExample()
runEagerLoadingExamples()
}
}

fun runOneToManyExample() {
val oneToManyExamples = OneToManyExamples()
oneToManyExamples.queryRatings()
}

fun runManyToManyExample() {
val manyToManyExamples = ManyToManyExamples()
manyToManyExamples.getActors()
}

fun runParentChildExample() {
// create tables
SchemaUtils.create(DirectorsTable)
SchemaUtils.create(StarWarsFilmsWithDirectorTable)
SchemaUtils.create(StarWarsFilmRelationsTable)

val parentChildExamples = ParentChildExamples()
parentChildExamples.querySequels()
}

fun runEagerLoadingExamples() {
val eagerLoadingExamples = EagerLoadingExamples()
eagerLoadingExamples.load()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.example.entities

import org.example.tables.ActorsTable
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID

class ActorEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<ActorEntity>(ActorsTable)

var firstname by ActorsTable.firstname
var lastname by ActorsTable.lastname
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.example.entities

import org.example.tables.DirectorsCompositeIdTable
import org.example.tables.DirectorsTable
import org.example.tables.StarWarsFilmsWithCompositeRefTable
import org.jetbrains.exposed.dao.CompositeEntity
import org.jetbrains.exposed.dao.CompositeEntityClass
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.CompositeID
import org.jetbrains.exposed.dao.id.EntityID

/*
Important: This file is referenced by line number in `DAO-Relationships.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.
*/

class DirectorEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<DirectorEntity>(DirectorsTable)

var name by DirectorsTable.name
var genre by DirectorsTable.genre
}

class DirectorCompositeIDEntity(id: EntityID<CompositeID>) : CompositeEntity(id) {
companion object : CompositeEntityClass<DirectorCompositeIDEntity>(DirectorsCompositeIdTable)

var genre by DirectorsCompositeIdTable.genre
val films by StarWarsFilmWithCompositeRefEntity referrersOn StarWarsFilmsWithCompositeRefTable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.example.entities

import org.example.tables.StarWarsFilmActorsTable
import org.example.tables.StarWarsFilmRelationsTable
import org.example.tables.StarWarsFilmsTable
import org.example.tables.StarWarsFilmsWithCompositeRefTable
import org.example.tables.StarWarsFilmsWithDirectorTable
import org.example.tables.UserRatingsTable
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID

/*
Important: This file is referenced by line number in `DAO-Relationships.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.
*/

class StarWarsFilmEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<StarWarsFilmEntity>(StarWarsFilmsTable)

var sequelId by StarWarsFilmsTable.sequelId
var name by StarWarsFilmsTable.name
var director by StarWarsFilmsTable.director
val ratings by UserRatingEntity referrersOn UserRatingsTable.film // make sure to use val and referrersOn
var actors by ActorEntity via StarWarsFilmActorsTable
}

class StarWarsFilmWithParentAndChildEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<StarWarsFilmWithParentAndChildEntity>(StarWarsFilmsWithDirectorTable)

var name by StarWarsFilmsWithDirectorTable.name
var director by DirectorEntity referencedOn StarWarsFilmsWithDirectorTable.director

// Define hierarchical relationships
var sequels by StarWarsFilmWithParentAndChildEntity.via(StarWarsFilmRelationsTable.parentFilm, StarWarsFilmRelationsTable.childFilm)
var prequels by StarWarsFilmWithParentAndChildEntity.via(StarWarsFilmRelationsTable.childFilm, StarWarsFilmRelationsTable.parentFilm)
}

class StarWarsFilmWithCompositeRefEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<StarWarsFilmWithCompositeRefEntity>(StarWarsFilmsWithCompositeRefTable)

var sequelId by StarWarsFilmsWithCompositeRefTable.sequelId
var name by StarWarsFilmsWithCompositeRefTable.name
var director by DirectorCompositeIDEntity referencedOn StarWarsFilmsWithCompositeRefTable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.example.entities

import org.example.entities.UserRatingEntity.Companion.backReferencedOn
import org.example.tables.UserRatingsTable
import org.example.tables.UsersTable
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.SortOrder

/*
Important: This file is referenced by line number in `DAO-Relationships.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.
*/

class UserEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<UserEntity>(UsersTable)

var name by UsersTable.name
val ratings by UserRatingEntity referrersOn UserRatingsTable.user orderBy UserRatingsTable.value
}

class UserOrderedEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<UserOrderedEntity>(UsersTable)

var name by UsersTable.name

/*
Without infix notation:

val ratings by UserRating.referrersOn(UserRatings.user).orderBy(
UserRatings.value to SortOrder.DESC,
UserRatings.id to SortOrder.ASC
)
*/
val ratings by UserRatingEntity referrersOn UserRatingsTable.user orderBy listOf(
UserRatingsTable.value to SortOrder.DESC,
UserRatingsTable.id to SortOrder.ASC
)
}

class UserWithSingleRatingEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<UserWithSingleRatingEntity>(UsersTable)

var name by UsersTable.name
val rating by UserRatingEntity backReferencedOn UserRatingsTable.user // make sure to use val and backReferencedOn
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.example.entities

import org.example.tables.UserRatingsTable
import org.example.tables.UserRatingsWithOptionalUserTable
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID

/*
Important: This file is referenced by line number in `DAO-Relationships.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.
*/

class UserRatingEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<UserRatingEntity>(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
}

class UserRatingWithOptionalUserEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<UserRatingWithOptionalUserEntity>(UserRatingsWithOptionalUserTable)

var value by UserRatingsWithOptionalUserTable.value
var film by StarWarsFilmEntity referencedOn UserRatingsWithOptionalUserTable.film // use referencedOn for normal references
var user by UserEntity optionalReferencedOn UserRatingsWithOptionalUserTable.user
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.example.examples

import org.example.entities.UserEntity
import org.example.entities.UserRatingEntity
import org.jetbrains.exposed.dao.load
import org.jetbrains.exposed.dao.with

class EagerLoadingExamples {
fun load() {
// load a reference
UserEntity.findById(1)?.load(UserEntity::ratings)

// load references of references
UserEntity.findById(1)?.load(UserEntity::ratings, UserRatingEntity::film)

// load references on Collections
UserEntity.all().with(UserEntity::ratings)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.example.examples

import org.example.entities.ActorEntity
import org.example.entities.StarWarsFilmEntity
import org.jetbrains.exposed.sql.SizedCollection

const val MOVIE2_SEQUEL_ID = 9

class ManyToManyExamples {
fun getActors() {
// create an actor
val actor = ActorEntity.new {
firstname = "Daisy"
lastname = "Ridley"
}
// create film
val film = StarWarsFilmEntity.new {
name = "The Rise of Skywalker"
sequelId = MOVIE2_SEQUEL_ID
director = "J.J. Abrams"
actors = SizedCollection(listOf(actor))
}

val filmActors = film.actors
filmActors.forEach {
println(it.firstname)
}
}
}
Loading
Loading