MybatisPlus出现Error attempting to get column ‘xxx字段‘ from result set异常解决
作者:段守志
MybatisPlus如何处理实体枚举类型转换的教程可以参考:点击查看教程,本文重点分析使用@EnumValue注解转换时遇到的一下错误原因,以及解决方案。
一、背景描述
在操作MybatisPlus的实体属性一般都是基本类型或者对应的引用类型或者String类型。但是MySQL8早已支持enum枚举类型,一直都没有实际操作过,于是想着有个枚举的场景打算实际项目里面用一下。当时在具体数据库表中设计了一个字段state来表示状态,这个状态有3个值:已入组、已出组、已移除。于是设计了这个字段为enum类型属性。但是在具体查询的时候出现如下的错误:

二、案例分析
当时具体场景比较简单就两个类:实体类、枚举类
实体类(demo)
实体类的定义具体如下,重点关注属性:state
@Data
@EqualsAndHashCode
@TableName("demo")
public class Demo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 身份证号
*/
@TableField("id_card")
private String idCard;
/**
* 受试者姓名
*/
@TableField("`name`")
private String name;
/**
* 联系电话
*/
@TableField("phone")
private String phone;
/**
* 入组时间
*/
@TableField("join_time")
private Date joinTime;
/**
* 状态:已入组、已出组、已移除
*/
@TableField("state")
private DemoStateEnum state;
/**
* 最近一次操作的用户
*/
@TableField("last_user")
private String lastUser;
/**
* 关联项目ID
*/
@TableField("project_id")
private Integer projectId;
/**
* 最后一次操作时间
*/
@TableField("last_update")
private Date lastUpdate;
public static Demo builder(){
return new Demo();
}
}
枚举类(DemoStateEnum)
public enum DemoStateEnum {
JOINED("已入组"),
OUTED("已出组"),
REMOVED("已移除");
/**
* 注意这个 @EnumValue注解一定要加上,否则SpringBoot启动都会报错,具体原因是因为MybatisePlus实现了根据这个注解表示实体属性自动枚举类型转换。如果找不到就会报错。
*/
@EnumValue
private String state;
DemoStateEnum(String state){
this.state = state;
}
/**
* @JSONField这个注解解决alibaba.fastjson序列化是枚举显示的索引的值,而我们想要的显示是具体的值:已入组、已出组、已移除等。
*/
@JSONField
public String getState() {
return state;
}
@Override
public String toString() {
return "DemoStateEnum{" +
"state='" + state + '\'' +
'}';
}
}
表结构
CREATE TABLE `demo` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`id_card` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '身份证号',
`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '受试者姓名',
`phone` varchar(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '联系电话',
`join_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '入组时间',
`state` enum('已入组','已出组','已移除') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '状态:已入组、已出组、已移除',
`last_user` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最近一次操作的用户',
`project_id` int NOT NULL COMMENT '关联项目ID',
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后一次操作时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='枚举测试表';
三、单元测试
@SpringBootTest
public class DemoTest {
@Autowired
private DemoMapper deemoMapper;
//查询所有demo数据
@Test
public void querySubject() throws JsonProcessingException {
List<Demo> demos = deemoMapper.selectList(null);
System.err.println(JSON.toJSONString(demos));
}
}
执行以上单元测试会出现上面的错误,这里我就再贴一下图:

四、问题分析
# 根据上图细看这句话 Caused by: java.sql.SQLFeatureNotSupportedException at com.alibaba.druid.pool.DruidPooledResultSet.getObject(DruidPooledResultSet.java:1771)
找到DruidPooledResultSet.getObject()这个方法打上一个断点,然后根据idea的线程调用栈debug往上确定是由哪一步出现的异常。

如上图设置好debug点,开始运行上面的单元测试方法querySubject方法,监听断点:

那怎么知道上层是那个方法触发了这个异常呢,接着看左边的线程调用栈:可以看出黄色部分是上层真实异常的调用方。

基于以上我们继续往下分析,可以发现MybatisEnumTypeHandler#getNullableResult(java.sql.ResultSet, java.lang.String)这个方法里面的ResultSet的具体参数类型是一个DruidPooledResultSet,然后再调用DruidPooledResultSet类中的getObject(String columnLabel, Class type)方法。
public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
显然这个方法无论谁调用都是会抛出SQLFeatureNotSupportedException异常,那这样的话这个方法又有什么意义呢,于是猜想着是不是当前的druid是1.1.16(从下图可以看出版本依赖)版本太低导致无法适配当前查询场景,既然版本太低那就升级一下druid版本到1.1.21。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>

升级版本1.1.21继续尝试着运行,发现确实成功解决了。从下图可以看出druid版本成功升级从黄色区域可以看出来,那为什么可以解决当前问题呢?于是尝试进入同样的DruidPooledResultSet类中的getObject(String columnLabel, Class type)方法。发现1.1.16版本中的固定异常SQLFeatureNotSupportedException没有了,实现也完全不同了。如下:
public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
try {
// 正常调用JDBC中的ResultSet了,所以可以正常获取数据,不再出现那个异常了。
return this.rs.getObject(columnLabel, type);
} catch (Throwable var4) {
throw this.checkException(var4);
}
}

查询成功:

[{"id":4,"idCard":"666666","joinTime":"2023-08-09 15:21:13","lastUpdate":"2023-08-09 15:21:13","lastUser":"test user","name":"test name 6","phone":"123123123","projectId":2,"state":"已出组"},{"id":5,"idCard":"666666","joinTime":"2023-08-09 15:22:28","lastUpdate":"2023-08-09 15:22:28","lastUser":"test user","name":"test name 6","phone":"123123123","projectId":2,"state":"已出组"},{"id":6,"idCard":"666666","joinTime":"2023-08-09 15:24:00","lastUpdate":"2023-08-09 15:24:00","lastUser":"test user","name":"test name 6","phone":"123123123","projectId":2,"state":"已出组"}]
五、其他问题
alibaba.fastjson序列化枚举时候显示一个int值得问题,其实这个int值就是枚举中的ordinary也就是枚举列表中的索引。这个了解java枚举的应该都清楚枚举列表中的值都有自己对应的所有,而且底层也是存储在一个数组中的,也就是相当于数组的下表。但是不希望在序列化的时候显示这个下表,希望显示state的值。如何解决,在枚举的state属性的get方法加上如下@JSONField注解,这样就可以显示state的枚举值了。
@JSONField
public String getState() {
return state;
}到此这篇关于MybatisPlus出现Error attempting to get column ‘xxx字段‘ from result set异常解决的文章就介绍到这了,更多相关mybatis ‘xx‘ from result set内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- MyBatis异常java.sql.SQLSyntaxErrorException的问题解决
- Mybatis操作数据时出现:java.sql.SQLSyntaxErrorException: Unknown column 'XXX' in 'field list'的问题解决
- MybatisPlusException:Failed to process,Error SQL异常报错的解决办法
- Mybatis配置错误:java.lang.ExceptionInInitializerError
- MybatisPlus BaseMapper 中的方法全部 Invalid bound statement (not found Error处理)
- 解决Mybatis出现报错Error querying database.Cause: java.lang.IndexOutOfBoundsException: Index 9 out of
