diff --git a/documentation-website/Writerside/topics/Breaking-Changes.md b/documentation-website/Writerside/topics/Breaking-Changes.md index 400cce7a10..69a124fd64 100644 --- a/documentation-website/Writerside/topics/Breaking-Changes.md +++ b/documentation-website/Writerside/topics/Breaking-Changes.md @@ -1,5 +1,12 @@ # Breaking Changes +## 1.0.0-rc-2 + +* The method `references()` with `EntityID` ref parameter changed the signature from + `fun > C.references(ref: Column>, ...): C` to + `fun > C.references(ref: Column>, ...): Column>`. It's done to align signature and behaviour of `references()` method + with `reference()` method. + ## 1.0.0-rc-1 * `exposed-migration` artifact has been replaced with `exposed-migration-core` to hold core common schema migration functionality across both available drivers. diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Table.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Table.kt index 563b345802..341d3527d2 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Table.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/Table.kt @@ -664,14 +664,19 @@ open class Table(name: String = "") : ColumnSet(), DdlAware { /** Creates an [EntityID] column, with the specified [name], for storing the same objects as the specified [originalColumn]. */ fun entityId(name: String, originalColumn: Column): Column> { + return createEntityIdColumn(name, originalColumn) + .also { + _columns.addColumn(it) + } + } + + private fun createEntityIdColumn(name: String, originalColumn: Column): Column> { val columnTypeCopy = originalColumn.columnType.cloneAsBaseType() - val answer = Column>( + return Column>( this, name, EntityIDColumnType(Column(originalColumn.table, name, columnTypeCopy)) ) - _columns.addColumn(answer) - return answer } /** Creates an [EntityID] column, with the specified [name], for storing the identifier of the specified [table]. */ @@ -1137,19 +1142,15 @@ open class Table(name: String = "") : ColumnSet(), DdlAware { * @sample org.jetbrains.exposed.v1.tests.shared.ddl.CreateMissingTablesAndColumnsTests.ExplicitTable */ @JvmName("referencesById") - fun > C.references( + fun > C.references( ref: Column>, onDelete: ReferenceOption? = null, onUpdate: ReferenceOption? = null, fkName: String? = null - ): C = apply { - this.foreignKey = ForeignKeyConstraint( - target = ref, - from = this, - onUpdate = onUpdate, - onDelete = onDelete, - name = fkName - ) + ): Column> { + val entityIdColumn = createEntityIdColumn(name, (ref.columnType as EntityIDColumnType).idColumn) + replaceColumn(this, entityIdColumn) + return entityIdColumn.references(ref, onDelete, onUpdate, fkName) } /** diff --git a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/v1/tests/DatabaseTestsBase.kt b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/v1/tests/DatabaseTestsBase.kt index f8485f286e..04794004b9 100644 --- a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/v1/tests/DatabaseTestsBase.kt +++ b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/v1/tests/DatabaseTestsBase.kt @@ -4,6 +4,8 @@ import org.jetbrains.exposed.v1.core.DatabaseConfig import org.jetbrains.exposed.v1.core.Key import org.jetbrains.exposed.v1.core.Schema import org.jetbrains.exposed.v1.core.Table +import org.jetbrains.exposed.v1.core.Transaction +import org.jetbrains.exposed.v1.core.statements.StatementContext import org.jetbrains.exposed.v1.core.statements.StatementInterceptor import org.jetbrains.exposed.v1.core.transactions.nullableTransactionScope import org.jetbrains.exposed.v1.jdbc.JdbcTransaction @@ -207,4 +209,30 @@ abstract class DatabaseTestsBase { quota = "20M", on = "USERS" ) + + interface Counter { + var count: Int + fun inc() + fun reset() + } + + protected fun JdbcTransaction.executionsCounter(): Counter { + val counter = object : Counter { + override var count = 0 + override fun inc() { + count++ + } + + override fun reset() { + count = 0 + } + } + registerInterceptor(object : StatementInterceptor { + override fun beforeExecution(transaction: Transaction, context: StatementContext) { + counter.inc() + } + }) + + return counter + } } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/EntityReferrersTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/EntityReferrersTests.kt new file mode 100644 index 0000000000..f562cff4b3 --- /dev/null +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/EntityReferrersTests.kt @@ -0,0 +1,61 @@ +package org.jetbrains.exposed.v1.tests.shared.entities + +import org.jetbrains.exposed.v1.core.ReferenceOption +import org.jetbrains.exposed.v1.core.dao.id.EntityID +import org.jetbrains.exposed.v1.core.dao.id.IntIdTable +import org.jetbrains.exposed.v1.core.dao.id.LongIdTable +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.dao.IntEntity +import org.jetbrains.exposed.v1.dao.IntEntityClass +import org.jetbrains.exposed.v1.dao.LongEntity +import org.jetbrains.exposed.v1.dao.LongEntityClass +import org.jetbrains.exposed.v1.dao.with +import org.jetbrains.exposed.v1.jdbc.insertAndGetId +import org.jetbrains.exposed.v1.tests.DatabaseTestsBase +import kotlin.test.Test +import kotlin.test.assertEquals + +class EntityReferrersTests : DatabaseTestsBase() { + + object AlertItemTable : IntIdTable("alert_item") { + val isAlarm = bool("is_alarm").default(true) + } + + class AlertItemEntity(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(AlertItemTable) + + val bids by ItemBidEntity.referrersOn(ItemBidTable.alertItemId, cache = true) + } + + object ItemBidTable : LongIdTable("item_bid") { + val alertItemId = integer("alert_item_id").references(AlertItemTable.id, onDelete = ReferenceOption.CASCADE) + } + + class ItemBidEntity(id: EntityID) : LongEntity(id) { + companion object : LongEntityClass(ItemBidTable) + } + + @Test + fun testCacheIsUsedWithReference() { + withTables(AlertItemTable, ItemBidTable) { + repeat(3) { + val itemId = AlertItemTable.insertAndGetId { + it[isAlarm] = true + } + repeat(5) { + ItemBidTable.insertAndGetId { + it[alertItemId] = itemId.value + } + } + } + + val counter = executionsCounter() + + AlertItemEntity + .find { AlertItemTable.isAlarm eq true } + .with(AlertItemEntity::bids) + + assertEquals(2, counter.count, "'find()' must execute exactly 2 statements. One to fetch items, another one to fetch all the bids") + } + } +} diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/LongIdTableEntityTest.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/LongIdTableEntityTest.kt index cf29c4e206..fb8c8f82ed 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/LongIdTableEntityTest.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/LongIdTableEntityTest.kt @@ -1,4 +1,5 @@ package org.jetbrains.exposed.v1.tests.shared.entities + import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.LongIdTable @@ -36,7 +37,7 @@ object LongIdTables { } object Towns : LongIdTable("towns") { - val cityId: Column = long("city_id").references(Cities.id) + val cityId: Column> = long("city_id").references(Cities.id) } class Town(id: EntityID) : LongEntity(id) { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UIntIdTableEntityTest.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UIntIdTableEntityTest.kt index 17eb45944f..ec6f85c5d3 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UIntIdTableEntityTest.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UIntIdTableEntityTest.kt @@ -1,4 +1,5 @@ package org.jetbrains.exposed.v1.tests.shared.entities + import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.UIntIdTable @@ -136,7 +137,7 @@ object UIntIdTables { } object Towns : UIntIdTable("towns") { - val cityId: Column = uinteger("city_id").references(Cities.id) + val cityId: Column> = uinteger("city_id").references(Cities.id) } class Town(id: EntityID) : UIntEntity(id) { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/ULongIdTableEntityTest.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/ULongIdTableEntityTest.kt index 3c3c2751ad..916064aee0 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/ULongIdTableEntityTest.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/ULongIdTableEntityTest.kt @@ -1,4 +1,5 @@ package org.jetbrains.exposed.v1.tests.shared.entities + import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.ULongIdTable @@ -136,7 +137,7 @@ object ULongIdTables { } object Towns : ULongIdTable("towns") { - val cityId: Column = ulong("city_id").references(Cities.id) + val cityId: Column> = ulong("city_id").references(Cities.id) } class Town(id: EntityID) : ULongEntity(id) { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UuidTableEntityTest.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UuidTableEntityTest.kt index 6d95b4c67c..34797c0deb 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UuidTableEntityTest.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/entities/UuidTableEntityTest.kt @@ -1,4 +1,5 @@ package org.jetbrains.exposed.v1.tests.shared.entities + import org.jetbrains.exposed.v1.core.Column import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.UUIDTable @@ -52,7 +53,7 @@ object UUIDTables { } object Towns : UUIDTable("towns") { - val cityId: Column = uuid("city_id").references(Cities.id) + val cityId: Column> = uuid("city_id").references(Cities.id) } class Town(id: EntityID) : UUIDEntity(id) {