Skip to content

Commit

Permalink
fix: Error when using with when the child references a parent but n…
Browse files Browse the repository at this point in the history
…ot using the parent's id column, but rather another column that is a unique index. (#1902)

- When a column in a child table references a column in the parent table that is not of type `EntityID`, a "is not in record set" error occurs because `entities.groupBy { it.readValues[parentTable.id] }` should be `entities.groupBy { it.readValues[refColumn] }` instead.
- After that was fixed, when we loop over the entities grouped by `refColumn`, we attempt to store the referenced column value in the cache, which does not compile because it's not the right type expected by `cache.getOrPutReferrers`, so we need to find the parent entity that matches the value of the refColumn and use its entityId to store it in the cache.
- The last step was to fix the invocation of `findById`, which was being invoked on `this`, which is the child entity, using `id as EntityID<ID>` which is for the parent entity. The fix is to find the child entity by using `refColumn`, which is the column in the child table that references the `id` in the parent table.
  • Loading branch information
joc-a authored Nov 28, 2023
1 parent 33f04fe commit 39544c3
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -492,10 +492,14 @@ abstract class EntityClass<ID : Comparable<ID>, out T : Entity<ID>>(
val findQuery = wrapRows(finalQuery)
val entities = getEntities(forUpdate, findQuery).distinct()

entities.groupBy { it.readValues[parentTable.id] }.forEach { (id, values) ->
cache.getOrPutReferrers(id, refColumn) { SizedCollection(values) }.also {
entities.groupBy { it.readValues[refColumn] }.forEach { (id, values) ->
val parentEntityId: EntityID<*> = parentTable.select { refColumn.referee as Column<SID> eq id }
.single()[parentTable.id]

cache.getOrPutReferrers(parentEntityId, refColumn) { SizedCollection(values) }.also {
if (keepLoadedReferenceOutOfTransaction) {
findById(id as EntityID<ID>)?.storeReferenceInCache(refColumn, it)
val childEntity = find { refColumn eq id }.firstOrNull()
childEntity?.storeReferenceInCache(refColumn, it)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1529,8 +1529,8 @@ class EntityTests : DatabaseTestsBase() {

@Test
fun testEagerLoadingWithStringParentId() {
withDb {
val db = it.connect {
withDb { testDb ->
val db = testDb.connect {
keepLoadedReferencesOutOfTransaction = true
}
transaction(db) {
Expand Down Expand Up @@ -1566,4 +1566,73 @@ class EntityTests : DatabaseTestsBase() {
}
}
}

object Customers : IntIdTable("Customers") {
val emailAddress = varchar("emailAddress", 30).uniqueIndex()
val fullName = text("fullName")
}

class Customer(id: EntityID<Int>) : IntEntity(id) {
var emailAddress by Customers.emailAddress
var name by Customers.fullName

val orders by Order referrersOn Orders.customer

companion object : IntEntityClass<Customer>(Customers)
}

object Orders : IntIdTable("Orders") {
var orderName = text("orderName")
val customer = reference("customer", Customers.emailAddress)
}

class Order(id: EntityID<Int>) : IntEntity(id) {
var name by Orders.orderName
var customer by Customer referencedOn Orders.customer

companion object : IntEntityClass<Order>(Orders)
}

/**
* This test is for the case when a child references a parent but not using the parent's id column, but rather
* another column that is a unique index.
*/
@Test
fun testEagerLoadingWithReferenceDifferentFromParentId() {
withDb { testDb ->
val db = testDb.connect {
keepLoadedReferencesOutOfTransaction = true
}
transaction(db) {
try {
SchemaUtils.drop(Orders, Customers)
SchemaUtils.create(Customers, Orders)

val customer1 = Customer.new {
emailAddress = "customer1@testing.com"
name = "Customer1"
}

val order1 = Order.new {
name = "Order1"
customer = customer1
}

val order2 = Order.new {
name = "Order2"
customer = customer1
}

Customer.all().with(Customer::orders)

val cache = this.entityCache

assertEquals(true, cache.getReferrers<Order>(customer1.id, Orders.customer)?.contains(order1))
assertEquals(true, cache.getReferrers<Order>(customer1.id, Orders.customer)?.contains(order2))
} finally {
SchemaUtils.drop(Orders, Customers)
}
}
}
}
}

0 comments on commit 39544c3

Please sign in to comment.