Files
exposed-wiki/DAO.md
Andrey.Tarashevskiy 1fcc105e8f Updated DAO (markdown)
2020-04-08 12:29:25 +03:00

5.6 KiB
Raw Blame History


Overview

The DAO (Data Access Object) API of Exposed, is similar to ORM frameworks like Hibernate with specific Kotlin API.
A DB table is represented by an object inherited from org.jetbrains.exposed.sql.Table like that:

object StarWarsFilms : Table() {
  val id: Column<Int> = integer("id").autoIncrement().primaryKey()
  val sequelId: Column<Int> = integer("sequel_id").uniqueIndex()
  val name: Column<String> = varchar("name", 50)
  val director: Column<String> = varchar("director", 50)
}

Tables that contains Int id with the name id can be declared like that:

object StarWarsFilms : IntIdTable() {
  val sequelId: Column<Int> = integer("sequel_id").uniqueIndex()
  val name: Column<String> = varchar("name", 50)
  val director: Column<String> = varchar("director", 50)
}

Note that these Column types will be defined automatically, so you can also just leave them away. This would produce the same result as the example above:

object StarWarsFilms : IntIdTable() {
  val sequelId = integer("sequel_id").uniqueIndex()
  val name = varchar("name", 50)
  val director = varchar("director", 50)
}

An entity instance or a row in the table is defined as a class instance:

class StarWarsFilm(id: EntityID<Int>) : IntEntity(id) {
 companion object : IntEntityClass<StarWarsFilm>(StarWarsFilms)

 var sequelId by StarWarsFilms.sequelId 
 var name     by StarWarsFilms.name
 var director by StarWarsFilms.director
}

Basic CRUD operations

Create

val movie = StarWarsFilm.new {
  name = "The Last Jedi"
  sequelId = 8
  director = "Rian Johnson"
}

Read

To get entities use one of the following

val movies = StarWarsFilm.all()
val movies = StarWarsFilm.find {StarWarsFilms.sequelId eq 8}
val movie = StarWarsFilm.findById(5)

Read a value from a property similar to any property in a Kotlin class:

val name = movie.name

Sort (Order-by)

val movies = StarWarsFilm.all().sortedBy { it.sequelId }

Update

Update a value of a property similar to any property in a Kotlin class:

movie.name = "Episode VIII  The Last Jedi"
  • Note: Exposed doesn't make an immediate update when you set a new value for Entity, it just store it on the inner map. "Flushing" values to the database occurs at the end of the transaction or before next "select *" from the database.

Delete

movie.delete() 

Referencing

Simple reference

Let's say you have this table:

object Users: IntIdTable() {
    val name = varchar("name", 50)
}

class User(id: EntityID<Int>): IntEntity(id) {
    companion object : IntEntityClass<User>(Users)

    var name by Users.name
}

And now you want to add a table referencing this table (and other tables!):

object UserRatings: IntIdTable() {
    val value = long("value")
    val film = reference("film", StarWarsFilms)
    val user = reference("user", Users)
}

class UserRating(id: EntityID<Int>): IntEntity(id) {
    companion object : IntEntityClass<UserRating>(UserRatings)

    var value by UserRatings.value
    var film by StarWarsFilm referencedOn UserRatings.film // use referencedOn for normal references
    var user by User referencedOn UserRatings.user
}

Now you can get the film for a rating in the same way you would get any other field:

filmRating.film // returns a StarWarsFilm object

Now if you wanted to get all the ratings for a film, you could do that by using the FilmRating.find function, but what is much easier is to just add a referrersOn field to the StarWarsFilm class:

class StarWarsFilm(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<StarWarsFilm>(StarWarsFilms)

    ...
    val ratings by UserRating referrersOn UserRatings.film // make sure to use val and referrersOn
    ...
}

You can call:

movie.ratings // returns all UserRating objects with this movie as film

Optional reference

You can also add an optional reference:

object UserRatings: IntIdTable() {
    ...
    val secondUser = reference("second_user", Users).nullable() // this reference is nullable!
    ...
}

class UserRating(id: EntityID<Int>): IntEntity(id) {
    companion object : IntEntityClass<UserRating>(UserRatings)

    ...
    var secondUser by User optionalReferencedOn UserRatings.secondUser // use optionalReferencedOn for nullable references
    ...
}

Now secondUser will be a nullable field. Of course you can still use referrersOn.

Advanced CRUD operations

Read entity with a join to another table

Lets imagine that you want to find all users who rated second SW film with more then 5. First of all we should write that query using Exposed DSL.

val query = Users.innerJoin(UserRatings).innerJoin(StarWarsFilm)
  .slice { Users.columns }
  .select {
    StarWarsFilms.sequelId eq 2 and (UserRatings.value gt 5) 
  }.withDistinct()

After that all we have to do is to "wrap" a result with User entity:

val users = User.wrapRows(query).toList()