Skip to content

Commit

Permalink
feat: EXPOSED-316 Add support for UIntIdTable and UIntEntity (#2026)
Browse files Browse the repository at this point in the history
  • Loading branch information
joc-a authored Mar 15, 2024
1 parent 982bf5a commit ba8aaf7
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 0 deletions.
8 changes: 8 additions & 0 deletions exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public class org/jetbrains/exposed/dao/id/LongIdTable : org/jetbrains/exposed/da
public final fun getPrimaryKey ()Lorg/jetbrains/exposed/sql/Table$PrimaryKey;
}

public class org/jetbrains/exposed/dao/id/UIntIdTable : org/jetbrains/exposed/dao/id/IdTable {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getId ()Lorg/jetbrains/exposed/sql/Column;
public final fun getPrimaryKey ()Lorg/jetbrains/exposed/sql/Table$PrimaryKey;
}

public class org/jetbrains/exposed/dao/id/ULongIdTable : org/jetbrains/exposed/dao/id/IdTable {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ open class IntIdTable(name: String = "", columnName: String = "id") : IdTable<In
final override val primaryKey = PrimaryKey(id)
}

/**
* Identity table with a primary key consisting of an auto-incrementing `UInt` value.
*
* @param name Table name. By default, this will be resolved from any class name with a "Table" suffix removed (if present).
* @param columnName Name for the primary key column. By default, "id" is used.
*/
open class UIntIdTable(name: String = "", columnName: String = "id") : IdTable<UInt>(name) {
/** The identity column of this [IntIdTable], for storing 4-byte unsigned integers wrapped as [EntityID] instances. */
final override val id: Column<EntityID<UInt>> = uinteger(columnName).autoIncrement().entityId()
final override val primaryKey = PrimaryKey(id)
}

/**
* Identity table with a primary key consisting of an auto-incrementing `Long` value.
*
Expand Down
9 changes: 9 additions & 0 deletions exposed-dao/api/exposed-dao.api
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ public final class org/jetbrains/exposed/dao/Referrers : kotlin/properties/ReadO
public fun getValue (Lorg/jetbrains/exposed/dao/Entity;Lkotlin/reflect/KProperty;)Lorg/jetbrains/exposed/sql/SizedIterable;
}

public abstract class org/jetbrains/exposed/dao/UIntEntity : org/jetbrains/exposed/dao/Entity {
public fun <init> (Lorg/jetbrains/exposed/dao/id/EntityID;)V
}

public abstract class org/jetbrains/exposed/dao/UIntEntityClass : org/jetbrains/exposed/dao/EntityClass {
public fun <init> (Lorg/jetbrains/exposed/dao/id/IdTable;Ljava/lang/Class;Lkotlin/jvm/functions/Function1;)V
public synthetic fun <init> (Lorg/jetbrains/exposed/dao/id/IdTable;Ljava/lang/Class;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
}

public abstract class org/jetbrains/exposed/dao/ULongEntity : org/jetbrains/exposed/dao/Entity {
public fun <init> (Lorg/jetbrains/exposed/dao/id/EntityID;)V
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.jetbrains.exposed.dao

import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IdTable

/** Base class for an [Entity] instance identified by an [id] comprised of a wrapped `UInt` value. */
abstract class UIntEntity(id: EntityID<UInt>) : Entity<UInt>(id)

