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;
}
