你可以使用Guava的不可变集合与MyBatis一起使用吗?

huangapple go评论72阅读模式
英文:

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:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
        PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
        &quot;https://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;
&lt;mapper namespace=&quot;org.acanthite.repository.UtilityRepository&quot;&gt;
    &lt;select id=&quot;getAllIds&quot; resultType=&quot;integer&quot;&gt;
        select id from utility;
    &lt;/select&gt;
&lt;/mapper&gt;

The mapper interface:

mapper接口:

@Mapper
public interface UtilityRepository {
  ImmutableList&lt;Integer&gt; getAllIds();
}

And how I use it:

以及我如何使用它的方式:

@ApplicationScoped
@AllArgsConstructor
public class UtilityService {
  private final UtilityRepository repository;
  
  public ImmutableList&lt;Integer&gt; 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:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
&lt;!DOCTYPE mapper
        PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
        &quot;https://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;
&lt;mapper namespace=&quot;org.acanthite.repository.UtilityRepository&quot;&gt;
    &lt;select id=&quot;getAllIds&quot; resultType=&quot;integer&quot;&gt;
        select id from utility;
    &lt;/select&gt;
&lt;/mapper&gt;

The mapper interface:

@Mapper
public interface UtilityRepository {
  ImmutableList&lt;Integer&gt; getAllIds();
}

And how I use it:

@ApplicationScoped
@AllArgsConstructor
public class UtilityService {
  private final UtilityRepository repository;
  
  public ImmutableList&lt;Integer&gt; 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() &amp;&amp; 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() &amp;&amp; (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      // ...
    }
    if (result == null &amp;&amp; method.getReturnType().isPrimitive() &amp;&amp; !method.returnsVoid()) {
      throw new BindingException(&quot;Mapper method &#39;&quot; + command.getName()
          + &quot;&#39; attempted to return null from a method with a primitive return type (&quot; + method.getReturnType() + &quot;).&quot;);
    }
    return result;
  }
  private &lt;E&gt; Object executeForMany(SqlSession sqlSession, Object[] args) {
    List&lt;E&gt; 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 &amp; arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      }
      return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
    }
    return result;
  }
  private &lt;E&gt; Object convertToDeclaredCollection(Configuration config, List&lt;E&gt; 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() &amp;&amp; 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() &amp;&amp; (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
// ...
}
if (result == null &amp;&amp; method.getReturnType().isPrimitive() &amp;&amp; !method.returnsVoid()) {
throw new BindingException(&quot;Mapper method &#39;&quot; + command.getName()
+ &quot;&#39; attempted to return null from a method with a primitive return type (&quot; + method.getReturnType() + &quot;).&quot;);
}
return result;
}
  private &lt;E&gt; Object executeForMany(SqlSession sqlSession, Object[] args) {
List&lt;E&gt; 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 &amp; arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
}
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
return result;
}
  private &lt;E&gt; Object convertToDeclaredCollection(Configuration config, List&lt;E&gt; 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&lt;Integer&gt; getAllIds();
}
@ApplicationScoped
@AllArgsConstructor
public class UtilityService {
private final UtilityRepository repository;
public ImmutableList&lt;Integer&gt; getAllIds() {
return ImmutableList.copyOf(repository.getAllIds());
}
}

huangapple
  • 本文由 发表于 2023年5月18日 00:46:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76274415.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定