本章中分析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不同,那麼下來就看它的屬性。