maxmello
02/07/2025, 1:42 PMobject ReviewTable { ... }
object ReviewToReviewerTable {
val review = reference("review", ReviewTable)
val reviewerId = long("reviewerId")
}
class ReviewModel(id: EntityID<Long>): LongEntity(id) {
...
val reviewers: SizedIterable<Long> by ... referrersOn ReviewToReviewerTable.review // list of long
// OR
val reviewers: SizedIterable<ReviewToReviewModel> by ReviewToReviewModel referrersOn ReviewToReviewerTable.review // list of entities
Is it possible to get a list of child elements (here, reviewers of a review) without the reviewId referencing another entity? I would be fine with either getting a list of long or some entity wrapper, its just that these reviewer is a resource from an external service so I can’t join on its table. Normally, I would have to do by ReviewerModel referrersOn ….
which is not possible
Most important for me is that i can use with(…) to load it instantly to prevent n+1 problemChantal Loncle
02/10/2025, 12:41 AMReviewToReviewerTable
something akin to a link table? Would a field reviews
be achieved using via
?
If that's so, this might be a case that could be covered by the feature task for many-to-many relationships with extra columns/fields. I can check the current implementation and see if it will be possible.
I'm not positive at the moment that this will work for you given the external service, but there is a test setup of tables and entities that might be useful for inspiration. The test link table ProjectTasks uses composite ids to allow additional data to be loaded when with()
is called.
If I've gotten the idea completely wrong, please let me know and I'll see if there are any other options that might help.maxmello
02/10/2025, 12:23 PMProject
entity has val tasks by Task via ProjectTasks
, but I have no Task
, only Long
, because the referenced resource is outside the service.
So basically my setup is identical to the Project
/ ProjectTask
/ Task
setup of the example you provided, except I don’t have a Task
class/table, so the CompositeIdTable / Entity
looks like this (this is another example from my code base, identical setup to the previous one)
object ToDoToResponsiblePersons : CompositeIdTable() {
val toDo = reference("toDo", ToDos)
val person = long("personId").entityId()
override val primaryKey = PrimaryKey(toDo, person)
init {
addIdColumn(toDo)
}
}
class ToDoToResponsiblePerson(
id: EntityID<CompositeID>
) : CompositeEntity(id) {
companion object : CompositeEntityClass<ToDoToResponsiblePerson>(ToDoToResponsiblePersons)
}
...
class ToDo(
id: EntityID<Long>
) : LongEntity(id) {
...
var responsiblePersons by ToDoToResponsiblePerson via ToDoToResponsiblePersons
}
This setup using via
results in
Table does not reference target
java.lang.IllegalStateException: Table does not reference target
at org.jetbrains.exposed.dao.InnerTableLink.<init>(InnerTableLink.kt:61)
Probably because the setup usually wouldn’t use by ToDoToResponsiblePerson via
, but instead by Person via
, but again I don’t actually have a Person entity, only its ID. If this project would be completely new, I would just store the person IDs as a jsonb array of numbers, but I am currently developing using an existing database I need to model.
What I just tested further and appears to work at least for some part of the functionality is:
val responsiblePersons by ToDoToResponsiblePerson referrersOn ToDoToResponsiblePersons
It seems to work with something like this: ToDo.all().with(ToDo::responsiblePersons)
, as well as creating ToDoToResponsiblePerson
using CompositeID {... }
syntax.
• So I guess the main difference with via
is that via
can be var
and written to “directly”? Sometimes I still get confused by the different approaches for referencing between entities in Exposed.
• I don’t quite understand what you mean with this sentence: “The test link table ProjectTasks uses composite ids to allow additional data to be loaded when with()
is called.” So is using the composite ID allowing the with(ToDo::responsiblePersons)
from above to succeed?