英文:
How can I use Guava's immutable collections with mybatis?
问题
I tried making return type ImmutableList<Integer> and selecting a bunch of IDs from a table, but of course mybatis does not know how to do that and throws exception:
我尝试将返回类型设置为ImmutableList<Integer>并从表中选择一组ID,但是当然,mybatis不知道如何执行这个操作,因此会抛出异常:
org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.google.common.collect.ImmutableList with invalid types () or values (). Cause: java.lang.InstantiationException
at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:88)
at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:53)
at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:45)
at org.apache.ibatis.binding.MapperMethod.convertToDeclaredCollection(MapperMethod.java:173)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:154)
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.proxy5/jdk.proxy5.$Proxy76.getAllIds(Unknown Source)
at org.acanthite.services.UtilityService.getAllIds(UtilityService.java:98)
at org.acanthite.services.UtilityService_ClientProxy.getAllIds(Unknown Source)
at org.acanthite.resources.UtilityResource.ids(UtilityResource.java:77)
at org.acanthite.resources.UtilityResource$quarkusrestinvoker$ids_8602df045ce9b7e168a75788bbd936486ed83b98.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.InstantiationException
at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:66)
... 22 more
Here is my mapper's XML:
这是我的mapper的XML:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.acanthite.repository.UtilityRepository">
    <select id="getAllIds" resultType="integer">
        select id from utility;
    </select>
</mapper>
The mapper interface:
mapper接口:
@Mapper
public interface UtilityRepository {
  ImmutableList<Integer> getAllIds();
}
And how I use it:
以及我如何使用它的方式:
@ApplicationScoped
@AllArgsConstructor
public class UtilityService {
  private final UtilityRepository repository;
  
  public ImmutableList<Integer> getAllIds() {
    return repository.getAllIds();
  }
}
Is there a library or workaround teach mybatis how to instantiate Guava's immutable collections (or basically any other non-java collection)?
是否有库或解决方法可以教导mybatis如何实例化Guava的不可变集合(或基本上任何其他非Java集合)?
英文:
I tried making return type ImmutableList<Integer> and selecting a bunch of IDs from a table, but of course mybatis does not know how to do that and throws exception:
org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.google.common.collect.ImmutableList with invalid types () or values (). Cause: java.lang.InstantiationException
	at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:88)
	at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:53)
	at org.apache.ibatis.reflection.factory.DefaultObjectFactory.create(DefaultObjectFactory.java:45)
	at org.apache.ibatis.binding.MapperMethod.convertToDeclaredCollection(MapperMethod.java:173)
	at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:154)
	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.proxy5/jdk.proxy5.$Proxy76.getAllIds(Unknown Source)
	at org.acanthite.services.UtilityService.getAllIds(UtilityService.java:98)
	at org.acanthite.services.UtilityService_ClientProxy.getAllIds(Unknown Source)
	at org.acanthite.resources.UtilityResource.ids(UtilityResource.java:77)
	at org.acanthite.resources.UtilityResource$quarkusrestinvoker$ids_8602df045ce9b7e168a75788bbd936486ed83b98.invoke(Unknown Source)
	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
	at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.InstantiationException
	at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at org.apache.ibatis.reflection.factory.DefaultObjectFactory.instantiateClass(DefaultObjectFactory.java:66)
	... 22 more
Here is my mapper's XML:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.acanthite.repository.UtilityRepository">
    <select id="getAllIds" resultType="integer">
        select id from utility;
    </select>
</mapper>
The mapper interface:
@Mapper
public interface UtilityRepository {
  ImmutableList<Integer> getAllIds();
}
And how I use it:
@ApplicationScoped
@AllArgsConstructor
public class UtilityService {
  private final UtilityRepository repository;
  
  public ImmutableList<Integer> getAllIds() {
    return repository.getAllIds();
  }
}
Is there a library or workaround teach mybatis how to instantiate Guava's immutable collections (or basically any other non-java collection)?
答案1
得分: 1
以下是代码的关键部分:
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      // ...
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      // ...
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + "' attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      }
      return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
    }
    return result;
  }
  private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
    Object collection = config.getObjectFactory().create(method.getReturnType());
    MetaObject metaObject = config.newMetaObject(collection);
    metaObject.addAll(list);
    return collection;
  }
如果您需要更多翻译,请告诉我。
英文:
These are the key parts of the code:
  public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
// ...
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
// ...
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ "' attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
}
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
return result;
}
  private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
Object collection = config.getObjectFactory().create(method.getReturnType());
MetaObject metaObject = config.newMetaObject(collection);
metaObject.addAll(list);
return collection;
}
https://mybatis.org/mybatis-3/zh/jacoco/org.apache.ibatis.binding/MapperMethod.java.html
Unfortunately, it seems like the only options are to use the original list, convert into a new collection using no-arg-constructor + addAll, or convert into an array. Therefore, I would do this:
@Mapper
public interface UtilityRepository {
List<Integer> getAllIds();
}
@ApplicationScoped
@AllArgsConstructor
public class UtilityService {
private final UtilityRepository repository;
public ImmutableList<Integer> getAllIds() {
return ImmutableList.copyOf(repository.getAllIds());
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论