mybatis if test 采坑记
前言
最近遇到的一个关于mybatis的xml条件判断的问题,平时写的都是以判断空属性是否为空,例如:
<if test="type!=null and type !=''">
and status = 1
</if>
所以在这一块并没有足够的重视。
正文
最近刚好要写一个与上边不一样的写法,但是还是以上面的为基础,是对属性的值进行判断:
判断type属性的值
<if test="type!=null and type !=''">
<if test="type=='6'">
and status = 61
</if>
<if test="type!='6'">
and status = 2
</if>
</if>
结果就是无论我传什么值,SQL中都是执行的status=2。
在网上也找到了很多解决办法,但是都不是自己想要的结果;
网上的解决办法
- 内双外单
<if test='type=="6"'>
and status = 1
</if>
- 加.toString()
<if test="type=='6'.toString()">
and status = 1
</if>
源码分析
从参考文章1中可以知道,mybatis使用ognl表达式来进行解析的;下面使用ognl来模拟解析;且不使用上面的两种方法;
pom引入
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.2.1</version>
</dependency>
测试代码:
// 构建一个OgnlContext对象
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(this,
new DefaultClassResolver(),
new DefaultTypeConverter(),
new DefaultMemberAccess(true));
Map<String,Object> hashMap=new HashMap<>(1);
hashMap.put("name", "aa");
context.setRoot(hashMap);
String expression="name =='aa'";
try {
Boolean flag = (Boolean) Ognl.getValue(expression, context, context.getRoot());
System.out.println(flag);
} catch (OgnlException e) {
e.printStackTrace();
}
自己的解决办法
除了网上的那两种办法,还有其他的一些方法可以解决我遇到的问题;
- 由于我比较的是数字类型,将传入的值改为数字类型的值传入,再将mybatis的xml中的单引号去掉,如下:
hashMap.put("name", 6);
context.setRoot(hashMap);
String expression="name ==6";
ognl源码解析
下面对ognl的源码进行分析:
1. 根据Ognl.getValue方法定位到 OgnlOps类的isEqual方法
public static boolean isEqual(Object object1, Object object2)
{
boolean result = false;
// 1. 判断两个对象是否为同一个对象
if (object1 == object2) {
result = true;
} else {
// 2. 集合判断
if ((object1 != null) && object1.getClass().isArray()) {
if ((object2 != null) && object2.getClass().isArray() && (object2.getClass() == object1.getClass())) {
result = (Array.getLength(object1) == Array.getLength(object2));
if (result) {
for(int i = 0, icount = Array.getLength(object1); result && (i < icount); i++) {
result = isEqual(Array.get(object1, i), Array.get(object2, i));
}
}
}
} else {
// 3. 非集合判断
// 3.1 当两个值都为String类型时,就直接equals后就返回了,如果不是,则进入compareWithConversion方法;
// Check for converted equivalence first, then equals() equivalence
result = (object1 != null) && (object2 != null)&& (object1.equals(object2) || (compareWithConversion(object1, object2) == 0));
}
}
return result;
}
这个方法传入了两个值,第一个值是调用mybatis方法传入的值,这个值的类型就有很多种了,例如BigDecimal,String,Integer等等,
第二个是从mybatis的xml中读取出来的值,这个值要根据xml文件中的写法来判断了:
- 外双内单
1.1 单长度为1时读取为Character,
1.2 单长度大于1时,读取为String
再根据第三个判断定位到compareWithConversion方法
2. compareWithConversion方法
- 该方法以getNumericType方法来判断参数数字类型,其实可以根据方法名判断出,进入到这里后,都是做数字类型判断,再转换,再比较
- 在转换类型的过程中,如果传入的字符无法被转换,将抛出错误,例如:传入的值为字母就无法被转换;
- 当mybatis的xml文件中的判断值为""或者''时,该方法会将值赋值为0;
- 当mybatis的xml文件中的判断值为'',且里面的值为单数值时,注意转换的值并不是原值;例如:'6'解析为double:54.0
public static int compareWithConversion(Object v1, Object v2)
{
int result;
if (v1 == v2) {
result = 0;
} else {
// 1
int t1 = getNumericType(v1), t2 = getNumericType(v2), type = getNumericType(t1, t2, true);
switch(type) {
case BIGINT:
result = bigIntValue(v1).compareTo(bigIntValue(v2));
break;
case BIGDEC:
result = bigDecValue(v1).compareTo(bigDecValue(v2));
break;
case NONNUMERIC:
if ((t1 == NONNUMERIC) && (t2 == NONNUMERIC)) {
if ((v1 instanceof Comparable) && v1.getClass().isAssignableFrom(v2.getClass())) {
result = ((Comparable) v1).compareTo(v2);
break;
} else {
throw new IllegalArgumentException("invalid comparison: " + v1.getClass().getName() + " and "
+ v2.getClass().getName());
}
}
// else fall through
case FLOAT:
case DOUBLE:
// 这里注意doubleValue方法,当传入的值为字母时,会抛出错误;
double dv1 = doubleValue(v1),
dv2 = doubleValue(v2);
return (dv1 == dv2) ? 0 : ((dv1 < dv2) ? -1 : 1);
default:
long lv1 = longValue(v1),
lv2 = longValue(v2);
return (lv1 == lv2) ? 0 : ((lv1 < lv2) ? -1 : 1);
}
}
return result;
}
总结
-
如果属性不为数字类型,使用上边的网上的解决办法就差不多可以了
-
如果属性为数字类型,例如Integer,BigDecimal等等会有一个问题
当传入的值为0时,且当mybatis的xml文件中的比较为''或者""时,比较的结果就是false,如下:
// 1 双引号"",传入的值为0,或者其他类型为0的值
hashMap.put("name",0);
hashMap.put("name",BigDecimal.ZERO);
String expression="name!=null and name!=\"\"";
// 2 单引号,传入的值为0,或者其他类型为0的值
hashMap.put("name",0);
hashMap.put("name",BigDecimal.ZERO);
String expression="name!=null and name!=''";
解决办法:
- 将传入的值类型转换为String类型
- 在mybatis的xml中修改,如下:
<if test="infSrc != null and infSrc!=''">AND INF_SRC = #{infSrc,jdbcType=BIGINT}</if>
// 或者
<if test="infSrc != null and infSrc!=''">AND INF_SRC = #{infSrc,jdbcType=DECIMAL}</if>
这个根据自己传入值类型而定;
最后
项目地址:https://github.com/guodayede/java-study/tree/master/ognl