Mapper映射是經過動態代理來實現的(由一能夠知道)sql
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 { 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. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
代理類以下: 這裏事動態代理將接口綁定,生成動態代理的對象(佔位),代理的方法放到了MapperProxy類中。數據庫
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); 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) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
MapperProxy類的代碼以下: 在invoke()中判斷判斷是否是一個類,若是使用的是Mapper接口,單麼就跳入else,在後面的代碼中生成了MapperMethod對象,使用了cachedMapperMethod()進行初始化,而後執行mapperMethod.execute(sqlSession, args),將參數和sqlSession傳遞進去. public class MapperProxy<T> implements InvocationHandler, Serializable {app
private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; 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](https://my.oschina.net/u/1162528) public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { 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); } 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類的代碼以下:框架
MapperMethod纔有了命令模式運行,根據上下文跳轉,它可能跳轉到許多方法中。例如executeForMany(SqlSession sqlSession, Object[] args),經過內部的代碼能夠知道該方法實際上仍是執行的sqlSession實例的的方法去執行SQL。ide
映射器的XML文件的命名控件對應的即是這個接口的全路徑,那麼它根據全路徑和方法名便呢呢剛剛綁定起來,經過動態代理技術,讓這個接口跑起來。二胡纔有命令模式,最後還使用SqlSession接口的方法使得它可以執行查詢,有了這層封裝咱們邊可使用接口變成。 public class MapperMethod {ui
private final SqlCommand command; 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; 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; } //這個是經常使用的查詢返回多條記錄的方法 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else { result = sqlSession.<E>selectList(command.getName(), param); } // issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; } }
MapperMethod的execute方法。它經經的判斷以後就進入了SqlSession的刪除,更新,插入,選擇等方法,那麼這些方法如何執行是咱們須要知道的。this
Mapper的執行的過程是經過的Executor,StatementHandler,ParameterHandler和ResultHandler來完成數據庫操做和結果返回的。.net
上面的代碼是構建Executor的過程,在構建完執行器的實例以後,最後調用了 executor = (Executor) interceptorChain.pluginAll(executor);這就是MyBatis的插件,這裏它將爲咱們構建一層層的動態代理對象。在調度真是的Executor方法以前執行配置插件的代碼能夠修改。
public class PreparedStatementHandler extends BaseStatementHandler { public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); } [@Override](https://my.oschina.net/u/1162528) public int update(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); int rows = ps.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject); return rows; } [@Override](https://my.oschina.net/u/1162528) public void batch(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.addBatch(); } [@Override](https://my.oschina.net/u/1162528) public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); } @Override public <E> Cursor<E> queryCursor(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleCursorResultSets(ps); } @Override 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); } } @Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } }
public abstract class BaseStatementHandler implements StatementHandler { @Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { 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()方法是對SQL進行了預編譯。首先,作一些基礎配置,好比超時,獲取的最大行數等的設置。而後,Executor活調用parameterize()方法去設置參數,這個時候使用了PreparedStatementHandler的parameterize()去設置參數 3. ParameterHandler用於SQL對參數的處理 public class PreparedStatementHandler extends BaseStatementHandler { @Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } }插件
因爲在執行前參數和SQL都已經被prepare()方法預編譯,參數在parameterize()方法上進行設置。因此到這裏已經很簡單了。咱們只要執行SQL,而後返回結果用ResultHandler3d
MyBatis是經過參數處理器(ParameterHandler)對預編譯語句進行參數設置的。它的做用是完成對預編譯參數的設置。 public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement ps) throws SQLException; } 其中,getParameterObject()方法做用是返回參數對象,setParameters()方法的做用是設置預編譯SQL語句的參數. MyBatis爲Parameter提供了一個實現類DefaultParameterHandler
public class DefaultParameterHandler implements ParameterHandler { private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; private final Object parameterObject; private BoundSql boundSql; private 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()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); 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)) { // 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 { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { 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); } } } } } }
從源碼中咱們能夠知道,參數是從parameterObject對象中獲取,而後使用typeHandler進行參數設置,typeHandler也是在MyBatis初始化的時候,註冊在Configuration裏面的,隨時能夠獲取獲得。
ResultHandler是進行最後數據集的封裝返回處理的 有了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; }
handleOutputParameters()負責處理存儲國車給輸出參數的,handleResultSets(),它是包裝結果集的。MyBatis爲咱們了它的實現類DefaultResultSetHandler,它負責包裝結果集,在默認的狀況下,都是經過這個類進行處理的。它涉及使用使用JAVASSIST和CGLIB,比較複雜,經過typeHandler和ObjectFactory進行組裝結果再返回。
@Override 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); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } 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); }
SqlSession是經過Executor建立StatementHandler來運行的,而StatementHandler主要有三步: 1.prepared預編譯SQL 2.parameter設置參數 3.query/update執行SQL 在這幾步中parameterize式調用parameterHandler的方法去設置的,而參數是根據類型處理器typeHnadler去處理的。query/update方法是經過resultHandler進行處理結果的,若是是update的語句,它就是返回整數,不然是使用typeHandler處理返回的結果類型,而後用ObjectFactory提供的規則組裝對象,返回給調用者。