quarkiverse / quarkus-mybatis

Quarkus MyBatis Extension
Apache License 2.0
89 stars 33 forks source link

MyBatis throws exceptions in native build #265

Open Tikani opened 1 year ago

Tikani commented 1 year ago

Hi! We have a Quarkus project with versions: Quarkus: 2.10.1.Final quarkus-mybatis: 1.0.4 After building the project into native with GraalVM and launching the executable, the following exceptions are in a log:

Error querying database.  Cause: org.apache.ibatis.executor.ExecutorException: Error creating lazy proxy.  Cause: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
The error occurred while handling results
SQL: SELECT * FROM foo WHERE bar_id = ?
Cause: org.apache.ibatis.executor.ExecutorException: Error creating lazy proxy.  Cause: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:153)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
    at java.lang.reflect.Method.invoke(Method.java:568)
    at org.apache.ibatis.session.SqlSessionManager$SqlSessionInterceptor.invoke(SqlSessionManager.java:357)
    at jdk.proxy4.$Proxy352.selectList(Unknown Source)
    at org.apache.ibatis.session.SqlSessionManager.selectList(SqlSessionManager.java:206)
    at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
    at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
    at jdk.proxy4.$Proxy340.getAll(Unknown Source)
    at com.foo.bar.ClassA.updateStatistics(ClassA.java:134)
    at com.foo.bar.ClassA_VertxInvoker_updateStatistics_d89477c248378a626dcb124bf003d4ccfa6f3ffc.invokeBean(Unknown Source)
    at io.quarkus.vertx.runtime.EventConsumerInvoker.invoke(EventConsumerInvoker.java:41)
    at io.quarkus.vertx.runtime.VertxRecorder$3$1.handle(VertxRecorder.java:130)
    at io.quarkus.vertx.runtime.VertxRecorder$3$1.handle(VertxRecorder.java:100)
    at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50)
    at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:168)
    at io.vertx.core.eventbus.impl.MessageConsumerImpl.dispatch(MessageConsumerImpl.java:177)
    at io.vertx.core.eventbus.impl.HandlerRegistration$InboundDeliveryContext.next(HandlerRegistration.java:169)
    at io.vertx.core.eventbus.impl.HandlerRegistration$InboundDeliveryContext.dispatch(HandlerRegistration.java:134)
    at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:111)
    at io.vertx.core.eventbus.impl.HandlerRegistration.dispatch(HandlerRegistration.java:105)
    at io.vertx.core.eventbus.impl.MessageConsumerImpl.deliver(MessageConsumerImpl.java:183)
    at io.vertx.core.eventbus.impl.MessageConsumerImpl.doReceive(MessageConsumerImpl.java:168)
    at io.vertx.core.eventbus.impl.HandlerRegistration.lambda$receive$0(HandlerRegistration.java:56)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:833)
    at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704)
    at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)
Caused by: org.apache.ibatis.executor.ExecutorException: Error creating lazy proxy.  Cause: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.crateProxy(JavassistProxyFactory.java:89)
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl.createProxy(JavassistProxyFactory.java:118)
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.createProxy(JavassistProxyFactory.java:59)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(DefaultResultSetHandler.java:648)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:404)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:361)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:335)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:308)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:201)
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:65)
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
    at org.apache.ibatis.executor.loader.ResultLoader.selectList(ResultLoader.java:81)
    at org.apache.ibatis.executor.loader.ResultLoader.loadResult(ResultLoader.java:70)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getNestedQueryMappingValue(DefaultResultSetHandler.java:874)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getPropertyMappingValue(DefaultResultSetHandler.java:511)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyPropertyMappings(DefaultResultSetHandler.java:487)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:411)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:361)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:335)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:308)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:201)
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:65)
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:89)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
    ... 35 more
Caused by: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
    at com.oracle.svm.core.util.VMError.unimplemented(VMError.java:98)
    at java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:46)
    at org.apache.ibatis.javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:295)
    at org.apache.ibatis.javassist.util.proxy.DefineClassHelper$Java11.defineClass(DefineClassHelper.java:48)
    at org.apache.ibatis.javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:260)
    at org.apache.ibatis.javassist.util.proxy.FactoryHelper.toClass(FactoryHelper.java:154)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass3(ProxyFactory.java:640)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass2(ProxyFactory.java:624)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass1(ProxyFactory.java:560)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass(ProxyFactory.java:481)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.create(ProxyFactory.java:828)
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.crateProxy(JavassistProxyFactory.java:87)
    ... 67 more

MyDao mapper:

    @Select("SELECT * FROM my_table")
    @Results(value = {
          @Result(id = true, property = "uuid", column = "uuid", javaType = UUID.class, typeHandler = UuidTypeHandler.class),
          @Result(property = "foo", column = "uuid", javaType = List.class,
                many = @Many(select = "com.test.FooDAO.getByMyId", fetchType = FetchType.EAGER)),
          @Result(property = "bar", column = "uuid", javaType = Bar.class,
                many = @Many(select = "com.test.BarDAO.getByMyId", fetchType = FetchType.EAGER))
})
List<My> getAll();

ClassA.updateStatistics() event handler:

@ConsumeEvent(EventConstant.UPDATE)
public void updateStatistics(UpdateEvent event) {
    myDAO.getAll().forEach(my -> {
            ...
    });
}

Is there any workaround for this?

zhfeng commented 1 year ago

Hi @Tikani

Is it possible to test with the lastest release 1.0.6?

VojtenkoRN commented 1 year ago

Hi there! I'm on the same team as @Tikani Versions was updated to: Quarkus: 2.16.3.Final quarkus-mybatis: 1.0.6 The exception is still there.


