Hi, I haven't been able to find anything on this b...
# komapper
z
Hi, I haven't been able to find anything on this but how would I set up relations? Like I wanna have a
Guild
object that has a
MarkovConfig
t
Hi. Use the include function in your query. Then retrieve the associations from the EntityStore resulting from the query execution. You may use the includeAll function instead of the include function. https://www.komapper.org/docs/reference/query/querydsl/select/#include
d
There was an idea to simplify this... I just wonder if there's really a way to do it: https://kotlinlang.slack.com/archives/C03JF82SDHA/p1672836906121629?thread_ts=1672765276.711739&cid=C03JF82SDHA
Then relations would be a bit more on the entity side... but for now, if the EntityStore actually stores those maps it returns and doesn't just generate them, you could technically add your own extension functions to your entities to denote relations...
If it generates them though, that could be a bit unefficient.
z
I would also prefer something like that. I'm currently using exposed and I can just access the objects directly. Though I really hate its table API
t
We want to support the generation of helper code to handle entity associations easily. Let us know your thoughts on this pull request. https://github.com/komapper/komapper/pull/859
d
Nice start, but just wondering why the thisSide and thatSide can't be deduced by the annotations?
Is there a big run time cost each time one calls
store.oneToMany(thisSide, thatSide)[this]
...?
t
why the thisSide and thatSide can’t be deduced by the annotations?
This is because there can be multiple EntityMetamodel instances for a single entity class. For example, you can create a new metamodel from an existing one at runtime.
Copy code
val r: EntityMetamodel<Employee, Int, _Employee> = Meta.employee.clone(table = "RETIRED_EMPLOYEE")
However, it may be possible to make it unnecessary to specify the thisSide and thatSide by using default arguments.
Is there a big run time cost each time one calls
store.oneToMany(thisSide, thatSide)[this]
...?
I don’t think so, but we would consider caching the results.
d
The
EntityStore
generates the map each time, or is it just casted to the result?
Default arguments would probably be better, because most of the time, I'd suppose that one didn't make a new metamodel... If the only way is for the user to cache the results, then maybe we could think of a better way to name the functions... I think people are used to ORMs that lazily retreive the linked data ONCE and then it's cheap to re-access the same data...
I was also thinking over another little idea... for aggregate roots (objects that are the root to access other related ones), it might be an idea to make the user derive from an interface that provides an extra EntityStore var property, which would be filled in by an
include(...)
query and then checked for/accessed by the generated field, which would add some cohesion to Komapper (but for those using annotations straight in their entities, there's that cohesion anyways...) and then we wouldn't even need that entitystore parameter in those generated functions and there wouldn't be the need to pass down an EntityStore everywhere, it would be saved in the aggregate root.
t
The
EntityStore
generates the map each time, or is it just casted to the result?
Currently the EntityStore generates the map each time. We will change the behavior to cache it in the EntityStore.
I was also thinking over another little idea... for aggregate roots
Interesting. Can you show me the concept code?
d
Copy code
interface EntityStoreContainer {
   var store: EntityStore?
   // perhaps other things needed to help here?
  ....
}

@KomapperEntity
data class Customer(
   ...
   val id: Int,
...) : EntityStoreContainer {
   override var store: EntityStore? = null
}

// Maybe there could be better error handling here...
val Customer.addresses: Set<Address>? get() = store?.oneToMany(...)
t
Thanks for sharing your code. It is exciting, but it would be better to keep the entities immutable and treat them as just data. We need a context receiver. With the context receiver and the default arguments mentioned above, we could write as followings.
Copy code
store.withContext {
    for (department in store[Meta.department])) {
        // use the generated extension function
        val employees = department.employees()
        for (employee in employees) {
            // use the generated extension function
            val address = employee.address()
            println("department=${department.departmentName}, employee=${employee.employeeName}, address=${address?.street}")
        }
    }
}
d
I noticed that in the PR... but it still involves returning the
EntityStore
along with any results... which is a bit messy... I tried to avoid that in MY code in the current implementation by adding an extra property on my root aggregate entity with
@KomapperIgnore
and just adding the results of what I got from the
EntityStore
into in in the repository layer... then my return type doesn't involve anything Komapper... My point is finding a way to have the least amount of overhead and boilerplate code to work with all the extra data retreived.
The truth is that in my case, I needed that extra information in the class in all cases... so I did add that property to my constructor with a default value, and then I did:
Copy code
store.oneToOne(a, ia).map { it.key.copy(myProp = it.value?.myProp) }
to add it.
But that just pulls ONE property from the linked table into the root entity.
I admit that not in all cases would one want to implement an extra interface for this, so then your proposition with context receivers would apply... but I still think it's a bit confusing to have to do that for loop on the store... if the store would be INSIDE the entities... maybe even an optional abstract base class, then it could be MUCH cleaner at the cost of the extra coupling to Komapper.
t
I have improved my Pull Request. Any feedback would be appreciated. https://github.com/komapper/komapper/pull/859#issuecomment-1407327307
d
Sorry for the late review @Toshihiro Nakamura... I just added a little comment, though I'm not sure how/if it could be implemented... but I'm just taking on the end user perspective...
Unless I misunderstood? Is it possible to get a clobstring returned as a string when requesting only one field... it doesn't seem like that case is covered by the tests that were in that PR...
That was for the
alternateType
, now I just commented on pull 859 too... thanks again!