英文:
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());
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论