Skip to content

Commit

Permalink
feat!: EXPOSED-577 Allow Entity and EntityID parameters to not be Com…
Browse files Browse the repository at this point in the history
…parable

- Add breaking changes notes
- EntityID and CompositeID no longer implement Comparable
- Min and Max are no longer restricted to Comparable
- References are also no longer restricted
  • Loading branch information
bog-walk committed Oct 17, 2024
1 parent a75ed0b commit 3eb80f2
Show file tree
Hide file tree
Showing 9 changed files with 24 additions and 51 deletions.
3 changes: 3 additions & 0 deletions documentation-website/Writerside/topics/Breaking-Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
* `ArrayColumnType` now supports multidimensional arrays and includes an additional generic parameter.
If it was previously used for one-dimensional arrays with the parameter `T` like `ArrayColumnType<T>`,
it should now be defined as `ArrayColumnType<T, List<T>>`. For instance, `ArrayColumnType<Int>` should now be `ArrayColumnType<Int, List<Int>>`.
* `EntityID` and `CompositeID` no longer implement `Comparable` themselves, to allow their wrapped identity values to be of a type that is not
necessarily `Comparable`. Any use of an entity's `id` with Kotlin comparison operators or `compareTo()` will now require that the wrapped
value be used directly: `entity1.id < entity2.id` will need to become `entity1.id.value < entity2.id.value`.

