public class Account { private Integer id; private String firstName; private String lastName; private String emailAddress; //新增一个Object类型的组合对象 private Object primaryKey; //.... }
maping配置:
<select id="selectAccount" parameterClass="Account" resultMap="AccountResult"> select ACC_ID , ACC_FIRST_NAME , ACC_LAST_NAME , ACC_EMAIL from ACCOUNT <dynamic prepend="where"> <isEqual property="primaryKey.id" prepend="and" compareValue="1"> ACC_ID = 2 </isEqual> </dynamic> </select>
程序中代码:
Account a = new Account(); Account a2 = new Account(); a2.setId(new Integer(1)); a.setPrimaryKey(a2); List list = (List) sqlMapper.queryForList("selectAccount", a);
很不幸,会报告Object类中没有id属性。why?
这个问题原因,是因为ibatis在判断属性值的时候,不是运行时候动态从设置的对象中获取,而是直接从缓存中获取确切的配置类信息:
com.ibatis.common.beans.ComplexBeanProbe中的代码:
public Class getPropertyTypeForGetter(Object object, String name) { Class type = object.getClass(); if (object instanceof Class) { type = getClassPropertyTypeForGetter((Class) object, name); } else if (object instanceof Map) { Map map = (Map) object; Object value = map.get(name); if (value == null) { type = Object.class; } else { type = value.getClass(); } } else { if (name.indexOf('.') > -1) { StringTokenizer parser = new StringTokenizer(name, "."); while (parser.hasMoreTokens()) { name = parser.nextToken(); //看看这里的type你会全明白了 type = ClassInfo.getInstance(type).getGetterType(name); } } else { type = ClassInfo.getInstance(type).getGetterType(name); } } return type; }
com.ibatis.common.beans.ClassInfo中getGetterType方法
public Class getGetterType(String propertyName) { Class clazz = (Class) getTypes.get(propertyName); if (clazz == null) { throw new ProbeException("There is no READABLE property named '" + propertyName + "' in class '" + className + "'"); } return clazz; }
解决方法是使用反射获取真实的对象信息:
public Class getPropertyTypeForSetter(Object object, String name) { Class type = object.getClass(); if (object instanceof Class) { type = getClassPropertyTypeForSetter((Class) object, name); } else if (object instanceof Map) { Map map = (Map) object; Object value = map.get(name); if (value == null) { type = Object.class; } else { type = value.getClass(); } } else { if (name.indexOf('.') > -1) { //用以下代码替代了上面StringTokenizer部分,其它地方同理。 //虽然反射效率较低,但此次使用反射可以让程序有更大的灵活性 String[] names = name.split("\\."); for (int i = 0; i < names.length; ++i) { try { Method method = ClassInfo.getInstance(type).getGetter(names[i]); object = method.invoke(object, new Object[]{}); if (object != null) { type = object.getClass(); } else { type = null; break; } } catch (Exception e) { throw new RuntimeException(e); } } } else { type = ClassInfo.getInstance(type).getSetterType(name); } } return type; }