前提:
mybatis是当下最流行的ORM框架,结合一个查询例子来跟踪下mybatis的执行流程以及mybatis的四大对象创建流程,再结合画图跟大家分享下,如有理解有误,欢迎指教。
首先说下mybatis与hibernate区别:
hibernate是全自动ORM框架;皆在消除sql,面向对象开发,使用javaBean与数据库打交道;
如图所示:java与数据库打交道需要经过以下5个步骤:
- 编写sql
- sql预编译
- 参数设置
- 执行sql
- 封装结果
hibernate将这5部全部封装起来,对于开发人员是透明的,相当于黑箱操作,无法编写sql以及优化sql;如果需要编写复杂sql需要学习hql语言,最后会加重学习负担。
而mybatis是一款半自动框架,sql与编码分离,sql交给开发人员控制,功能边界清晰,一个专注于业务,一个专注于数据。对于开发人员来说,核心sql还是需要自己优化。
通过一个例子讲解mybatis查询流程
@Test
public void test() throws IOException {
// 1、获取sqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3、获取mapper
RefundBillMapper refundBillMapper = sqlSession.getMapper(RefundBillMapper.class);
// 4、执行查询
refundBillMapper.selectOne(1);
}
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String source = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(source);
return new SqlSessionFactoryBuilder().build(inputStream);
}
上述例子为使用mabatis进行一个普通的查询,那么总共要经历4步:
- 获取sqlSessionFactory
- 获取sqlSession
- 获取mapper
- 执行查询
下面我们结合源码以及图解来详细看下每一步的执行过程
1、获取sqlSessionFactory
new SqlSessionFactoryBuilder().build(inputStream)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
这步主要是创建XMLConfigBuilder对象,创建XMLConfigBuilder对象时会初始化configuration对象,XMLConfigBuilder用于解析mybatis-config.xml中配置的各种标签属性;来看下parser.parse()方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
从configuration标签开始解析,因为configuration是mybatis-config.xml中的顶级标签,parseConfiguration(parser.evalNode("/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);
}
}
这里解析了mybatis-config.xml文件中的所有标签;并将解析后的属性全部添加到configuration中;注意mapper映射类会加入mapperRegistry中;
最后返回build(parser.parse()),此方法中返回new DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
总结:创建解析器,解析配置文件中所有标签,并将解析的结果放到configuration中
configuration属性如下
2、获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession是从sqlSessionFactory中获取,重点看下openSession()方法:
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
openSession调用了DefaultSqlSessionFactory中的openSessionFromDataSource方法
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
前面为创建tx,重点为Executor executor = configuration.newExecutor(tx, execType)
,来看下四大对象之一的Executor是怎么创建的:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
- 如果没有配置executorType的话,默认为SimpleExecutor类型的Executor(
executor = new SimpleExecutor(this, transaction)
); - 如果配置了二级缓存cacheEnabled,则会创建CachingExecutor(
executor = new CachingExecutor(executor);
),CachingExecutor实则对Executor做了一层包装,最终调用的都是BaseExecutor executor = (Executor) interceptorChain.pluginAll(executor)
插件开发,可以对Executor做动态代理,再次包装- 初始化SimpleExecutor最终会初始化BaseExecutor,BaseExecutor初始化如下:
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
Executor创建完会创建DefaultSqlSession;DefaultSqlSession里会包含configuration以及executor
new DefaultSqlSession(configuration, executor, autoCommit)
Executor类型及区别:
- SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
- ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
- BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
- CachingExecutor:CachingExecutor是一个Executor接口的装饰器,它为Executor对象增加了二级缓存的相关功能,委托的执行器对象可以是SimpleExecutor、ReuseExecutor、BatchExecutor中任一一个。执行 update 方法前判断是否清空二级缓存;执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。
总结:返回SqlSession的实现类DefaultSqlSession,里面包含了configuration以及executor对象;
3、获取mapper + 4、执行查询
先看图吧,这部是核心,内容比较多;
sqlSession.getMapper(RefundBillMapper.class)
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
调用DefaultSqlSession中的getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
调用Configuration中的getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
先从缓存中获取mapperProxyFactory,knownMappers是个map,获取不到则直接创建;直接看mapperProxyFactory.newInstance(sqlSession)
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
创建mapperProxy,直接看newInstance(mapperProxy)
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
这里使用了jdk动态代理,代理类为MapperProxy
,直接看MapperProxy
的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 放行继承与Object的原生方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
从缓存中获取MapperMethod
,获取不到则创建,创建完加入缓存;创建MapperMethod
时会给SqlCommand
、MethodSignature
赋值;然后执行mapperMethod.execute
方法:
SqlCommand结构如下:
MethodSignature结构如下:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
按操作类型进行匹配,如果是查询类型,还会根据返回结果类型匹配;看下sqlSession.selectOne
方法:
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
调用了selectList
并取了第一条,看下selectList
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
从configuration中获取MappedStatement,MappedStatement是第一步解析mapper.xml然后添加到configuration的,看下MappedStatement结构:
紧接着执行了executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
获取BoundSql,BoundSql里存储了原生sql,以及参数;BoundSql结构如下:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
Cache cache = ms.getCache();
为二级缓存,我们没有配置二级缓存,直接看delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
localCache.getObject(key)
先从一级缓存中获取,获取不到直接queryFromDatabase
从数据库中查找;
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
localCache.putObject(key, EXECUTION_PLACEHOLDER)
,缓存占位;然后执行doQuery
查询,将查询结果放到localCache
缓存
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
创建了4大对象之一的StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
StatementHandler创建时会根据statementType类型创建不同类型的StatementHandler,创建完会执行interceptorChain.pluginAll
插件代理
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
创建不同类型的StatementHandler;不同类型StatementHandler区别
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
创建完StatementHandler会调用父类的构造方法BaseStatementHandler()
,该方法里创建了ParameterHandler和ResultSetHandler;
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
创建ParameterHandler和ResultSetHandler时,同样会调用插件代理;
再回到doQuery()
方法中看prepareStatement(handler, ms.getStatementLog())
方法,
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
获取Connection连接;handler.prepare(connection, transaction.getTimeout())
会预编译sql,handler.parameterize(stmt)
会给sql设置参数值;
connection.prepareStatement(sql, keyColumnNames);
预编译sql
typeHandler.setParameter(ps, i + 1, value, jdbcType);
给sql设置参数值,借助typeHandler给不同类型的参数设值
再回到doQuery()
方法中看handler.<E>query(stmt, resultHandler)
方法,
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
ps.execute()
执行数据库查询,与原生的jdbc是一样的,然后将查询结构传给resultSetHandler,由resultSetHandler去将数据库结果集跟我们在mapper里配置的javaBean类型进行转换
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//获取第一个结果值
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获得resultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
//这边应该为1吧,一般resultMap为一个
int resultMapCount = resultMaps.size();
//判断是否有resultMap,没有的话抛出异常
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
//获得resultMap,实体类和表中数据字段的对应关系
ResultMap resultMap = resultMaps.get(resultSetCount);
//将值设置成对应的resultmap对象
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResulSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
//获得第一个值,并将值打包
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
//结果集不为空
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
//将数据打包
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}