babyfish-ct / jimmer

A revolutionary ORM framework for both java and kotlin.
Apache License 2.0
876 stars 88 forks source link

Associative property can not be fetched if `ManyToManyView.deeperProp` is omitted #773

Closed ForteScarlet closed 1 week ago

ForteScarlet commented 1 week ago

Hi! When I try to use ManyToManyView, I can't fetch the associated property without specifying ManyToManyView.deeperProp , but in the documentation, it always reminds me that this attribute can be omitted.

Here's what I used to reproduce:

schema and data

create table if not exists system_account
(
    id       int auto_increment primary key,
    username varchar(100) not null,
    email    varchar(100) not null,
    unique (username)
);

create table if not exists system_role
(
    id     int auto_increment primary key,
    name   varchar(100)         not null,
    enable boolean default true not null comment '启用状态',
    unique (name)
);

create table if not exists system_account_role_mapping
(
    id         int auto_increment primary key,
    account_id int                  not null,
    role_id    int                  not null,
    enable     boolean default true not null,
    foreign key (account_id) references system_account (id),
    foreign key (role_id) references system_role (id),
    unique (account_id, role_id)
);

insert into system_account(id, username, email)
VALUES (1, 'user-A', '1@1.com');

insert into system_role(id, name, enable)
VALUES (1, 'role-A', true),
       (2, 'role-B', true),
       (3, 'role-C', false),
       (4, 'role-D', false);

insert into system_account_role_mapping (account_id, role_id, enable)
values (1, 1, true),
       (1, 2, false),
       (1, 3, true),
       (1, 4, false);

entites

// SystemAccount.kt
@Entity
interface SystemAccount : BaseEntity {
    @Key
    val username: String
    val email: String

    @OneToMany(mappedBy = "account")
    val roleMappings: List<SystemAccountRoleMapping>

    @ManyToManyView(prop = "roleMappings") // Note: no `deeperProp` here for now
    val roles: List<SystemRole>
}

// SystemRole.kt
@Entity
interface SystemRole : BaseEntity {
    @Key
    val name: String
    val enable: Boolean

    @OneToMany(mappedBy = "role")
    val accountMappings: List<SystemAccountRoleMapping>

    @ManyToManyView(prop = "accountMappings")
    val accounts: List<SystemAccount>
}

// SystemAccountRoleMapping.kt
@Entity
interface SystemAccountRoleMapping : BaseEntity {
    @Key
    @ManyToOne
    val account: SystemAccount

    @Key
    @ManyToOne
    val role: SystemRole

    val enable: Boolean
}

the test

@Test
    @Transactional
    fun queryTest() {
        val data = sql.createQuery(SystemAccount::class) {
            where(table.id eq 1)

            select(
                table.fetchBy {
                    allScalarFields()
                    roles {
                        allScalarFields()
                    }
                }
            )
        }.limit(1).execute().first()

        // print data
        // --> {"id":1,"username":"user-A","email":"1@1.com"}
        println(data)

        // Error: The property "forte.example.jimmerbug1.etity.SystemAccountRoleMapping.enable" is unloaded
        println(data.roles)
    }

Console output:

{"id":1,"username":"user-A","email":"1@1.com"}

The property "forte.example.jimmerbug1.etity.SystemAccountRoleMapping.enable" is unloaded
org.babyfish.jimmer.UnloadedException: The property "forte.example.jimmerbug1.etity.SystemAccountRoleMapping.enable" is unloaded
    at forte.example.jimmerbug1.etity.SystemAccountRoleMappingDraft$$$Impl.getEnable(SystemAccountRoleMappingDraft.kt:195)
    at forte.example.jimmerbug1.etity.SystemAccountRoleMappingDraft$$$Implementor$DefaultImpls.__get(SystemAccountRoleMappingDraft.kt:114)
    at forte.example.jimmerbug1.etity.SystemAccountRoleMappingDraft$$$Impl.__get(SystemAccountRoleMappingDraft.kt:143)
    at org.babyfish.jimmer.sql.collection.ManyToManyViewList.toElement(ManyToManyViewList.java:177)
    at org.babyfish.jimmer.sql.collection.ManyToManyViewList.toString(ManyToManyViewList.java:167)
    at java.base/java.lang.String.valueOf(String.java:4465)
    at java.base/java.io.PrintStream.print(PrintStream.java:1017)
    at org.gradle.internal.io.LinePerThreadBufferingOutputStream.println(LinePerThreadBufferingOutputStream.java:229)
    at forte.example.jimmerbug1.JimmerBug1ApplicationTests.queryTest(JimmerBug1ApplicationTests.kt:54)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

The output data does not have roles in it and if I try to get roles I get an UnloadedException.

Then, I added the explicit deeperProp attribute to SystemAccount.roles

    // SystemAccount.kt

    @ManyToManyView(prop = "roleMappings", deeperProp = "role")
    val roles: List<SystemRole>

The test performs as expected.

Console output:

{"id":1,"username":"user-A","email":"1@1.com","roles":[{"id":1,"name":"role-A","enable":true},{"id":2,"name":"role-B","enable":true},{"id":3,"name":"role-C","enable":false},{"id":4,"name":"role-D","enable":false}]}
[{"id":1,"name":"role-A","enable":true}, {"id":2,"name":"role-B","enable":true}, {"id":3,"name":"role-C","enable":false}, {"id":4,"name":"role-D","enable":false}]

I don't know if it is due to bugs or because the documentation has not been updated in time?

babyfish-ct commented 1 week ago

Both java and kotlin code have test cases like yours, for example, the kotlin test is here

https://github.com/babyfish-ct/jimmer/blob/main/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/query/LinkDSLTest.kt

And the entity type is declared here https://github.com/babyfish-ct/jimmer/blob/main/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/link/Student.kt

Like your test, 'deeperProp' is not specified.

All of them work fine, I cannot reproduce the issue, can you upload the minized reproducing package?

ForteScarlet commented 1 week ago

This is the project I used to test: jimmer-test.zip

Hope it helps.

babyfish-ct commented 1 week ago

Try 0.8.14

ForteScarlet commented 1 week ago

It seems to work well on 0.9.14, thanks!