原文出处:
光闪
开端
今天小朋友X在开发过程中遇到了一个bug,并给mybatis提了一个ISSUE:throw ReflectionException when using #{array.length}
大致说明下该问题,在mapper.xml中,使用#{array.length}来获取数组的长度时,会报出ReflectionException。 代码:
public List<QuestionnaireSent> selectByIds(Integer[] ids) { return commonSession.selectList("QuestionnaireSentMapper.selectByIds", ImmutableMap.of("ids", ids)); }
对应的xml:
<select id="selectByIds"> SELECT * FROM t_questionnaire <if test="ids.length > 0"> WHERE id in <foreach collection="ids" open="(" separator="," close=")" item="id">#{id} </foreach> </if> LIMIT #{ids.length} </select>
下面结合源码对该问题进行分析
分析
xml中有两处使用了length,那么这个报错究竟是哪个引起的呢?
尝试把test条件去掉,limit保留后,依然报错。那么可定位出报错是#{ids.length}导致的。
由此引出了两个问题:
- XML标签中条件是如何解析的(扩展,foreach是如何解析的数组和集合)
- #{ids.length}是如何解析的
带着这两个问题,我们进入源码
第一部分 XML标签的解析
在类org.apache.ibatis.scripting.xmltags.XMLScriptBuilder中
private void initNodeHandlerMap() { nodeHandlerMap.put("trim", new TrimHandler()); nodeHandlerMap.put("where", new WhereHandler()); nodeHandlerMap.put("set", new SetHandler()); nodeHandlerMap.put("foreach", new ForEachHandler()); nodeHandlerMap.put("if", new IfHandler()); nodeHandlerMap.put("choose", new ChooseHandler()); nodeHandlerMap.put("when", new IfHandler()); nodeHandlerMap.put("otherwise", new OtherwiseHandler()); nodeHandlerMap.put("bind", new BindHandler()); } protected MixedSqlNode parseDynamicTags(XNode node) { List<SqlNode> contents = new ArrayList<SqlNode>(); NodeList children = node.getNode().getChildNodes(); for (int i = 0; i < children.getLength(); i++) { XNode child = node.newXNode(children.item(i)); if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) { String data = child.getStringBody(""); TextSqlNode textSqlNode = new TextSqlNode(data); if (textSqlNode.isDynamic()) { contents.add(textSqlNode); isDynamic = true; } else { contents.add(new StaticTextSqlNode(data)); } } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628 String nodeName = child.getNode().getNodeName(); NodeHandler handler = nodeHandlerMap.get(nodeName); if (handler == null) { throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); } handler.handleNode(child, contents); isDynamic = true; } } return new MixedSqlNode(contents); }
在每个对应的Handler中,有相应的处理逻辑。
以IfHandler为例:
private class IfHandler implements NodeHandler { public IfHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); String test = nodeToHandle.getStringAttribute("test"); IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test); targetContents.add(ifSqlNode); } }
在这里主要生成了IfSqlNode,解析在相应的类中
public class IfSqlNode implements SqlNode { private final Expre