在mybatis中StatementType的值決定了由什麼對象來執行咱們的SQL語句。本文來分析下在mybatis中具體是怎麼處理的。java
StatementType是一個枚舉類型。以下:node
/** * @author Clinton Begin */ public enum StatementType { STATEMENT, PREPARED, CALLABLE }
選項 | 說明 |
---|---|
STATEMENT | 對應於Statement對象,有SQL注入的風險 |
PREPARED | PreparedStatement,預編譯處理 |
CALLABLE | CallableStatement通常調用存儲過程的時候使用 |
咱們能夠在映射文件中經過‘statementType’屬性設置,以下:sql
注意默認是'PREPARED ',經過源碼分析咱們能夠查看到。mybatis
咱們要從源碼中發現StatementType的處理,應該是從加載配置文件的地方開始查看,因此咱們從'new SqlSessionFactoryBuilder().build(in);'這行代碼入手app
//InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); InputStream in = TestMybatis.class.getResourceAsStream("/mybatis-config.xml"); // 獲取SqlSessionFactory對象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
進入build方法中查看ide
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); // 配置文件解析的重點是 'parser.parse()'這行代碼 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
進入==parse==方法中源碼分析
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // 進入 parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); // 上面都是解析主配置文件中子節點,直接進入本方法 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
==mapperElement==方法,該方法會根據咱們在mappers中不一樣的配置作出不一樣的處理。咱們直接看resource的方式fetch
private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); // 加載配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); // 解析配置文件的方法 進入 mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
進入 ==mapperParser.parse()== 方法查看ui
public void parse() { if (!configuration.isResourceLoaded(resource)) { // 解析根節點 configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); // 綁定名稱空間 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); // 重點本方法,解析未處理的Statements parsePendingStatements(); }
進入==parsePendingStatements==方法:this
private void parsePendingStatements() { Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements(); synchronized (incompleteStatements) { Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator(); while (iter.hasNext()) { try { // 解析咱們的select|update|insert|delete節點 iter.next().parseStatementNode(); iter.remove(); } catch (IncompleteElementException e) { // Statement is still missing a resource... } } } }
進入==parseStatementNode== 方法
public void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute("resultMap"); String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); // 重點代碼,看截圖 StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
最後解析完這個select|insert|update|delete節點的信息被封裝到了一個MapperedStatement對象中。
至此StatementType解析階段的內容分析完了~