mybatis踩坑之"time!=''"

2019-07-29  本文已影响0人  kevin0016

关于内部mybatis升级版本(3.2.8->3.3.1)之后出现的问题说明

关于时间类型的在xml里面进行判空抛的异常

mybatis版本是3.3.1

错误代码

    <update id="updateUserProductDelayStatusAndAuditEndTime"
        parameterType="com.koolearn.sharks.model.UserProductDelay">
        update pe_user_product_delay
        set
        status = #{status,jdbcType=TINYINT},
        update_time = now(),
        update_uid = #{updateUid,jdbcType=VARCHAR},
        <if test="null != auditEndTime and auditEndTime!=''">
            audit_end_time = #{auditEndTime,jdbcType=TIMESTAMP},
        </if>
        update_username = #{updateUsername,jdbcType=VARCHAR}
        where id = #{id,jdbcType=INTEGER}
    </update>

此处需要注意的是null != auditEndTime and auditEndTime!=''这个判断,代码中传入的auditEndTimedate类型,此处异常信息为

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
### Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:75)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368)
    at com.sun.proxy.$Proxy26.update(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:254)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:55)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
    at com.sun.proxy.$Proxy94.updateUserProductDelayStatusAndAuditEndTime(Unknown Source)
    at com.koolearn.sharks.service.impl.UserProductDelayServiceImpl.updateUserProductDelayStatusAndAuditEndTime(UserProductDelayServiceImpl.java:360)
    at com.koolearn.sharks.service.impl.UserProductDelayServiceImpl$$FastClassBySpringCGLIB$$4517d719.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:700)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at com.koolearn.common.dubbo.logger.AccessLoggerInterceptor.invoke(AccessLoggerInterceptor.java:25)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at com.koolearn.common.dubbo.logger.PerformanceLoggerInterceptor.invoke(PerformanceLoggerInterceptor.java:22)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:633)
    at com.koolearn.sharks.service.impl.UserProductDelayServiceImpl$$EnhancerBySpringCGLIB$$9e102bd3.updateUserProductDelayStatusAndAuditEndTime(<generated>)
    at com.koolearn.sharks.service.impl.demotest.test2(demotest.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
### Cause: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:172)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358)
    ... 44 more
Caused by: java.lang.IllegalArgumentException: invalid comparison: java.util.Date and java.lang.String
    at org.apache.ibatis.ognl.OgnlOps.compareWithConversion(OgnlOps.java:92)
    at org.apache.ibatis.ognl.OgnlOps.isEqual(OgnlOps.java:142)
    at org.apache.ibatis.ognl.OgnlOps.equal(OgnlOps.java:794)
    at org.apache.ibatis.ognl.ASTNotEq.getValueBody(ASTNotEq.java:53)
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
    at org.apache.ibatis.ognl.ASTAnd.getValueBody(ASTAnd.java:61)
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:494)
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:458)
    at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:44)
    at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32)
    at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34)
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33)
    at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:41)
    at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:279)
    at com.koolearn.framework.mybatis.datasource.plugin.CatPlugin.intercept(CatPlugin.java:27)
    at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
    at com.sun.proxy.$Proxy168.update(Unknown Source)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:170)
    ... 49 more

其中重点的错误信息提示是invalid comparison: java.util.Date and java.lang.String说明类型转换异常,直接找到错误信息的锁定位的源码查看

源码路径为:org.apache.ibatis.ognl.OgnlOps#compareWithConversion

public static int compareWithConversion(Object v1, Object v2) {
        int result;
        if (v1 == v2) {
            result = 0;
        } else {
            int t1 = getNumericType(v1);
            int t2 = getNumericType(v2);
            int type = getNumericType(t1, t2, true);
            switch(type) {
            case 6:
                result = bigIntValue(v1).compareTo(bigIntValue(v2));
                break;
            case 9:
                result = bigDecValue(v1).compareTo(bigDecValue(v2));
                break;
            case 10:
                if (t1 == 10 && t2 == 10) {
                    if (v1 instanceof Comparable && v1.getClass().isAssignableFrom(v2.getClass())) {
                        result = ((Comparable)v1).compareTo(v2);
                        break;
                    }

                    throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and " + v2.getClass().getName());
                }
            case 7:
            case 8:
                double dv1 = doubleValue(v1);
                double dv2 = doubleValue(v2);
                return dv1 == dv2 ? 0 : (dv1 < dv2 ? -1 : 1);
            default:
                long lv1 = longValue(v1);
                long lv2 = longValue(v2);
                return lv1 == lv2 ? 0 : (lv1 < lv2 ? -1 : 1);
            }
        }

        return result;
    }

其中v1和v2分别是类型对比中的auditEndTime‘’此处是添加了这样的一个判断导致抛出这样的异常

对比3.2.8版本中的代码实现

public static int compareWithConversion(Object v1, Object v2, boolean equals) {
        int result;
        if (v1 == v2) {
            result = 0;
        } else {
            int t1 = getNumericType(v1);
            int t2 = getNumericType(v2);
            int type = getNumericType(t1, t2, true);
            switch(type) {
            case 6:
                result = bigIntValue(v1).compareTo(bigIntValue(v2));
                break;
            case 9:
                result = bigDecValue(v1).compareTo(bigDecValue(v2));
                break;
            case 10:
                if (t1 == 10 && t2 == 10) {
                    if (v1 != null && v2 != null) {
                        if (v1.getClass().isAssignableFrom(v2.getClass()) || v2.getClass().isAssignableFrom(v1.getClass())) {
                            if (v1 instanceof Comparable) {
                                result = ((Comparable)v1).compareTo(v2);
                                break;
                            }

                            if (equals) {
                                result = v1.equals(v2) ? 0 : 1;
                                break;
                            }
                        }

                        if (!equals) {
                            throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and " + v2.getClass().getName());
                        }

                        result = 1;
                        break;
                    } else {
                        boolean var10000 = v1 != v2;
                    }
                }
            case 7:
            case 8:
                double dv1 = doubleValue(v1);
                double dv2 = doubleValue(v2);
                return dv1 == dv2 ? 0 : (dv1 < dv2 ? -1 : 1);
            default:
                long lv1 = longValue(v1);
                long lv2 = longValue(v2);
                return lv1 == lv2 ? 0 : (lv1 < lv2 ? -1 : 1);
            }
        }

        return result;
    }

严格来说这里其实并不是mybatis的源码,而是ognl解析xml出来的代码,mybatis的3.3.X版本主要升级点就是把ognl的版本升级到了3.0.11,这个版本优化了对于xml解析流程

修改方法:

    <update id="updateUserProductDelayStatusAndAuditEndTime"
        parameterType="com.koolearn.sharks.model.UserProductDelay">
        update pe_user_product_delay
        set
        status = #{status,jdbcType=TINYINT},
        update_time = now(),
        update_uid = #{updateUid,jdbcType=VARCHAR},
        <if test="null != auditEndTime">
            audit_end_time = #{auditEndTime,jdbcType=TIMESTAMP},
        </if>
        update_username = #{updateUsername,jdbcType=VARCHAR}
        where id = #{id,jdbcType=INTEGER}
    </update>

直接把判断空串的部分删掉即可…

这边需要注意一下,对于xml的判断,要符合OGNL的规范,有些写法可能目前并未进行校验,但是后期版本很有可能添加上这种判断。

另外对于业务代码,这里有明显的逻辑性错误,传入的是date类型,却要进行空串判断,这种逻辑不能出现,尤其是框架本身并未明说支持这种写法的情况下,谨慎使用。

上一篇下一篇

猜你喜欢

热点阅读