目錄
宏觀理解
拿到的 mapper 其實是一個代理對象,底層調用的是 SqlSession 的方法,SqlSession 的方法裏其實調用的 Executor 的方法。
源碼驗證
從 Mapper 到 SqlSession
跟 getMapper:
類 SqlSession
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
每一個 SqlSession 都包含一個 Configuration 的引用,所有的配置信息都被解讀在裏面了。
跟 getMapper:
類 Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry
public class MapperRegistry {
private final Configuration config;//config對象,mybatis全局唯一的
//記錄了mapper接口與對應MapperProxyFactory之間的關係
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
this.config = config;
}
@SuppressWarnings("unchecked")
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);
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
//將mapper接口的工廠類添加到mapper註冊中心
//XMLMapperBuilder裏的parse()裏的bindMapperForNamespace()裏的
//configuration.addMapper(boundType)調用
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//實例化Mapper接口的代理工程類,並將信息添加至knownMappers
knownMappers.put(type, new MapperProxyFactory<T>(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.
//解析接口上的註解信息,並添加至configuration對象
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
public Collection<Class<?>> getMappers() {
return Collections.unmodifiableCollection(knownMappers.keySet());
}
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
}
MapperProxyFactory
public class MapperProxyFactory<T> {
//mapper接口的class對象
private final Class<T> mapperInterface;
//key是mapper接口中的某個方法的method對象,value是對應的MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//創建實現了mapper接口的動態代理對象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//每次調用都會創建新的MapperProxy對象,MapperProxy繼承了InvocationHandler
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
可以看出來最後是拿到的代理對象。
MapperProxy
學過 JDK 的動態代理就會知道:調用 mapper 接口的方法,其實就是調用這個對象裏的 invoke 方法
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;//記錄關聯的SqlSession對象
private final Class<T> mapperInterface;//mapper接口對應的class對象;
//key是mapper接口中的某個方法的method對象,value是對應的MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增強
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//從緩存中獲取mapperMethod對象,如果緩存中沒有,則創建一個,並添加到緩存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
//調用execute方法執行sql,可以看出並沒有執行method.invoke
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
@UsesJava7
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
/**
* Backport of java.lang.reflect.Method#isDefault()
*/
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
&& method.getDeclaringClass().isInterface();
}
}
MapperMethod
封裝了 mapper 接口中對應方法的信息,以及對應的 sql 語句的信息
它是 mapper 接口與映射配置文件中 sql 語句的橋樑
MapperMethod 對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
它有3個內部類:
- SqlCommand : 從 configuration 中獲取方法的命名空間.方法名以及 sql 語句的類型
- MethodSignature:封裝 mapper 接口方法的相關信息(入參,返回類型)
- ParamNameResolver: 解析 mapper 接口方法中的入參,將多個參數轉成 Map
public class MapperMethod {
//從configuration中獲取方法的命名空間.方法名以及SQL語句的類型(SELECT等)
private final SqlCommand command;
//封裝mapper接口方法的相關信息(入參,返回類型)
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根據sql語句類型以及接口返回的參數選擇調用不同的
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()) {//返回值爲void
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {//返回值爲集合或者數組
//這個方法裏面其實就是調用的SqlSession的方法
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {//返回值爲map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {//返回值爲遊標
result = executeForCursor(sqlSession, args);
} else {//處理返回爲單一對象的情況
//通過參數解析器解析解析參數
Object param = method.convertArgsToSqlCommandParam(args);
//調用SqlSession的方法,重點看
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() &&
(result == null || !method.getReturnType().equals(result.getClass()))) {
result = OptionalUtil.ofNullable(result);
}
}
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 到 Executor
之前的代碼已經看到,當返回對象爲單一對象的情況,調用的是 selectOne 方法
但最終,會調用到 selectList 方法:
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//從configuration中獲取要執行的sql語句的配置信息
MappedStatement ms = configuration.getMappedStatement(statement);
//通過executor執行語句,並返回指定的結果集
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();
}
}
跟 query:
類 Executor
會調哪個方法呢?
SqlSession sqlSession = sqlSessionFactory.openSession();
這是我們獲取 sqlSession 的代碼
跟 openSession:
類 DefaultSqlSessionFactory:
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
跟 openSessionFromDataSource:
//從數據源獲取數據庫連接
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//獲取mybatis配置文件中的environment對象
final Environment environment = configuration.getEnvironment();
//從environment獲取transactionFactory對象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//創建事務對象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根據配置創建executor
final Executor executor = configuration.newExecutor(tx, execType);
//創建DefaultSqlSession
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();
}
}
跟 newExecutor:
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);
}
//如果有<setting name="cacheEnabled" value="true" />節點,
//通過裝飾器,添加二級緩存的能力
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//通過interceptorChain遍歷所有的插件爲executor增強,添加插件的功能
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
看見沒有,裝飾器模式,會給你的 executor,套上 CachingExecutor。
而 cacheEnabled 在 Configuration 裏默認是 true 的。
而有了這些,並不是真的開啓了二級緩存。要開啓二級緩存,得在 Mapper.xml 文件里加上 <cache> 相關標籤,這個會在 Mybatis 初始化時,被 XMLMapperBuilder 解析到,交給 builderAssistant 創建 Cache 並存儲到 Configuration 的一個 Map裏,以 namespace 爲 key。
詳情請看 Mybatis 的初始化與建造者模式
CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//獲取sql語句信息,佔位符,參數等信息
BoundSql boundSql = ms.getBoundSql(parameterObject);
//拼裝緩存的key值
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//從MappedStatement中獲取二級緩存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);//從二級緩存中獲取數據
if (list == null) {
//二級緩存爲空,纔會調用BaseExecutor.query
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);
}
開啓了二級緩存,而沒有,或者沒開啓二級緩存,都會執行 delegate.<E> query
BaseExecutor
@Override
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) {//檢查當前executor是否關閉
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {//非嵌套查詢,並且FlushCache配置爲true,則需要清空一級緩存
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) {//如果當前sql的一級緩存配置爲STATEMENT,查詢完既清空一集緩存
// issue #482
clearLocalCache();
}
}
return list;
}
//真正訪問數據庫獲取結果的方法
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 {
//調用抽象方法doQuery,方法查詢數據庫並返回結果,可選的實現包括:simple、reuse、batch
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;
}
跟 doQuery:
可以看出,這是一個模板方法,模板模式的體現。默認的是 SimpleExecutor 實現。
BaseExecutor 定義了一套骨架流程:
紅色的部分就是不同的 doQuery 實現。
SimpleExecutor
@Override
//查詢的實現
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();//獲取configuration對象
//創建StatementHandler對象,
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler對象創建stmt,並使用parameterHandler對佔位符進行處理
stmt = prepareStatement(handler, ms.getStatementLog());
//通過statementHandler對象調用ResultSetHandler將結果集轉化爲指定對象返回
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQuery 裏的 newStatementHandler
先跟 newStatementHandler:
類 Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//創建RoutingStatementHandler對象,實際由statmentType來指定真實的StatementHandler來實現
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
StatementHandler
創建了一個 StatementHandler 的引用,對象是 RoutingStatementHandler:
跟 RoutingStatementHandler:
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//RoutingStatementHandler最主要的功能就是根據mappedStatment的配置,生成一個對應的StatementHandler對象並賦值給delegate
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());
}
}
可以看出來這是個靜態代理,根據配置選擇代理對象。默認是 PREPARED,也就是預編譯的。
doQuery 裏的 prepareStatement
回去跟 prepareStatement:
//創建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//獲取connection對象的動態代理,添加日誌能力;
Connection connection = getConnection(statementLog);
//通過不同的StatementHandler,利用connection創建(prepare)Statement
stmt = handler.prepare(connection, transaction.getTimeout());
//使用parameterHandler處理佔位符
handler.parameterize(stmt);
return stmt;
}
跟 prepare:
類 StatementHandler
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
由前面可以知道,這個 delegate 是 PreparedStatementHandler。但由於這個方法它並沒有重寫,所以還是得看它的父類 BaseStatementHandler
@Override
//使用模板模式,定義了獲取Statement的步驟,其子類實現實例化Statement的具體的方式;
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//通過不同的子類實例化不同的Statement,分爲三類:simple(statment)、prepare(prepareStatement)、callable(CallableStatementHandler)
statement = instantiateStatement(connection);
//設置超時時間
setStatementTimeout(statement, transactionTimeout);
//設置數據集大小
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);
}
}
跟 instantiateStatement:
這就是一個需要子類重寫的骨架方法了
@Override
//使用底層的prepareStatement對象來完成對數據庫的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
//根據mappedStatement.getKeyGenerator字段,創建prepareStatement
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {//對於insert語句
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
//返回數據庫生成的主鍵
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
//返回數據庫生成的主鍵填充至keyColumnNames中指定的列
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
//設置結果集是否可以滾動以及其遊標是否可以上下移動,設置結果集是否可更新
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
//創建PrepareStatement對象,這是熟悉的JDBC代碼裏了
return connection.prepareStatement(sql);
}
}
//回去看前面最近一條分界線下面的方法,緊接着prepareStatement就是下面這個方法了
@Override
//使用parameterHandler對sql語句的佔位符進行處理
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
跟 setParameters:
ParameterHandler
對 sql 語句的佔位符進行處理
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
public class DefaultParameterHandler implements ParameterHandler {
//typeHandler註冊中心
private final TypeHandlerRegistry typeHandlerRegistry;
//對應的sql節點的信息
private final MappedStatement mappedStatement;
//用戶傳入的參數
private final Object parameterObject;
//SQL語句信息,其中還包括佔位符和參數名稱信息
private final BoundSql boundSql;
private final Configuration configuration;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
@Override
public Object getParameterObject() {
return parameterObject;
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//從boundSql中獲取sql語句的佔位符對應的參數信息
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//遍歷這個參數列表,把參數設置到PreparedStatement中
if (parameterMappings != null) {
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)) { // 獲取對應的實參值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();//從parameterMapping中獲取typeHandler對象
JdbcType jdbcType = parameterMapping.getJdbcType();//獲取參數對應的jdbcType
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
//爲statment中的佔位符綁定參數
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
doQuery 裏的 query
類 PreparedStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//JDBC方法
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
ResultSetHandler
找到映射匹配規則、反射實例化目標對象、根據規則填充屬性值
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
類 DefaultResultSetHandler
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//用於保存結果集對象
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
//statment可能返回多個結果集對象,這裏先取出第一個結果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
//獲取結果集對應resultMap,本質就是獲取字段與java屬性的映射規則
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);//結果集和resultMap不能爲空,爲空拋出異常
while (rsw != null && resultMapCount > resultSetCount) {
//獲取當前結果集對應的resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//根據映射規則(resultMap)對結果集進行轉化,轉換成目標對象以後放入multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);//獲取下一個結果集
cleanUpAfterHandlingResultSet();//清空nestedResultObjects對象
resultSetCount++;
}
//獲取多結果集。多結果集一般出現在存儲過程的執行,存儲過程返回多個resultset,
//mappedStatement.resultSets屬性列出多個結果集的名稱,用逗號分割;
//多結果集的處理不是重點
String[] resultSets = mappedStatement.getResultSets();
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);
}
跟 handleResultSet:
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {//處理多結果集的嵌套映射
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {//如果resultHandler爲空,實例化一個人默認的resultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//對ResultSet進行映射,映射結果暫存在resultHandler中
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//將暫存在resultHandler中的映射結果,填充到multipleResults
multipleResults.add(defaultResultHandler.getResultList());
} else {
//使用指定的rusultHandler進行轉換
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
//調用resultset.close()關閉結果集
closeResultSet(rsw.getResultSet());
}
}
跟 handleRowValues:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {//處理有嵌套resultmap的情況
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {//處理沒有嵌套resultmap的情況
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
跟 handleRowValuesForSimpleResultMap:
//簡單映射處理
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
//創建結果上下文,所謂的上下文就是專門在循環中緩存結果對象的
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
//1.根據分頁信息,定位到指定的記錄
skipRows(rsw.getResultSet(), rowBounds);
//2.shouldProcessMoreRows判斷是否需要映射後續的結果,實際還是翻頁處理,避免超過limit
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
//3.進一步完善resultMap信息,主要是處理鑑別器的信息
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
//4.讀取resultSet中的一行記錄並進行映射,轉化並返回目標對象
Object rowValue = getRowValue(rsw, discriminatedResultMap);
//5.保存映射結果對象
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}