javaspring-bootjooqgraalvmspring-native

GraalVM / SpringBoot3 / Maven / Jooq: on db save "Could not construct new record"


I'm trying to move a small project to native build. On Win10 I can build an exe without error, the app is starting, and now I can insert with ctx.insertInto(), but I have a tons of ctx.newRecord() with error "Could not construct new record".

2024-05-26T23:43:31.700+02:00 ERROR 29568 --- [SmartScrumPokerBackendNativeApplication] [boundChannel-10] .WebSocketAnnotationMethodMessageHandler : Unhandled exception from message handler method

java.lang.IllegalStateException: Could not construct new record
        at org.jooq.impl.Tools.recordFactory(Tools.java:1538) ~[na:na]
        at org.jooq.impl.Tools.newRecord(Tools.java:1377) ~[na:na]
        at org.jooq.impl.DefaultDSLContext.newRecord(DefaultDSLContext.java:4844) ~[smart_scrum_poker_backend_native.exe:na]
        at org.kbalazs.smart_scrum_poker_backend_native.socket_domain.account_module.repositories.InsecureUserSessionsRepository.create(InsecureUserSessionsRepository.java:22) ~[smart_scrum_poker_backend_native.exe:na]
        at java.base@21.0.3/java.lang.reflect.Method.invoke(Method.java:580) ~[smart_scrum_poker_backend_native.exe:na]

This is the code not working in native, but fine in JVM:

InsecureUserRecord insecureUserRecord = getDSLContext().newRecord(insecureUserTable, insecureUser);
insecureUserRecord.store();

It could be somehow related to the native reflection problem, and maybe I should somehow list the generated files in the ReflectionConfigurationFiles, and I'm trying to find an example for this.

I would be really happy with some hint or code example. (Thanks for the fast response Lucas! )


Solution

  • RegisterReflectionForBinding is the solution for the reflection problem. It can be class based, or JSON file format. I think it is easy to generate the JSON by build time with a folder listing.

    Class based solution:

    package org.kbalazs.smart_scrum_poker_backend_native.config;
    
    import org.kbalazs.smart_scrum_poker_backend_native.db.tables.records.InsecureUserRecord;
    import org.springframework.aot.hint.RuntimeHints;
    import org.springframework.aot.hint.RuntimeHintsRegistrar;
    import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.ImportRuntimeHints;
    
    import static org.springframework.aot.hint.MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS;
    import static org.springframework.aot.hint.MemberCategory.INVOKE_PUBLIC_METHODS;
    import static org.springframework.aot.hint.MemberCategory.PUBLIC_FIELDS;
    
    @Configuration
    @RegisterReflectionForBinding({
        InsecureUserRecord.class,
    })
    @ImportRuntimeHints(ReflectionConfiguration.AppRuntimeHintsRegistrar.class)
    public class ReflectionConfiguration
    {
        public static class AppRuntimeHintsRegistrar implements RuntimeHintsRegistrar
        {
            @Override
            public void registerHints(RuntimeHints hints, ClassLoader classLoader)
            {
                hints.reflection()
                    .registerType(InsecureUserRecord.class, PUBLIC_FIELDS, INVOKE_PUBLIC_METHODS, INVOKE_PUBLIC_CONSTRUCTORS)
                ;
            }
        }
    }