mybatis原始碼 (三) —— mapper 動態代理以及select操作
String resource = "com/analyze/mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper (UserMapper.class);
Map map = userMapper.getUA();
sqlSession.close();
org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
@Override
public <T> T getMapper(Class<T> type) {
//最後會去呼叫MapperRegistry.getMapper
return configuration.<T>getMapper(type, this);
}
org.apache.ibatis.binding.MapperRegistry#getMapper
//返回代理類
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//從map中去取MapperProxyFactory
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);
}
}
org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)
protected T newInstance(MapperProxy<T> mapperProxy) {
//用JDK自帶的動態代理生成對映器
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//建立一個MapperProxy物件
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
最終呼叫代理類mapper的方法會走invoker的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理以後,所有Mapper的方法呼叫時,都會呼叫這個invoke方法
//並不是任何一個方法都需要執行呼叫代理物件進行執行,如果這個方法是Object中通用的方法(toString、hashCode等)無需執行
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//這裡優化了,去快取中找MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//執行
return mapperMethod.execute(sqlSession, args);
}
org.apache.ibatis.binding.MapperProxy#cachedMapperMethod
//去快取中找MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//找不到才去new
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
//存入map
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
MapperMethod構造方法
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, method);
}
org.apache.ibatis.binding.MapperMethod.SqlCommand#SqlCommand構造,這個是關鍵,覺得是什麼型別的sql
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
//mapperInterface.getName(即mapper中配置的package)
// method.getName() mapper中配置的id
//拼在一起就是Configuration.mappedStatements中存的key
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
if (configuration.hasStatement(statementName)) {
//取出來 MappedStatement
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
//如果不是這個mapper介面的方法,再去查父類
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) {
ms = configuration.getMappedStatement(parentStatementName);
}
}
if (ms == null) {
throw new BindingException("Invalid bound statement (not found): " + statementName);
}
name = ms.getId();
//賦值sql的型別是select update
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
這一段主要是為了把mapper介面和mapper配置檔案的資訊關聯起來
org.apache.ibatis.binding.MapperMethod#execute
//執行
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//可以看到執行時就是4種情況,insert|update|delete|select,分別呼叫SqlSession的4大類方法
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()) {
//如果結果是map
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;
}
這段程式碼主要就是判斷是什麼型別的sql,執行怎樣的邏輯,我們選取一段程式碼來分析
//否則就是一條記錄
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
org.apache.ibatis.binding.MapperMethod.MethodSignature#convertArgsToSqlCommandParam
把方法引數轉換成sql引數
public Object convertArgsToSqlCommandParam(Object[] args) {
final int paramCount = params.size();
if (args == null || paramCount == 0) {
//如果沒引數
return null;
} else if (!hasNamedParameters && paramCount == 1) {
//如果只有一個引數
return args[params.keySet().iterator().next().intValue()];
} else {
//否則,返回一個ParamMap,修改引數名,引數名就是其位置
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : params.entrySet()) {
//1.先加一個#{0},#{1},#{2}...引數
param.put(entry.getValue(), args[entry.getKey().intValue()]);
// issue #71, add param names as param1, param2...but ensure backward compatibility
final String genericParamName = "param" + String.valueOf(i + 1);
if (!param.containsKey(genericParamName)) {
//2.再加一個#{param1},#{param2}...引數
//你可以傳遞多個引數給一個對映器方法。如果你這樣做了,
//預設情況下它們將會以它們在引數列表中的位置來命名,比如:#{param1},#{param2}等。
//如果你想改變引數的名稱(只在多引數情況下) ,那麼你可以在引數上使用@Param(“paramName”)註解。
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
然後呼叫result = sqlSession.selectOne(command.getName(), param);
來執行sql,這裡command.getName()其實就是configuration裡面map中存的{key:namespace+id value:sql}得key即id
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
//核心selectList
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根據statement id找到對應的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//轉而用執行器來查詢結果,注意這裡傳入的ResultHandler是null
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();
}
}
org.apache.ibatis.executor.BaseExecutor#query
//SqlSession.selectList會呼叫此方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//得到繫結sql
BoundSql boundSql = ms.getBoundSql(parameter);
//建立快取Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//查詢
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@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) {
throw new ExecutorException("Executor was closed.");
}
//先清區域性快取,再查詢.但僅查詢堆疊為0,才清。為了處理遞迴呼叫
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
//加一,這樣遞迴呼叫到上面的時候就不會再清區域性快取了
queryStack++;
//先根據cachekey從localCache去查
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//若查到localCache快取,處理localOutputParameterCache
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
//如果是STATEMENT,清本地快取
clearLocalCache();
}
}
return list;
}
org.apache.ibatis.executor.BaseExecutor#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);
//如果是儲存過程,OUT引數也加入快取
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
org.apache.ibatis.executor.SimpleExecutor#doQuery
//select
@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();
//新建一個StatementHandler
//這裡看到ResultHandler傳入了
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//準備語句
stmt = prepareStatement(handler, ms.getStatementLog());
//StatementHandler.query
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
org.apache.ibatis.executor.SimpleExecutor#prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//呼叫StatementHandler.prepare
stmt = handler.prepare(connection);
//呼叫StatementHandler.parameterize
handler.parameterize(stmt);
return stmt;
}
最後呼叫的connection 來進行sql
再看org.apache.ibatis.executor.CachingExecutor#update
在進行update操作的時候,會先把快取給清空
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
//重新整理快取完再update
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}