### The error may exist in com/test/MyDao.java (best guess)
### The error may involve com.test.MyDao.getByBarId
### The error occurred while handling results
### SQL: SELECT * FROM foo WHERE bar_id = ?
### Cause: org.apache.ibatis.executor.ExecutorException: Error creating lazy proxy.  Cause: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:153)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
    at java.lang.reflect.Method.invoke(Method.java:568)
    at org.apache.ibatis.session.SqlSessionManager$SqlSessionInterceptor.invoke(SqlSessionManager.java:357)
    at jdk.proxy4.$Proxy352.selectList(Unknown Source)
    at org.apache.ibatis.session.SqlSessionManager.selectList(SqlSessionManager.java:206)
    at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
    at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
    at jdk.proxy4.$Proxy340.getAll(Unknown Source)
    at com.foo.bar.ClassA.updateStatistics(QueueEventProcessor.java:134)
    at com.foo.bar.ClassA_VertxInvoker_updateStatistics_d89477c248378a626dcb124bf003d4ccfa6f3ffc.invokeBean(Unknown Source)
    at io.quarkus.vertx.runtime.EventConsumerInvoker.invoke(EventConsumerInvoker.java:41)
    at io.quarkus.vertx.runtime.VertxRecorder$3$1.handle(VertxRecorder.java:130)
    at io.quarkus.vertx.runtime.VertxRecorder$3$1.handle(VertxRecorder.java:100)
    at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50)
    at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:168)
    at io.vertx.core.eventbus.impl.MessageConsumerImpl.dispatch(MessageConsumerImpl.java:177)
    at io.vertx.core.eventbus.impl.HandlerRegistration$InboundDeliveryContext.next(HandlerRegistration.java:169)
    at io.vertx.core.eventbus.impl.HandlerRegistration$InboundDeliveryContext.dispatch(HandlerRegistration.java:134)
    at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:111)
    at io.vertx.core.eventbus.impl.HandlerRegistration.dispatch(HandlerRegistration.java:105)
    at io.vertx.core.eventbus.impl.MessageConsumerImpl.deliver(MessageConsumerImpl.java:183)
    at io.vertx.core.eventbus.impl.MessageConsumerImpl.doReceive(MessageConsumerImpl.java:168)
    at io.vertx.core.eventbus.impl.HandlerRegistration.lambda$receive$0(HandlerRegistration.java:56)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:469)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:833)
    at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704)
    at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)
Caused by: org.apache.ibatis.executor.ExecutorException: Error creating lazy proxy.  Cause: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.crateProxy(JavassistProxyFactory.java:89)
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl.createProxy(JavassistProxyFactory.java:118)
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.createProxy(JavassistProxyFactory.java:59)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(DefaultResultSetHandler.java:648)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:404)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:361)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:335)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:308)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:201)
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:65)
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
    at org.apache.ibatis.executor.loader.ResultLoader.selectList(ResultLoader.java:81)
    at org.apache.ibatis.executor.loader.ResultLoader.loadResult(ResultLoader.java:70)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getNestedQueryMappingValue(DefaultResultSetHandler.java:874)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getPropertyMappingValue(DefaultResultSetHandler.java:511)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyPropertyMappings(DefaultResultSetHandler.java:487)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.getRowValue(DefaultResultSetHandler.java:411)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:361)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:335)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:308)
    at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:201)
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:65)
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:89)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
    ... 35 more
Caused by: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
    at com.oracle.svm.core.util.VMError.unimplemented(VMError.java:98)
    at java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:46)
    at org.apache.ibatis.javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:295)
    at org.apache.ibatis.javassist.util.proxy.DefineClassHelper$Java11.defineClass(DefineClassHelper.java:48)
    at org.apache.ibatis.javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:260)
    at org.apache.ibatis.javassist.util.proxy.FactoryHelper.toClass(FactoryHelper.java:154)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass3(ProxyFactory.java:640)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass2(ProxyFactory.java:624)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass1(ProxyFactory.java:560)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.createClass(ProxyFactory.java:481)
    at org.apache.ibatis.javassist.util.proxy.ProxyFactory.create(ProxyFactory.java:828)
    at org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory.crateProxy(JavassistProxyFactory.java:87)
    ... 67 more```
zhfeng commented 1 year ago

OK, I see. And can you provide a simple reproducer project or test case ? It could be very helpful.

VojtenkoRN commented 1 year ago

https://github.com/VojtenkoRN/test Run docker-compose up Exception in app container. Reproducer was maked on Linux machine, be aware of line endings trying it on Windows

zhfeng commented 1 year ago

Thanks and I will take a look.

Tikani commented 1 year ago

@zhfeng hope, a quick workaround exists (without rewriting of MyBatis low-level layers to bypass GraalVM limitations)

zhfeng commented 1 year ago

I checked the reproducer and it looks like mybatis wants to create a new class for Result during runtime. But unfortunately it does not work in GraalVM native mode. Because in native execution where it is a closed world, it needs to know everything at build time. I have to say there is no quick workaound currently. I will try to find a way to create these classes at build time. TBH, I'm not very sure if it is possible or we have to do some tricks at Mybatis.

Tikani commented 1 year ago

@zhfeng okay, i understood :( i've checked out similar projects (MyBatis + GraalVM), found this - https://github.com/mybatis/spring-native maybe, you could pick some "hacks" from it

zhfeng commented 1 year ago

Hmm, it's interesting! I just curious that did you folks try to test with spring-native and mybatis? I just go through the codes but have not found any trick related to ProxyFactory.