关于内部mybatis升级版本(3.2.8->3.3.1)之后出现的问题说明
关于时间类型的在xml里面进行判空抛的异常
mybatis版本是3.3.1
错误代码
update pe_user_product_delay
set
status = #{status,jdbcType=TINYINT},
update_time = now(),
update_uid = #{updateUid,jdbcType=VARCHAR},
audit_end_time = #{auditEndTime,jdbcType=TIMESTAMP},
update_username = #{updateUsername,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
此处需要注意的是null != auditEndTime and auditEndTime!=''
这个判断,代码中传入的auditEndTime
是date
类型,此处异常信息为
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()
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()
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 pe_user_product_delay
set
status = #{status,jdbcType=TINYINT},
update_time = now(),
update_uid = #{updateUid,jdbcType=VARCHAR},
audit_end_time = #{auditEndTime,jdbcType=TIMESTAMP},
update_username = #{updateUsername,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
直接把判断空串的部分删掉即可…
这边需要注意一下,对于xml的判断,要符合OGNL的规范,有些写法可能目前并未进行校验,但是后期版本很有可能添加上这种判断。
另外对于业务代码,这里有明显的逻辑性错误,传入的是date类型,却要进行空串判断,这种逻辑不能出现,尤其是框架本身并未明说支持这种写法的情况下,谨慎使用。