Mybatis Cause: java.sql.SQLException: Invalid value for getLong()异常解析

导读:本篇文章讲解 Mybatis Cause: java.sql.SQLException: Invalid value for getLong()异常解析,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

      首先,遇到这个问题的情况下就说明你的实体接收类重写了构造函数,并且你的构造函数无法对应上你的Sql查询语句。

      我的Mybatis的版本是3.5.0

      前阵子在用Mysql写select语句的时候,发生了 Invalid value for getLong() 异常,Debug发现Mabatis在用Long类型为返回的Varchar赋值(这边赋值表示Mysql返回的Rs给Java的Entity赋值),用Long给String当然赋值不上,因此就报错了。

       查Mybatis的gitHub可以知道,是构造函数搞的鬼。

       以下截图自Mybatis的中文版doc       Mybatis Cause: java.sql.SQLException: Invalid value for getLong()异常解析

       Mybatis在解析Mysql返回的数据的时候,会赋值给对应的Entity实体类。

       以下是具体的源码,类是  DefaultResultSetHandler       

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();
    // 1.当返回的结果是隶属于Java目前的类型的
    if (hasTypeHandlerForResultObject(rsw, resultType)) {
      return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
    // 2.如果我们在对应的Mapper方法上添加 @ConstructorArgs 注解,会走这里
    } else if (!constructorMappings.isEmpty()) {
      return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
    // 3.判断是否有默认的构造函数,如果有默认的构造函数,可以直接赋值
    } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
      return objectFactory.create(resultType);
    // 4.如果上述条件都不满足,并且设置了mybatis为自动装配
    } else if (shouldApplyAutomaticMappings(resultMap, false)) {
      return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
    }
    throw new ExecutorException("Do not know how to create an instance of " + resultType);
  }

     1.  Mybatis在初始化的时候会初始化一个 TYPE_HANDLER_MAP,用于存储常用的基本数据类型,如果我们返回的值是在此类型之中的,那么可以直接装配值,就不会发生上述问题。

      2.  如果我们操作比较骚气一点,在对应的查询方法上添加参数,当然一般情况下不会这么做的。

@ConstructorArgs(value = {@Arg(column = "symbol",javaType = String.class),@Arg(column = "start",javaType = BigDecimal.class),@Arg(column = "end",javaType = BigDecimal.class),@Arg(column = "date",javaType = Long.class),@Arg(column = "create_time",javaType = Long.class)})

     那 constructorMappings 将能解析到对应的参数,但是即使解析到了对应的参数,我们还是逃不过构造方法的命运,还是需要去寻找对应返回类的构造方法进行装配    

// type代表的是返回的装配的实体类,constructorArgTypes 代表的是我们已经赋值好的Mysql返回的值
// 需要找到一个构造函数长度和当前Mysql返回长度一致的,否则会报NoSuchMethodException
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));

     3. 如果能正常的走3,那基本就是可以正确的自动装配的。

     4. 因为Mybatis的 Configuration 帮我们设置了自动装配的属性,AutoMappingBehavior.PARTIAL,所以在前3种情况都不符合的情况下,会走当前方法。

     在当前方法下Mybatis会自动寻找当前类的所有构造函数,然后进行一一赋值,如果能找到类型转换正确不报异常的,那么恭喜你,可以通过。而如果找不到对应的,那么就会报上述异常,  Invalid value for getLong()。 

      有一点要注意的是:如果我们当前的查询结果是  select a,b,c from test。而我们的构造函数顺序如果是 b,c,a。如果a和b的类型一个是字符串,一个是数字的话,那么不好意思,也会报错。在经过反射之后,Mybatis不知道我们的column名字是什么,他暂且保留了构造函数的默认顺序来进行赋值,所以在这一块方面并不是那么的智能。但是如果a和b都隶属于数字类型的话,只要在解析初期能解析通过,那么就不会报错,虽然在解析初期的时候值会被赋予反,如果是Bigdecimal被赋值成Long,在这一步可能会精度丢失,但是 Mybatis 并不放心,会在赋值完成后检查一遍value是否赋值正确,如果赋值反了,会在 applyAutomaticMappings() 方法中自动赋值成正确的值,所以这部分还是比较智能的。

     

    说了这么多,说下具体的解决办法吧。

    1.不建议在Mybatis的实体类中重写他的构造函数,如果需要赋值,可以借助充血模型在里面另起炉灶,写一个方法进行赋值返回对应的实体类即可;

     2.如果很固执想自己写一个构造方法,那么记得要写一个默认的无参构造函数;

     3.如果不想写代码,可以借助使用  @NoArgsConstructor 注解加在实体类上,也是基于Lombok的注解,和 @Data注解区别在于,该注解会稳定生成一个 无参构造函数,而@Data注解下,如果写一个自己的构造函数,那么无参构造函数将被覆盖;

     4.最不建议的写法,比如你查询的语句是 select a,b,c from test。那么在对应的test实体类中写一个对应的构造函数用于赋值,代码如下:  

public test(String a,String b,String c){
    this.a = a;
    this.b = b;
    this.c = c;
}

      不建议的原因是因为每写一句Sql,就需要写一个对应的构造函数,很容易忘记造成Bug。

 

   源码看的不是特别深入,有什么不对的地方欢迎指出探讨

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/6468.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!