MyBatis源码解读--从入口类SqlSessionFactoryBuilder出发
先摘抄一段MyBatis官方文档代码:
configuration的中配置的statement
2.XMLConfigBuilder:通过XPathParse将文件转化成Document,然后XMLConfigBuilder再将Document转化成Configuration。
3.获取SqlSession:SqlSession session = sqlSessionFactory.openSession();根据Configuration生成Executor,作为DefaultSqlSession构造参数,返回DefaultSqlSession。
查看DefaultSqlSession生成代码
4.查询
String resource = "org/mybatis/example/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);1.SqlSessionFactoryBuilder就是官方指定的入口了,这个类只做了一件事,就是生成SqlSessionFactory,只有一个被多次重载的方法public SqlSessionFactory build(...){}, 此处做两个处理:a.将字符或字节流的文件转化成Configuration,b.将Configuration作为参数给DefaultSqlSessionFactory,返回SqlSessoinFactory(DefaultSqlSessionFactory)对象. 查看SqlSessionFactory.builder生成代码
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder e = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build((Configuration)e.parse());//配置信息,处理sql的mapper等都在这里处理, } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException var13) { ; } } return var5; }
public Configuration parse() {//SqlSessionFactoryBuilder的方法build中调用这个方法 if(this.parsed) { throw new BuilderException("Each MapperConfigParser can only be used once."); } else { this.parsed = true; this.parseConfiguration(this.parser.evalNode("/configuration")); return this.configuration; } } private void parseConfiguration(XNode root) { try { this.typeAliasesElement(root.evalNode("typeAliases")); this.pluginElement(root.evalNode("plugins")); this.objectFactoryElement(root.evalNode("objectFactory")); this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.propertiesElement(root.evalNode("properties")); this.settingsElement(root.evalNode("settings")); this.environmentsElement(root.evalNode("environments")); this.typeHandlerElement(root.evalNode("typeHandlers")); this.mapperElement(root.evalNode("mappers")); } catch (Exception var3) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); } } private void mapperElement(XNode parent) throws Exception { if(parent != null) { Iterator i$ = parent.getChildren().iterator(); while(i$.hasNext()) { XNode child = (XNode)i$.next(); String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); InputStream inputStream; XMLMapperBuilder mapperParser; if(resource != null && url == null) { ErrorContext.instance().resource(resource); inputStream = Resources.getResourceAsStream(resource); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments()); mapperParser.parse(); } else { if(url == null || resource != null) { throw new BuilderException("A mapper element may only specify a url or resource, but not both."); } ErrorContext.instance().resource(url); inputStream = Resources.getUrlAsStream(url); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments()); mapperParser.parse(); } } } } public void parse() { if(!this.configuration.isResourceLoaded(this.resource)) { this.configurationElement(this.parser.evalNode("/mapper")); this.configuration.addLoadedResource(this.resource); this.bindMapperForNamespace(); } this.parsePendingResultMaps(); this.parsePendingChacheRefs(); this.parsePendingStatements(); } private void configurationElement(XNode context) { try { String e = context.getStringAttribute("namespace"); this.builderAssistant.setCurrentNamespace(e); this.cacheRefElement(context.evalNode("cache-ref")); this.cacheElement(context.evalNode("cache")); this.parameterMapElement(context.evalNodes("/mapper/parameterMap")); this.resultMapElements(context.evalNodes("/mapper/resultMap")); this.sqlElement(context.evalNodes("/mapper/sql")); this.buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception var3) { throw new RuntimeException("Error parsing Mapper XML. Cause: " + var3, var3); } } private void buildStatementFromContext(List list) { Iterator i$ = list.iterator(); while(i$.hasNext()) { XNode context = (XNode)i$.next(); XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context); try { statementParser.parseStatementNode(); } catch (IncompleteStatementException var6) { this.configuration.addIncompleteStatement(statementParser); } } } public void parseStatementNode() { String id = this.context.getStringAttribute("id"); Integer fetchSize = this.context.getIntAttribute("fetchSize", (Integer)null); Integer timeout = this.context.getIntAttribute("timeout", (Integer)null); String parameterMap = this.context.getStringAttribute("parameterMap"); String parameterType = this.context.getStringAttribute("parameterType"); Class parameterTypeClass = this.resolveClass(parameterType); String resultMap = this.context.getStringAttribute("resultMap"); String resultType = this.context.getStringAttribute("resultType"); Class resultTypeClass = this.resolveClass(resultType); String resultSetType = this.context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType); List contents = this.parseDynamicTags(this.context); MixedSqlNode rootSqlNode = new MixedSqlNode(contents); DynamicSqlSource sqlSource = new DynamicSqlSource(this.configuration, rootSqlNode); String nodeName = this.context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = this.context.getBooleanAttribute("flushCache", Boolean.valueOf(!isSelect)).booleanValue(); boolean useCache = this.context.getBooleanAttribute("useCache", Boolean.valueOf(isSelect)).booleanValue(); String keyProperty = this.context.getStringAttribute("keyProperty"); String keyStatementId = id + "!selectKey"; keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId); Object keyGenerator; if(this.configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = this.configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", Boolean.valueOf(this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))).booleanValue()?new Jdbc3KeyGenerator(this.context.getStringAttribute("keyColumn", (String)null)):new NoKeyGenerator(); } this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, (KeyGenerator)keyGenerator, keyProperty); } public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, KeyGenerator keyGenerator, String keyProperty) { id = this.applyCurrentNamespace(id); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType); statementBuilder.resource(this.resource); statementBuilder.fetchSize(fetchSize); statementBuilder.statementType(statementType); statementBuilder.keyGenerator(keyGenerator); statementBuilder.keyProperty(keyProperty); this.setStatementTimeout(timeout, statementBuilder); this.setStatementParameterMap(parameterMap, parameterType, statementBuilder); this.setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder); this.setStatementCache(isSelect, flushCache, useCache, this.currentCache, statementBuilder); MappedStatement statement = statementBuilder.build(); this.configuration.addMappedStatement(statement); return statement; }
public SqlSession openSession() { return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Connection connection = null; DefaultSqlSession var10; try { Environment e = this.configuration.getEnvironment(); DataSource dataSource = this.getDataSourceFromEnvironment(e); TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(e); connection = dataSource.getConnection(); if(level != null) { connection.setTransactionIsolation(level.getLevel()); } connection = this.wrapConnection(connection); Transaction tx = transactionFactory.newTransaction(connection, autoCommit); Executor executor = this.configuration.newExecutor(tx, execType);//生成executor var10 = new DefaultSqlSession(this.configuration, executor, autoCommit); } catch (Exception var14) { this.closeConnection(connection); throw ExceptionFactory.wrapException("Error opening session. Cause: " + var14, var14); } finally { ErrorContext.instance().reset(); } return var10; } public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null?this.defaultExecutorType:executorType; executorType = executorType == null?ExecutorType.SIMPLE:executorType; Object executor; if(ExecutorType.BATCH == executorType) { // BatchExecutor的设计主要是用于做批量更新操作的。其底层会调用Statement的executeBatch()方法实现批量操作 executor = new BatchExecutor(this, transaction); } else if(ExecutorType.REUSE == executorType) { /*是可以重用的Executor。它重用的是Statement对象,它会在内部利用一个Map把创建的Statement都缓存起来, *每次在执行一条SQL语句时,它都会去判断之前是否存在基于该SQL缓存的Statement对象,存在而且之前缓存的Statement对象对应的Connection还没有关闭的时候就继续用之前的Statement对象, *否则将创建一个新的Statement对象,并将其缓存起来。因为每一个新的SqlSession都有一个新的Executor对象,所以我们缓存在ReuseExecutor上的Statement的作用域是同一个SqlSession。*/ executor = new ReuseExecutor(this, transaction); } else { //SimpleExecutor是Mybatis执行Mapper语句时默认使用的Executor。它提供最基本的Mapper语句执行功能,没有过多的封装的 executor = new SimpleExecutor(this, transaction); } if(this.cacheEnabled) { executor = new CachingExecutor((Executor)executor); } Executor executor1 = (Executor)this.interceptorChain.pluginAll(executor); return executor1; }
SqlSession session = sqlSessionFactory.openSession(); try { Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); } finally { session.close(); }selectOne 调用selectList:
public <T> T selectOne(String statement, Object parameter) { List<T> list = this.<T>selectList(statement, parameter); //...略过非主要代码 return list.get(0); } 在selectList最终调用:
public List selectList(String statement, Object parameter, RowBounds rowBounds) { List var5; try { MappedStatement e = this.configuration.getMappedStatement(statement); var5 = this.executor.query(e, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception var9) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); } finally { ErrorContext.instance().reset(); } return var5; }