## 0.55.0
* The `DeleteStatement` property `table` is now deprecated in favor of `targetsSet`, which holds a `ColumnSet` that may be a `Table` or `Join`.
Expand Down
8 changes: 2 additions & 6 deletions exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
public final class org/jetbrains/exposed/dao/id/CompositeID : java/lang/Comparable {
public final class org/jetbrains/exposed/dao/id/CompositeID {
public static final field Companion Lorg/jetbrains/exposed/dao/id/CompositeID$Companion;
public synthetic fun compareTo (Ljava/lang/Object;)I
public fun compareTo (Lorg/jetbrains/exposed/dao/id/CompositeID;)I
public final fun contains (Lorg/jetbrains/exposed/sql/Column;)Z
public fun equals (Ljava/lang/Object;)Z
public final fun get (Lorg/jetbrains/exposed/sql/Column;)Ljava/lang/Object;
Expand All @@ -23,11 +21,9 @@ public class org/jetbrains/exposed/dao/id/CompositeIdTable : org/jetbrains/expos
public final fun getId ()Lorg/jetbrains/exposed/sql/Column;
}

public class org/jetbrains/exposed/dao/id/EntityID : java/lang/Comparable {
public class org/jetbrains/exposed/dao/id/EntityID {
public fun <init> (Ljava/lang/Object;Lorg/jetbrains/exposed/dao/id/IdTable;)V
protected fun <init> (Lorg/jetbrains/exposed/dao/id/IdTable;Ljava/lang/Object;)V
public synthetic fun compareTo (Ljava/lang/Object;)I
public fun compareTo (Lorg/jetbrains/exposed/dao/id/EntityID;)I
public fun equals (Ljava/lang/Object;)Z
public final fun getTable ()Lorg/jetbrains/exposed/dao/id/IdTable;
public final fun getValue ()Ljava/lang/Object;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.jetbrains.exposed.dao.id
import org.jetbrains.exposed.sql.Column

/** Class representing a mapping of each composite primary key column to its stored identity value. */
class CompositeID private constructor() : Comparable<CompositeID> {
class CompositeID private constructor() {
internal val values: MutableMap<Column<*>, Any?> = HashMap()

@Suppress("UNCHECKED_CAST")
Expand Down Expand Up @@ -50,22 +50,6 @@ class CompositeID private constructor() : Comparable<CompositeID> {
return values == other.values
}

override fun compareTo(other: CompositeID): Int {
val compareSize = compareValues(other.values.size, values.size)
if (compareSize != 0) return compareSize

values.entries.forEach { (column, idValue) ->
if (!other.values.containsKey(column)) return -1
require(idValue is Comparable<*>) { "This stored identity value must implement Comparable" }
val otherValue = other.values[column]
require(otherValue is Comparable<*>) { "This stored identity value must implement Comparable" }
compareValues(idValue, otherValue).let {
if (it != 0) return it
}
}
return 0
}

companion object {
operator fun invoke(body: (CompositeID) -> Unit): CompositeID {
return CompositeID().apply(body).also {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ package org.jetbrains.exposed.dao.id
* @sample org.jetbrains.exposed.sql.tests.shared.entities.EntityTestsData.YTable
* @sample org.jetbrains.exposed.sql.tests.shared.dml.InsertTests.testInsertWithPredefinedId
*/
open class EntityID<T : Any> protected constructor(val table: IdTable<T>, id: T?) : Comparable<EntityID<T>> {
open class EntityID<T : Any> protected constructor(val table: IdTable<T>, id: T?) {
constructor(id: T, table: IdTable<T>) : this(table, id)

@Suppress("VariableNaming")
Expand Down Expand Up @@ -41,10 +41,4 @@ open class EntityID<T : Any> protected constructor(val table: IdTable<T>, id: T?

return other._value == _value && other.table == table
}

override fun compareTo(other: EntityID<T>): Int {
require(value is Comparable<*>) { "This stored identity value must implement Comparable" }
require(other.value is Comparable<*>) { "The other stored identity value must implement Comparable" }
return (value as Comparable<T>).compareTo(other.value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class Locate<T : String?>(val expr: Expression<T>, val substring: String) : Func
/**
* Represents an SQL function that returns the minimum value of [expr] across all non-null input values, or `null` if there are no non-null values.
*/
class Min<T : Comparable<T>, in S : T?>(
class Min<T : Any, in S : T?>(
/** Returns the expression from which the minimum value is obtained. */
val expr: Expression<in S>,
columnType: IColumnType<T>
Expand All @@ -182,7 +182,7 @@ class Min<T : Comparable<T>, in S : T?>(
/**
* Represents an SQL function that returns the maximum value of [expr] across all non-null input values, or `null` if there are no non-null values.
*/
class Max<T : Comparable<T>, in S : T?>(
class Max<T : Any, in S : T?>(
/** Returns the expression from which the maximum value is obtained. */
val expr: Expression<in S>,
columnType: IColumnType<T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ fun <T : String?> Expression<T>.locate(substring: String): Locate<T> = Locate(th
// General-Purpose Aggregate Functions

/** Returns the minimum value of this expression across all non-null input values, or `null` if there are no non-null values. */
fun <T : Comparable<T>, S : T?> ExpressionWithColumnType<in S>.min(): Min<T, S> = Min<T, S>(this, this.columnType as IColumnType<T>)
fun <T : Any, S : T?> ExpressionWithColumnType<in S>.min(): Min<T, S> = Min<T, S>(this, this.columnType as IColumnType<T>)

/** Returns the maximum value of this expression across all non-null input values, or `null` if there are no non-null values. */
fun <T : Comparable<T>, S : T?> ExpressionWithColumnType<in S>.max(): Max<T, S> = Max<T, S>(this, this.columnType as IColumnType<T>)
fun <T : Any, S : T?> ExpressionWithColumnType<in S>.max(): Max<T, S> = Max<T, S>(this, this.columnType as IColumnType<T>)

/** Returns the average (arithmetic mean) value of this expression across all non-null input values, or `null` if there are no non-null values. */
fun <T : Comparable<T>, S : T?> ExpressionWithColumnType<S>.avg(scale: Int = 2): Avg<T, S> = Avg<T, S>(this, scale)
Expand Down
22 changes: 9 additions & 13 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1005,15 +1005,11 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
fun <N : Any> Column<N>.autoIncrement(sequence: Sequence): Column<N> =
cloneWithAutoInc(sequence).also { replaceColumn(this, it) }

/**
* Make @receiver column an auto-increment column to generate its values in a database.
* **Note:** Only integer and long columns are supported (signed and unsigned types).
* Some databases, like PostgreSQL, support auto-increment via sequences.
* In this case a name should be provided using the [idSeqName] param and Exposed will create a sequence.
* If a sequence already exists in the database just use its name in [idSeqName].
*
* @param idSeqName an optional parameter to provide a sequence name
*/
@Deprecated(
message = "This function will be removed in future releases.",
replaceWith = ReplaceWith("autoIncrement(idSeqName)"),
level = DeprecationLevel.WARNING
)
fun <N : Any> Column<EntityID<N>>.autoinc(idSeqName: String? = null): Column<EntityID<N>> =
cloneWithAutoInc(idSeqName).also { replaceColumn(this, it) }

Expand Down Expand Up @@ -1069,7 +1065,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* @param ref A column from another table which will be used as a "parent".
* @sample org.jetbrains.exposed.sql.tests.shared.dml.JoinTests.testJoin04
*/
infix fun <T : Comparable<T>, S : T, C : Column<S>> C.references(ref: Column<T>): C = references(
infix fun <T : Any, S : T, C : Column<S>> C.references(ref: Column<T>): C = references(
ref,
null,
null,
Expand All @@ -1089,7 +1085,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* @param fkName Optional foreign key constraint name.
* @sample org.jetbrains.exposed.sql.tests.sqlite.ForeignKeyConstraintTests.testUpdateAndDeleteRulesReadCorrectlyWhenSpecifiedInChildTable
*/
fun <T : Comparable<T>, S : T, C : Column<S>> C.references(
fun <T : Any, S : T, C : Column<S>> C.references(
ref: Column<T>,
onDelete: ReferenceOption? = null,
onUpdate: ReferenceOption? = null,
Expand Down Expand Up @@ -1147,7 +1143,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* @param fkName Optional foreign key constraint name.
* @sample org.jetbrains.exposed.sql.tests.shared.entities.EntityTests.Orders
*/
fun <T : Comparable<T>> reference(
fun <T : Any> reference(
name: String,
refColumn: Column<T>,
onDelete: ReferenceOption? = null,
Expand Down Expand Up @@ -1230,7 +1226,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* @param fkName Optional foreign key constraint name.
* @sample org.jetbrains.exposed.sql.tests.shared.entities.EntityTests.Posts
*/
fun <T : Comparable<T>> optReference(
fun <T : Any> optReference(
name: String,
refColumn: Column<T>,
onDelete: ReferenceOption? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.Transaction
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transactionScope
import java.util.*
import java.util.Deque
import java.util.concurrent.ConcurrentLinkedDeque
import java.util.concurrent.ConcurrentLinkedQueue

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ class OrderedReferenceTest : DatabaseTestsBase() {
fun assertRatingsOrdered(current: UserRatingMultiColumn, prev: UserRatingMultiColumn) {
assertTrue(current.value <= prev.value)
if (current.value == prev.value) {
assertTrue(current.id <= prev.id)
assertTrue(current.id.value <= prev.id.value)
}
}

fun assertNullableRatingsOrdered(current: UserNullableRatingMultiColumn, prev: UserNullableRatingMultiColumn) {
assertTrue(current.value <= prev.value)
if (current.value == prev.value) {
assertTrue(current.id <= prev.id)
assertTrue(current.id.value <= prev.id.value)
}
}

Expand Down Expand Up @@ -140,7 +140,7 @@ class OrderedReferenceTest : DatabaseTestsBase() {
fun assertRatingsOrdered(current: UserRatingChainedColumn, prev: UserRatingChainedColumn) {
assertTrue(current.value <= prev.value)
if (current.value == prev.value) {
assertTrue(current.id <= prev.id)
assertTrue(current.id.value <= prev.id.value)
}
}

Expand Down

0 comments on commit 3eb80f2

Please sign in to comment.