Skip to content

fix!: EXPOSED-851 Not use Cached Entity when reference column defined with .references()#2614

Open
obabichevjb wants to merge 1 commit into
mainfrom
obabichev/exposed-851-reference-with-entity-cache
Open

fix!: EXPOSED-851 Not use Cached Entity when reference column defined with .references()#2614
obabichevjb wants to merge 1 commit into
mainfrom
obabichev/exposed-851-reference-with-entity-cache

Conversation

@obabichevjb
Copy link
Copy Markdown
Collaborator

Description

The root problem from the issue EXPOSED-851 is that call Entity.find().with() performs more sql queries than it should.

if the columnd with reference defined as val alertItemId = integer("alert_item_id").references(AlertItemTable.id, ...) some extra queries on the parent entity is performed, but if it's defined via val alertItemId = reference(...) it doesn't happen.

For that case the signatures of these methods have the following structure:

fun <T : Any, S : T, C : Column<S>> C.references(
  ref: Column<EntityID<T>>,
  ...
): C

fun <T : Any, E : EntityID<T>> reference(
  name: String,
  refColumn: Column<E>,
  ...
): Column<E>

In the case of reference() the method is defined for Column<EntityID<T>> reference and returns Column<E> ~ Column<EntitytID<T>> column.

From other side the method references() is also defined on Column<EntityID<T>> reference, but returns Column<S column (without EntityID). Internally that method also doesn't register the current column as entity id column.

The solution is to align behavior of references() method to the reference() method.


Type of Change

Please mark the relevant options with an "X":

  • Bug fix

Updates/remove existing public API methods:

  • Is breaking change

Affected databases:

  • All

Related Issues

EXPOSED-851 Not use Cached Entity when reference column defined with .references()

@obabichevjb obabichevjb requested review from bog-walk and e5l September 5, 2025 08:55
Comment on lines +3 to +9
## 1.0.0-rc-2

* The method `references()` with `EntityID` ref parameter changed the signature from
`fun <T : Any, S : T, C : Column<S>> C.references(ref: Column<EntityID<T>>, ...): C` to
`fun <T : Any, C : Column<T>> C.references(ref: Column<EntityID<T>>, ...): Column<EntityID<T>>`. It's done to align signature and behaviour of `references()` method
with `reference()` method.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, the current setup is intentional. reference() and Column.references() are not meant to be 100% equivalent or to be aligned exactly.

Column.references() exists for users that want to keep the column they have defined/registered, but only additionally create a foreign key on table DDL.
There could be DSL users who are choosing to use IntIdTable for convenience but don't want to deal with EntityID any more than necessary and who purposefully want to strip the entityID wrapper from the referenced column.

I think there could be a lot of people who want the original distinction to remain:

// registers a new integer column + create FK
val city: Column<Int> = integer("city_id").references(Cities.id)

// registers an entityID column that matches reference + create FK
val city: Column<EntityID<Int>> = reference("city_id", Cities.id)

My question is:

If the user is relying on DAO, and therefore expects the cache to be utilised to minimise db queries, why are they using .references() instead of reference()?
If there is no reason, maybe we should be making it clear in documentation that .references() does not involve the entity cache?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bog-walk Thank you, it makes sense,

in this case it could be better update documentation,

but in this case I see the problem that Exposed allows to create reference for entity on such a column, and allows to fetch entities using with(), but makes extra queries in this case, and there is no way for the user that it would happen without checking the logs, and it's easy to mix reference() and references() within code.

I will check, probably it could be easy to prevent usage of referrersOn() on columns that have no EntityID<T> in type.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Firstly, thank you for taking care of this issue as an issue reporter.

I didn't expect the references() method to affect the cache behaviour either and had to look at the code to analyse the cause. As mentioned above, If the references() method is not intended to wrap EntityID, It would be helpful to have clear documentation stating that cached entities will not be used for reference columns with references().

However, as @obabichevjb said, even with documentation, the behavior of references() appears fine on the surface, and the issue is only apparent in the logs. Therefore, I think it's better to be more defensive to prevent user mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants