mybatis报错Error attempting to get column ‘id’ from result set. Cause: org.postgresql.util.PSQLException: Bad value for type int : 493987884173376\n;
1、事件起因:公司变更原本的自增id转成snowid,但是在测试过程中出现了一个select语句报错了,代码无变更,报错的内容大致是接收的对象不应该使用id来接收,因为数据库变更后是个bigint,正常得拿long来接收,但是问题是sql查询的确实有id,可是接收的对象类中是没有id字段的,正常不会接收这个id参数才对。对象类和sql贴在下方。
2、sql语句比较简单,仅仅是一个查询语句,附上sql和表结构
<select id="selectAutoPullGroupConfig"
resultType="com.hujing.hujing_sc_bussiness.pojo.vo.channel2.AutoPullGroupConfigVo">
select * from qw_pull_group_config
where corpid =
and state =
and deleted = 0
</select>
3、对象的接收类如下,其中可以看到报错的id字段在数据库中是存在的,但是对象类中并没有id字段:
@Data
public class AutoPullGroupConfigVo {
private Integer lastGroupSurplus;
private Integer pullGroupDays;
@NotNull
private Integer qrcodeType;
private String userConfig;
private Integer usingQrcodeOrder;
private Long createrId;
private String createrName;
private Long createrDeleted;
}
4、后来根据报错显示:
Error attempting to get column ‘id’ from result set.
也就是说在resultset中的id字段是存在问题的
我们找到了mybatis对接数据库,进行查询接收数据的流程如下,从建立连接到取回结果集,最终会拿到一个resultset
5、当我们拿到了resultset,接下来我们要做的肯定是和接收结果的对象类来做映射,问题呢就出现在这个地方,我们正好回顾一下取结果的过程。
→数据库在执行query方法的过程中,最后会去拿handleResultSets的结果,执行的过程我标红处理
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
closeResultSet(rsw.getResultSet());
}
}
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false;
final List<Class<?>> constructorArgTypes = new ArrayList<>();
final List<Object> constructorArgs = new ArrayList<>();
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
return resultObject;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
6、问题主要出现在这里!!
我们可以看到最后返回接收对象的结果类,是使用了构造函数来进行构造的,可是按照我们的对象类正常是无参构造才对,因为我们只用了@Data注解,正常情况是没有构造函数的,但是这里却走进了默认的构造函数。问题出现如下:
private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
Constructor<?>[] constructors = resultType.getDeclaredConstructors();
Constructor<?> defaultConstructor = this.findDefaultConstructor(constructors);
if (defaultConstructor != null) {
return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
} else {
Constructor[] var7 = constructors;
int var8 = constructors.length;
for(int var9 = 0; var9 < var8; ++var9) {
Constructor<?> constructor = var7[var9];
if (this.allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
}
}
throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
}
}
我们发现拿回来的构造函数是带有一个integer类型的,而mybatis对于构造函数的结果处理如下,其中会拿出构造函数的属性,那么拿到的会是一个integer,而根据取回的rsw所拿到的字段,第一个拿到的colunname是id,所以我们到现在明白了第一个问题 —> mybatis会根据构造函数进行一一对应,这个id对应的integer类型是在这里拿到的,那么问题回到了这个构造函数为什么会带一个integer呢???
我们发现这个integer是通过上面resultType.getDeclaredConstructors();拿到的,那么我们做了一个测试,就是脱离mybatis,单从jdk的角度确认他反射出来的对象的构造方法是什么样子的。
@Test
public void text() {
Class a = AutoPullGroupConfigVo.class;
Constructor<?>[] constructors = a.getDeclaredConstructors();
}
我们拿到的这个构造方法如下,发现这个构造方法也是带有Integer的,也就是说明他和mybatis无关,我们注意到这个对象类有一个特别之处在于使用了@NotNull这个注解,我们测试了一下去掉NotNull注解
下面是去掉NotNull注解的情况,发现变成了无参构造,那么问题就出现在了NotNull注解上
由此我们知道了,在mybatis结果集映射中,因为受到构造函数的影响,造成了值的对应错误,同时notNull注解会更改构造函数的结构,处理方法我们可以增加@NoArgsConstructor 和@AllArgsConstructor变为有参和无参构造,但是请注意,这样会使notNull的单参数构造失效,以上就是本次错误的排查总结过程。
附:我又测试了单全参注解,和单无参注解,得到的结果是:只有全参构造也报错,因为需要字段与类参数一一对应,无参构造则不走这个对应规则,只有无参不报错,这个以后继续来看mybatis的映射规则吧。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)