上篇我们分析了mybatis在启动过程中是如何加载配置文件的,本篇我们继续来分析一下mybatis在创建完Configuration配置后,是如何进行查询的(本篇重点是查询实现,mybatis的缓存方面不会详细分析,后续文章会涉及mybatis的缓存)。
1、通过SqlSessionFactory创建SqlSession
我们上篇分析完了mybatis解析配置文件创建Configuration,根据Configuration创建SqlSessionFactory的过程。现在,我们看一下mybatis是如何通过SqlSessionFactory创建SqlSession的。我们先看看SqlSessionFactory接口有哪些方法:
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
SqlSessionFactory顾名思义,是用来创建SqlSession的,我们看一下默认的DefaultSqlSessionFactory的实现,这里只贴出主要的一个方法:
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e)
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
我们可以看到,创建Sql需要Executor,而创建Executor需要Transaction对象,我们先来看一下Transaction接口:
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
Transaction接口给我们提供了事物基本操作的封装。
而Executor也在Transaction的基础上进行了封装:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
实现这两个接口的之类有很多,所以我们暂时先不看这两个接口的具体实现。在创建完这两个接口的实现对象后,SqlSessionFactory会创建SqlSession。我们以DefaultSqlSession为例子,来看一下我们常用的几个操作接口是如何实现的。
1、使用SqlSession操作数据库的实现原理
1.1、使用selectOne()方法进行查询
我们先来看一下方法的实现:
@Override
public T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List list = this.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;
}
}
我们可以看到selectOne()方法是通过selectList()来实现的,当查询出的结果多余1个时就会跑出异常。那接下来我们看看selectList()的实现。
1.2、使用selectList()方法进行查询
@Override
public List 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();
}
}
我们可以看到selectList的执行过程:首先根据id从configuration中查询出对应的MappedStatement对象(上篇已经介绍了MappedStatement,这里就不多说了),获取到MappedStatement对象后,将MappedStatement对象和传入的参数交给executor来执行具体的操作,我们以BaseExecutor为例,看一下具体的实现:
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
public List 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 list;
try {
queryStack++;
list = resultHandler == null ? (List) 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();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
return list;
}
我们略过有关mybatis缓存的操作,只看具体查询执行的过程,我们可以看到executor查询真正执行是通过调用queryFromDatabase()方法来实现的,我们一步一步跟踪方法调用链会发现,queryFromDatabase()方法最后会调用子类的
doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 方法实现的。Executor有三个子类:BatchExecutor、ReuseExecutor和SimpleExecutor,我们分别看一下三个子类对doQuery方法的实现:
首先我们看一下SimpleExecutor的实现:
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//将查询数据封装成StatementHandler进行查询
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
SimpleExecutor将数据封装成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);
//调用mybatis生命周期中的方法,mybatis插件应用
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
//RoutingStatementHandler的构造方法
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());
}
}
我们可以看到这里直接创建了一个RoutingStatementHandler的对象,其实RoutingStatementHandler是一个代理对象,RoutingStatementHandler根据定义的StatementType来创建真正的StatementHandler。熟悉jdbc的同学应该对StatementType都不会陌生,StatementType有三种:1.Statement、2.PrepareStatement、3.CallableStatement。具体的区别这里不多做解释,不清楚的同学可以自己百度。三种不同的StatementType对应着SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。
我们继续回到doQuery方法,在创建完StatementHandler后,SimpleExecutor会调用prepareStatement()方法来获取Statement对象,然后将Statement对象交给StatementHandler来执行查询,最后返回最终的结果。我们可以看到prepareStatement()方法分别调用了StatementHandler的prepare()方法和parameterize()方法来准备Statement。我们先来看一下prepare()方法的实现,这个方法在BaseStatementHandler这个抽象类中:
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// instantiateStatement方法是一个抽象方法,交给子类实现
statement = instantiateStatement(connection);
setStatementTimeout(statement);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
创建Statement的过程是通过instantiateStatement()方法交给子类实现的,我们贴出来三个子类的实现细节:
// SimpleStatementHandler
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
// PreparedStatementHandler
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
// CallableStatementHandler
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() != null) {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareCall(sql);
}
}
PrepareStatemnet和CallableStatement都会先通过BoundSql对象来获取真正执行的sql语句,这一块解析我们后面再讲。然后会通过jdbc的Connection来获取Statement。
看完Statement的创建过程,我们继续回到prepareStatement()方法,我们都知道,PrepareStatement方法是执行预编译查询的Statement,在执行查询之前需要设置对应的参数,而CallableStatement用来执行存储过程,也需要设置参数信息,这些操作都是放在StatementHandler的parameterize()方法里处理,我们以PrepareStatement为例子,看一下参数是如何设置的,通过方法追踪,我们能够找到设置参数是ParameterHandler的setParameters()方法实现的:
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null) {
throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId());
}
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
这里根据解析mapper文件的parameterType结果来进行参数的设置,具体逻辑并不复杂,有兴趣的同学可以自己看一下。
这里创建Statement的逻辑我们就看完了,继续回到之前的public
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler. handleResultSets(ps);
}
执行完查询之后,会把执行结果交给ResultSetHandler来解析,我们看一下具体实现方法:
public List
逻辑很简单,就是交给方法handleResultSet()方法来处理,我们一步一步跟踪,发现处理会交给handleRowValues()方法进行处理,handleRowValues()处理的逻辑是首先跳过指定行数(RowBounds设置的,默认不跳过),然后遍历ResultSet,这里要注意,遍历ResultSet的时候,遍历有个上限,也是RowBounds指定,默认为Integer的最大值。在遍历的过程中将结果集进行解析,我们看一下实现细节:
protected void handleRowValues(ResultSet rs, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultColumnCache resultColumnCache) throws SQLException {
final DefaultResultContext resultContext = new DefaultResultContext();
//跳过指定行
skipRows(rs, rowBounds);
//限制遍历结果集个数
while (shouldProcessMoreRows(rs, resultContext, rowBounds)) {
//选择真正的ResultMap(mybatis的discriminator功能)
final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);
//创建查询结果对象
Object rowValue = getRowValue(rs, discriminatedResultMap, null, resultColumnCache);
//注册到结果集中
callResultHandler(resultHandler, resultContext, rowValue);
}
}
我们只看遍历的过程,首先通过resolveDiscriminatedResultMap()方法获取到真正要使用的ResultMap,这个过程很简单,就是获取discriminator 标签定义的colum的值,然后获取该值对应的ResultMap,但是要注意,这个是一个循环的过程(解析discriminator里映射的ResultMap还有discriminator标签)。这里源码不贴出,有兴趣的同学可以看一下。
获取到对应的ResultMap后,通过getRowValue()来进行创建查询结果对象,创建的过程可以概括为下面几个步骤:
1、判断要返回的结果的类型是否有TypeHandler,有的话使用TypeHandler来进行创建结果对象;
2、判断resultMap中是否指定构造方法,有的话使用构造方法创建结果对象;
3、如果没有TypeHandler和指定构造方法,使用objectFactory来进行创建对象;
4、创建完对象之后,如果不是通过TypeHandler进行创建,则需要设置对象的属性值(是否进行参数设置还需要根据automapping属性来进行判断)
5、如果需要设置属性,通过反射进行设置
6、通过反射设置resultMap中property属性指定的映射的值
protected Object getRowValue(ResultSet rs, ResultMap resultMap, CacheKey rowKey, ResultColumnCache resultColumnCache) throws SQLException {
final ResultLoaderMap lazyLoader = instantiateResultLoaderMap();
//创建对象
Object resultObject = createResultObject(rs, resultMap, lazyLoader, null, resultColumnCache);
// 如果不是通过TypeHandler进行创建,则需要设置对象的属性值
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(resultObject);
boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {
final List unmappedColumnNames = resultColumnCache.getUnmappedColumnNames(resultMap, null);
//设置需要自动配置的属性值
foundValues = applyAutomaticMappings(rs, unmappedColumnNames, metaObject, null, resultColumnCache) || foundValues;
}
final List mappedColumnNames = resultColumnCache.getMappedColumnNames(resultMap, null);
// 通过反射设置resultMap中property属性指定的映射的值
foundValues = applyPropertyMappings(rs, resultMap, mappedColumnNames, metaObject, lazyLoader, null) || foundValues;
foundValues = (lazyLoader != null && lazyLoader.size() > 0) || foundValues;
resultObject = foundValues ? resultObject : null;
return resultObject;
}
return resultObject;
}
将查询结果解析成对象之后,继续调用callResultHandler()来对结果集进一步处理,解析成map或list。这里我们就不多解释了。
到这里SimpleExecutor就介绍完了,我们继续看一下ReuseExecutor:ReuseExecutor顾名思义,是可以重用的Executor。ReuseExecutor会缓存Statement,以sql为keyStatement为value缓存,这里不多介绍了。
最后我们看一下BatchExecutor:BatchExecutor继承了BaseExecutor,来讲多次查询或者更新合并成一个。熟悉jdbc的同学应该对这个不陌生。这里不多讲,有兴趣的同学也可以看一下,逻辑并不复杂。到这里SqlSession的selectList()的方法的逻辑已经分析完了。接下来我们看看SqlSession的selectMap()方法。
1.3、使用selectMap()方法进行查询
我们先看一下DefaultSqlSession的selectMap()方法:
public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
final List> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler mapResultHandler = new DefaultMapResultHandler(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory());
final DefaultResultContext context = new DefaultResultContext();
for (Object o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
Map selectedMap = mapResultHandler.getMappedResults();
return selectedMap;
}
我们发现,selectMap()操作也是先进行selectList()查询出结果集,再使用DefaultMapResultHandler来做一步转换。逻辑也很简单。有兴趣的同学可以自己阅读一下源码。
1.4、使用Mapper接口进行查询
我们再使用mybatis的过程中,使用最多的操作应该是使用Mapper接口进行查询:
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
我们看一下Mapper具体的获取过程:
//sqlSession的getMapper方法
public T getMapper(Class type) {
return configuration.getMapper(type, this);
}
//configuration的getMapper方法
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
// mapperRegistry的getMapper方法
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);
}
}
获取Mapper的过程就是从configuration中的MapperRegistry中获取已经注册的mapperProxyFactory,然后通过mapperProxyFactory获取Mapper,注册是发生在mybatis解析configuration的过程中。我们可以先来看一下mapper的注册过程:
public void addMapper(Class type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
注册过程很简单,就是new一个MapperProxyFactory对象,我们看看MapperProxyFactory的实现:
public class MapperProxyFactory {
private final Class mapperInterface;
private Map methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class getMapperInterface() {
return mapperInterface;
}
public Map getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
熟悉java反射的同学应该很容易看懂MapperProxyFactory。其中MapperProxy实现了InvocationHandler接口,作为代理的处理逻辑。我们可以看一下具体的实现过程:
public class MapperProxy implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class mapperInterface;
private final Map methodCache;
public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//获取MapperMethod对象,使用MapperMethod来执行具体的操作
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
//获取和构造MapperMethod对象
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
我们可以看一下MapperMethod的execute()方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
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 {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
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;
}
我们可以看到,在使用Mapper接口进行操作时,代理会根据方法的签名信息来选择执行操作的类型,然后使用SqlSession来执行具体的操作。
好了,到此mybatis的查询分析就介绍到这里。为了总结上面的操作,我画了一个简单的时序图帮助理解: