本章中分析insert元素的解析。java
1 配置文件node
<insert id="insert" parameterType="cn.vansky.schedule.time.menu.bo.Menu"> <!-- WARNING - @mbggenerated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Fri Aug 14 16:08:36 CST 2015. --> insert into tb_menu (menu_name, menu_remark, menu_parent_id, menu_url, is_show, is_delete, operation_user_name, operation_time) values (#{menuName,jdbcType=VARCHAR}, #{menuRemark,jdbcType=VARCHAR}, #{menuParentId,jdbcType=INTEGER}, #{menuUrl,jdbcType=VARCHAR}, #{isShow,jdbcType=TINYINT}, #{isDelete,jdbcType=TINYINT}, #{operationUserName,jdbcType=VARCHAR}, #{operationTime,jdbcType=TIMESTAMP}) </insert>
2 方法buildStatementFromContextsql
private void buildStatementFromContext(List<XNode> list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); } private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { // 对一个SQL进行解析 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }
上面是一个总的方法包括,insert、update、delete、select4中元素都会进入此方法。这里也会捕获一种异常,并把错误的解析放入Configuration(全局配置类)的Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>()。express
3 方法parseStatementNodeapache
public void parseStatementNode() { // insert String id = context.getStringAttribute("id"); // null String databaseId = context.getStringAttribute("databaseId"); // 第一次检查这里是不经过的,直接跳过 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return; // null Integer fetchSize = context.getIntAttribute("fetchSize"); // null Integer timeout = context.getIntAttribute("timeout"); // null String parameterMap = context.getStringAttribute("parameterMap"); // cn.vansky.schedule.time.menu.bo.Menu String parameterType = context.getStringAttribute("parameterType"); // class cn.vansky.schedule.time.menu.bo.Menu Class<?> parameterTypeClass = resolveClass(parameterType); // null String resultMap = context.getStringAttribute("resultMap"); // null String resultType = context.getStringAttribute("resultType"); // null String lang = context.getStringAttribute("lang"); // 获取默认的处理对象 // org.apache.ibatis.scripting.xmltags.XMLLanguageDriver LanguageDriver langDriver = getLanguageDriver(lang); // null Class<?> resultTypeClass = resolveClass(resultType); // null String resultSetType = context.getStringAttribute("resultSetType"); // PREPARED StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); // null ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); // insert String nodeName = context.getNode().getNodeName(); // INSERT SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); // false boolean isSelect = sqlCommandType == SqlCommandType.SELECT; // true boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); // false boolean useCache = context.getBooleanAttribute("useCache", isSelect); // false boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); // 解析<include refid="Base_Column_List" /> includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // 这里经过下面分析获取的SQL源(静态SQL源),由于没有动态标签 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); // null String resultSets = context.getStringAttribute("resultSets"); // null String keyProperty = context.getStringAttribute("keyProperty"); // null String keyColumn = context.getStringAttribute("keyColumn"); // org.apache.ibatis.executor.keygen.NoKeyGenerator KeyGenerator keyGenerator; // insert!selectKey String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; // cn.vansky.schedule.time.menu.dao.MenuMapper.insert!selectKey keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
4 方法XMLLanguageDriver.createSqlSourceapp
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); // 入参Class及节点解析 return builder.parseScriptNode(); }
5 方法XMLScriptBuilder.parseScriptNodeide
public SqlSource parseScriptNode() { // 解析动态标签,if、where、for List<SqlNode> contents = parseDynamicTags(context); // 全部动态标签集合类 MixedSqlNode rootSqlNode = new MixedSqlNode(contents); SqlSource sqlSource = null; if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; }
6 方法XMLScriptBuilder.parseDynamicTagsfetch
private List<SqlNode> 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(""); // 内容SqlNode 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 // 节点为元素,也就是动态标签where、if、for String nodeName = child.getNode().getNodeName(); // 获取动态标签处理器WhereHandler、ForEachHandler NodeHandler handler = nodeHandlers.get(nodeName); if (handler == null) { throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); } // 处理器处理内容,并把处理的内容加入到SQLNode handler.handleNode(child, contents); isDynamic = true; } } return contents; }
方法5中有2中数据源DynamicSqlSource(动态SQL源)和RawSqlSource(静态SQL源),动态SQL源就是包括where、if、for,静态SQL源就是只包括SQL语句,下面就简单分析一下2中SQL源。ui
7 RawSqlSource(静态SQL源)
this
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) { SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> clazz = parameterType == null ? Object.class : parameterType; sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>()); } public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { // 参数处理Mapping ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); // 解析XML中的SQL语句,#{menuName,jdbcType=VARCHAR},转换成?及对应的处理类型ParameterMapping String sql = parser.parse(originalSql); return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }
这里直接解析并生成StaticSqlSource,包括SQL语句及对应的处理类型。
7.1 ParameterMapping对应的menuName
/** 全局配置类 */ private Configuration configuration; /** menuName */ private String property; /** IN:输入 */ private ParameterMode mode; /** class java.lang.String */ private Class<?> javaType = Object.class; /** VARCHAR */ private JdbcType jdbcType; /** null */ private Integer numericScale; /** StringTypeHandler */ private TypeHandler<?> typeHandler; /** null */ private String resultMapId; /** null */ private String jdbcTypeName; /** null */ private String expression;
8 DynamicSqlSource(动态SQL源)
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) { this.configuration = configuration; this.rootSqlNode = rootSqlNode; }
很明显这里没有进行生成SQL,那就是在具体获取SQL时进行生成SQL
9 方法addMappedStatement
此方法只是赋值,没有什么特殊处理,因此此方法不在分析,能够自行研究。
总结
附上图片MappedStatement属性值
下面给出动态SQL源的MappedStatement属性值
<insert id="insertSelective" parameterType="cn.vansky.schedule.time.menu.bo.Menu"> <!-- WARNING - @mbggenerated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Fri Aug 14 16:08:36 CST 2015. --> insert into tb_menu <trim prefix="(" suffix=")" suffixOverrides=","> <if test="menuName != null"> menu_name, </if> <if test="menuRemark != null"> menu_remark, </if> <if test="menuParentId != null"> menu_parent_id, </if> <if test="menuUrl != null"> menu_url, </if> <if test="isShow != null"> is_show, </if> <if test="isDelete != null"> is_delete, </if> <if test="operationUserName != null"> operation_user_name, </if> <if test="operationTime != null"> operation_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="menuName != null"> #{menuName,jdbcType=VARCHAR}, </if> <if test="menuRemark != null"> #{menuRemark,jdbcType=VARCHAR}, </if> <if test="menuParentId != null"> #{menuParentId,jdbcType=INTEGER}, </if> <if test="menuUrl != null"> #{menuUrl,jdbcType=VARCHAR}, </if> <if test="isShow != null"> #{isShow,jdbcType=TINYINT}, </if> <if test="isDelete != null"> #{isDelete,jdbcType=TINYINT}, </if> <if test="operationUserName != null"> #{operationUserName,jdbcType=VARCHAR}, </if> <if test="operationTime != null"> #{operationTime,jdbcType=TIMESTAMP}, </if> </trim> </insert>
其实这里属性都差很少,主要是SqlSource不同,那么下来就看它的属性。