/**
* Base class representing the [EntityClass] that manages [UIntEntity] instances and
* maintains their relation to the provided [table].
*
* @param [table] The [IdTable] object that stores rows mapped to entities of this class.
* @param [entityType] The expected [UIntEntity] type. This can be left `null` if it is the class of type
* argument [E] provided to this [UIntEntityClass] instance. If this `UIntEntityClass` is defined as a companion
* object of a custom `UIntEntity` class, the parameter will be set to this immediately enclosing class by default.
* @sample org.jetbrains.exposed.sql.tests.shared.DDLTests.testDropTableFlushesCache
* @param [entityCtor] The function invoked to instantiate an [UIntEntity] using a provided [EntityID] value.
* If a reference to a specific constructor or a custom function is not passed as an argument, reflection will
* be used to determine the primary constructor of the associated entity class on first access. If this `UIntEntityClass`
* is defined as a companion object of a custom `UIntEntity` class, the constructor will be set to that of the
* immediately enclosing class by default.
* @sample org.jetbrains.exposed.sql.tests.shared.entities.EntityTests.testExplicitEntityConstructor
*/
abstract class UIntEntityClass<out E : UIntEntity>(
table: IdTable<UInt>,
entityType: Class<E>? = null,
entityCtor: ((EntityID<UInt>) -> E)? = null
) : EntityClass<UInt, E>(table, entityType, entityCtor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.jetbrains.exposed.sql.tests.shared.entities

import org.jetbrains.exposed.dao.UIntEntity
import org.jetbrains.exposed.dao.UIntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.UIntIdTable
import org.jetbrains.exposed.sql.exists
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
import org.jetbrains.exposed.sql.tests.shared.assertEquals
import org.junit.Test

class UIntIdTableEntityTest : DatabaseTestsBase() {

@Test
fun `create tables`() {
withTables(UIntIdTables.Cities, UIntIdTables.People) {
assertEquals(true, UIntIdTables.Cities.exists())
assertEquals(true, UIntIdTables.People.exists())
}
}

@Test
fun `create records`() {
withTables(UIntIdTables.Cities, UIntIdTables.People) {
val mumbai = UIntIdTables.City.new { name = "Mumbai" }
val pune = UIntIdTables.City.new { name = "Pune" }
UIntIdTables.Person.new {
name = "David D'souza"
city = mumbai
}
UIntIdTables.Person.new {
name = "Tushar Mumbaikar"
city = mumbai
}
UIntIdTables.Person.new {
name = "Tanu Arora"
city = pune
}

val allCities = UIntIdTables.City.all().map { it.name }
assertEquals(true, allCities.contains<String>("Mumbai"))
assertEquals(true, allCities.contains<String>("Pune"))
assertEquals(false, allCities.contains<String>("Chennai"))

val allPeople = UIntIdTables.Person.all().map { Pair(it.name, it.city.name) }
assertEquals(true, allPeople.contains(Pair("David D'souza", "Mumbai")))
assertEquals(false, allPeople.contains(Pair("David D'souza", "Pune")))
}
}

@Test
fun `update and delete records`() {
withTables(UIntIdTables.Cities, UIntIdTables.People) {
val mumbai = UIntIdTables.City.new { name = "Mumbai" }
val pune = UIntIdTables.City.new { name = "Pune" }
UIntIdTables.Person.new {
name = "David D'souza"
city = mumbai
}
UIntIdTables.Person.new {
name = "Tushar Mumbaikar"
city = mumbai
}
val tanu = UIntIdTables.Person.new {
name = "Tanu Arora"
city = pune
}

tanu.delete()
pune.delete()

val allCities = UIntIdTables.City.all().map { it.name }
assertEquals(true, allCities.contains<String>("Mumbai"))
assertEquals(false, allCities.contains<String>("Pune"))

val allPeople = UIntIdTables.Person.all().map { Pair(it.name, it.city.name) }
assertEquals(true, allPeople.contains(Pair("David D'souza", "Mumbai")))
assertEquals(false, allPeople.contains(Pair("Tanu Arora", "Pune")))
}
}
}

object UIntIdTables {
object Cities : UIntIdTable() {
val name = varchar("name", 50)
}

class City(id: EntityID<UInt>) : UIntEntity(id) {
companion object : UIntEntityClass<City>(Cities)

var name by Cities.name
}

object People : UIntIdTable() {
val name = varchar("name", 80)
val cityId = reference("city_id", Cities)
}

class Person(id: EntityID<UInt>) : UIntEntity(id) {
companion object : UIntEntityClass<Person>(People)

var name by People.name
var city by City referencedOn People.cityId
}
}

0 comments on commit ba8aaf7

Please sign in to comment.