深入淺出MyBatis-Sqlsession


目錄(?)[+]
  1. 創建
    1. Executor的創建
  2. Mapper
    1. 創建
    2. MapperProxy的invoke
  3. Executor
    1. CacheExecutor
    2. 普通Executor
  4. StatementHandler
    1. 創建
    2. 初始化
    3. 參數設置
    4. 結果處理

前面的章節主要講mybatis如何解析配置文件,這些都是一次性的過程。從本章開始講解動態的過程,它們跟應用程序對mybatis的調用密切相關。本章先從sqlsession開始。

創建

正如其名,Sqlsession對應着一次數據庫會話。由於數據庫回話不是永久的,因此Sqlsession的生命週期也不應該是永久的,相反,在你每次訪問數據庫時都需要創建它(當然並不是說在Sqlsession裏只能執行一次sql,你可以執行多次,當一旦關閉了Sqlsession就需要重新創建它)。創建Sqlsession的地方只有一個,那就是SqlsessionFactory的openSession方法:

  1. public SqlSessionopenSession() {  
  2.     returnopenSessionFromDataSource(configuration.getDefaultExecutorType(),nullfalse);  
  3. }  
    public SqlSessionopenSession() {
        returnopenSessionFromDataSource(configuration.getDefaultExecutorType(),null, false);
    }

我們可以看到實際創建SqlSession的地方是openSessionFromDataSource,如下:

  1. private SqlSessionopenSessionFromDataSource(ExecutorType execType, TransactionIsolationLevellevel, boolean autoCommit) {  
  2.   
  3.     Connectionconnection = null;  
  4.   
  5.     try {  
  6.   
  7.         finalEnvironment environment = configuration.getEnvironment();  
  8.   
  9.         final DataSourcedataSource = getDataSourceFromEnvironment(environment);  
  10.   
  11.        TransactionFactory transactionFactory =getTransactionFactoryFromEnvironment(environment);  
  12.   
  13.        connection = dataSource.getConnection();  
  14.   
  15.         if (level != null) {  
  16.   
  17.            connection.setTransactionIsolation(level.getLevel());  
  18.   
  19.         }  
  20.   
  21.        connection = wrapConnection(connection);  
  22.   
  23.        Transaction tx = transactionFactory.newTransaction(connection,autoCommit);  
  24.   
  25.         Executorexecutor = configuration.newExecutor(tx, execType);  
  26.   
  27.         returnnewDefaultSqlSession(configuration, executor, autoCommit);  
  28.   
  29.     } catch (Exceptione) {  
  30.   
  31.        closeConnection(connection);  
  32.   
  33.         throwExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);  
  34.   
  35.     } finally {  
  36.   
  37.        ErrorContext.instance().reset();  
  38.   
  39.     }  
  40.   
  41. }  
    private SqlSessionopenSessionFromDataSource(ExecutorType execType, TransactionIsolationLevellevel, boolean autoCommit) {

        Connectionconnection = null;

        try {

            finalEnvironment environment = configuration.getEnvironment();

            final DataSourcedataSource = getDataSourceFromEnvironment(environment);

           TransactionFactory transactionFactory =getTransactionFactoryFromEnvironment(environment);

           connection = dataSource.getConnection();

            if (level != null) {

               connection.setTransactionIsolation(level.getLevel());

            }

           connection = wrapConnection(connection);

           Transaction tx = transactionFactory.newTransaction(connection,autoCommit);

            Executorexecutor = configuration.newExecutor(tx, execType);

            returnnewDefaultSqlSession(configuration, executor, autoCommit);

        } catch (Exceptione) {

           closeConnection(connection);

            throwExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);

        } finally {

           ErrorContext.instance().reset();

        }

    }

可以看出,創建sqlsession經過了以下幾個主要步驟:

1)       從配置中獲取Environment

2)       Environment中取得DataSource

3)       Environment中取得TransactionFactory

4)       DataSource裏獲取數據庫連接對象Connection

5)       在取得的數據庫連接上創建事務對象Transaction

6)       創建Executor對象(該對象非常重要,事實上sqlsession的所有操作都是通過它完成的);

7)       創建sqlsession對象。

Executor的創建

Executor與Sqlsession的關係就像市長與書記,Sqlsession只是個門面,真正幹事的是Executor,Sqlsession對數據庫的操作都是通過Executor來完成的。與Sqlsession一樣,Executor也是動態創建的:

  1.     public ExecutornewExecutor(Transaction transaction, ExecutorType executorType) {  
  2.   
  3.        executorType = executorType == null ? defaultExecutorType :executorType;  
  4.   
  5.        executorType = executorType == null ?ExecutorType.SIMPLE : executorType;  
  6.   
  7.         Executor executor;  
  8.   
  9.         if(ExecutorType.BATCH == executorType) {  
  10.            executor = new BatchExecutor(this,transaction);  
  11.         } elseif(ExecutorType.REUSE == executorType) {  
  12.            executor = new ReuseExecutor(this,transaction);  
  13.         } else {  
  14.             executor = newSimpleExecutor(this, transaction);  
  15.         }  
  16.   
  17.         if (cacheEnabled) {  
  18.            executor = new CachingExecutor(executor);  
  19.         }  
  20.         executor =(Executor) interceptorChain.pluginAll(executor);  
  21.         return executor;  
  22. }  
    public ExecutornewExecutor(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);
        } elseif(ExecutorType.REUSE == executorType) {
           executor = new ReuseExecutor(this,transaction);
        } else {
            executor = newSimpleExecutor(this, transaction);
        }

        if (cacheEnabled) {
           executor = new CachingExecutor(executor);
        }
        executor =(Executor) interceptorChain.pluginAll(executor);
        return executor;
}


可以看出,如果不開啓cache的話,創建的Executor只是3中基礎類型之一,BatchExecutor專門用於執行批量sql操作,ReuseExecutor會重用statement執行sql操作,SimpleExecutor只是簡單執行sql沒有什麼特別的。開啓cache的話(默認是開啓的並且沒有任何理由去關閉它),就會創建CachingExecutor,它以前面創建的Executor作爲唯一參數。CachingExecutor在查詢數據庫前先查找緩存,若沒找到的話調用delegate(就是構造時傳入的Executor對象)從數據庫查詢,並將查詢結果存入緩存中。

Executor對象是可以被插件攔截的,如果定義了針對Executor類型的插件,最終生成的Executor對象是被各個插件插入後的代理對象(關於插件會有後續章節專門介紹,敬請期待)。

Mapper

Mybatis官方手冊建議通過mapper對象訪問mybatis,因爲使用mapper看起來更優雅,就像下面這樣:

  1. session = sqlSessionFactory.openSession();  
  2. UserDao userDao= session.getMapper(UserDao.class);  
  3. UserDto user =new UserDto();  
  4. user.setUsername("iMbatis");  
  5. user.setPassword("iMbatis");  
  6. userDao.insertUser(user);  
     session = sqlSessionFactory.openSession();
     UserDao userDao= session.getMapper(UserDao.class);
     UserDto user =new UserDto();
     user.setUsername("iMbatis");
     user.setPassword("iMbatis");
     userDao.insertUser(user);

那麼這個mapper到底是什麼呢,它是如何創建的呢,它又是怎麼與sqlsession等關聯起來的呢?下面爲你一一解答。

創建

表面上看mapper是在sqlsession裏創建的,但實際創建它的地方是MapperRegistry

  1. public <T>T getMapper(Class<T> type, SqlSession sqlSession) {  
  2.     if (!knownMappers.contains(type))  
  3.         thrownewBindingException("Type " + type + " isnot known to the MapperRegistry.");  
  4.     try {  
  5.         returnMapperProxy.newMapperProxy(type, sqlSession);  
  6.     } catch (Exceptione) {  
  7.         thrownewBindingException("Error getting mapper instance. Cause: " + e, e);  
  8.     }  
  9. }  
    public <T>T getMapper(Class<T> type, SqlSession sqlSession) {
        if (!knownMappers.contains(type))
            thrownewBindingException("Type " + type + " isnot known to the MapperRegistry.");
        try {
            returnMapperProxy.newMapperProxy(type, sqlSession);
        } catch (Exceptione) {
            thrownewBindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }

可以看到,mapper是一個代理對象,它實現的接口就是傳入的type,這就是爲什麼mapper對象可以通過接口直接訪問。同時還可以看到,創建mapper代理對象時傳入了sqlsession對象,這樣就把sqlsession也關聯起來了。我們進一步看看MapperProxy.newMapperProxy(type,sqlSession);背後發生了什麼事情:

  1. publicstatic <T>T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {  
  2.     ClassLoaderclassLoader = mapperInterface.getClassLoader();  
  3.     Class<?>[] interfaces = new Class[]{mapperInterface};  
  4.     MapperProxyproxy = new MapperProxy(sqlSession);  
  5.     return (T) Proxy.newProxyInstance(classLoader,interfaces, proxy);  
  6. }  
    publicstatic <T>T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
        ClassLoaderclassLoader = mapperInterface.getClassLoader();
        Class<?>[] interfaces = new Class[]{mapperInterface};
        MapperProxyproxy = new MapperProxy(sqlSession);
        return (T) Proxy.newProxyInstance(classLoader,interfaces, proxy);
    }

看起來沒什麼特別的,和其他代理類的創建一樣,我們重點關注一下MapperProxyinvoke方法

MapperProxy的invoke

我們知道對被代理對象的方法的訪問都會落實到代理者的invoke上來,MapperProxyinvoke如下:

  1. public Objectinvoke(Object proxy, Method method, Object[] args) throws Throwable{  
  2.     if (method.getDeclaringClass()== Object.class) {  
  3.         return method.invoke(this, args);  
  4.     }  
  5.   
  6.     finalClass<?> declaringInterface = findDeclaringInterface(proxy, method);  
  7.     finalMapperMethod mapperMethod = newMapperMethod(declaringInterface, method, sqlSession);  
  8.     final Objectresult = mapperMethod.execute(args);  
  9.   
  10.     if (result ==null && method.getReturnType().isPrimitive()&& !method.getReturnType().equals(Void.TYPE)) {  
  11.         thrownewBindingException("Mapper method '" + method.getName() + "'(" + method.getDeclaringClass()  
  12.                 + ") attempted toreturn null from a method with a primitive return type ("  
  13.                + method.getReturnType() + ").");  
  14.     }  
  15.     return result;  
  16. }  
    public Objectinvoke(Object proxy, Method method, Object[] args) throws Throwable{
        if (method.getDeclaringClass()== Object.class) {
            return method.invoke(this, args);
        }

        finalClass<?> declaringInterface = findDeclaringInterface(proxy, method);
        finalMapperMethod mapperMethod = newMapperMethod(declaringInterface, method, sqlSession);
        final Objectresult = mapperMethod.execute(args);

        if (result ==null && method.getReturnType().isPrimitive()&& !method.getReturnType().equals(Void.TYPE)) {
            thrownewBindingException("Mapper method '" + method.getName() + "'(" + method.getDeclaringClass()
                    + ") attempted toreturn null from a method with a primitive return type ("
                   + method.getReturnType() + ").");
        }
        return result;
    }


可以看到invoke把執行權轉交給了MapperMethod,我們來看看MapperMethod裏又是怎麼運作的:

  1.     public Objectexecute(Object[] args) {  
  2.         Objectresult = null;  
  3.         if(SqlCommandType.INSERT == type) {  
  4.             Objectparam = getParam(args);  
  5.             result= sqlSession.insert(commandName, param);  
  6.         } elseif(SqlCommandType.UPDATE == type) {  
  7.             Object param = getParam(args);  
  8.             result= sqlSession.update(commandName, param);  
  9.         } elseif(SqlCommandType.DELETE == type) {  
  10.             Objectparam = getParam(args);  
  11.             result= sqlSession.delete(commandName, param);  
  12.         } elseif(SqlCommandType.SELECT == type) {  
  13.             if (returnsVoid &&resultHandlerIndex != null) {  
  14.                executeWithResultHandler(args);  
  15.             } elseif (returnsList) {  
  16.                result = executeForList(args);  
  17.             } elseif (returnsMap) {  
  18.                result = executeForMap(args);  
  19.             } else {  
  20.                Object param = getParam(args);  
  21.                result = sqlSession.selectOne(commandName, param);  
  22.             }  
  23.         } else {  
  24.             thrownewBindingException("Unknown execution method for: " + commandName);  
  25.         }  
  26.         return result;  
  27.   
  28. }  
    public Objectexecute(Object[] args) {
        Objectresult = null;
        if(SqlCommandType.INSERT == type) {
            Objectparam = getParam(args);
            result= sqlSession.insert(commandName, param);
        } elseif(SqlCommandType.UPDATE == type) {
            Object param = getParam(args);
            result= sqlSession.update(commandName, param);
        } elseif(SqlCommandType.DELETE == type) {
            Objectparam = getParam(args);
            result= sqlSession.delete(commandName, param);
        } elseif(SqlCommandType.SELECT == type) {
            if (returnsVoid &&resultHandlerIndex != null) {
               executeWithResultHandler(args);
            } elseif (returnsList) {
               result = executeForList(args);
            } elseif (returnsMap) {
               result = executeForMap(args);
            } else {
               Object param = getParam(args);
               result = sqlSession.selectOne(commandName, param);
            }
        } else {
            thrownewBindingException("Unknown execution method for: " + commandName);
        }
        return result;

}


可以看到,MapperMethod就像是一個分發者,他根據參數和返回值類型選擇不同的sqlsession方法來執行。這樣mapper對象與sqlsession就真正的關聯起來了。

Executor

前面提到過,sqlsession只是一個門面,真正發揮作用的是executor,對sqlsession方法的訪問最終都會落到executor的相應方法上去。Executor分成兩大類,一類是CacheExecutor,另一類是普通ExecutorExecutor的創建前面已經介紹了,下面介紹下他們的功能:

CacheExecutor

CacheExecutor有一個重要屬性delegate,它保存的是某類普通的Executor,值在構照時傳入。執行數據庫update操作時,它直接調用delegateupdate方法,執行query方法時先嚐試從cache中取值,取不到再調用delegate的查詢方法,並將查詢結果存入cache中。代碼如下:

  1. public Listquery(MappedStatement ms, Object parameterObject, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {  
  2.     if (ms != null) {  
  3.         Cachecache = ms.getCache();  
  4.         if (cache != null) {  
  5.            flushCacheIfRequired(ms);  
  6.            cache.getReadWriteLock().readLock().lock();  
  7.            try {  
  8.                if (ms.isUseCache() && resultHandler ==null) {  
  9.                    CacheKey key = createCacheKey(ms, parameterObject, rowBounds);  
  10.                    final List cachedList = (List)cache.getObject(key);  
  11.                    if (cachedList != null) {  
  12.                         returncachedList;  
  13.                    } else {  
  14.                        List list = delegate.query(ms,parameterObject, rowBounds, resultHandler);  
  15.                        tcm.putObject(cache,key, list);  
  16.                        return list;  
  17.                    }  
  18.                } else {  
  19.                    returndelegate.query(ms,parameterObject, rowBounds, resultHandler);  
  20.                }  
  21.             } finally {  
  22.                cache.getReadWriteLock().readLock().unlock();  
  23.             }  
  24.         }  
  25.     }  
  26.     returndelegate.query(ms,parameterObject, rowBounds, resultHandler);  
  27. }  
    public Listquery(MappedStatement ms, Object parameterObject, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {
        if (ms != null) {
            Cachecache = ms.getCache();
            if (cache != null) {
               flushCacheIfRequired(ms);
               cache.getReadWriteLock().readLock().lock();
               try {
                   if (ms.isUseCache() && resultHandler ==null) {
                       CacheKey key = createCacheKey(ms, parameterObject, rowBounds);
                       final List cachedList = (List)cache.getObject(key);
                       if (cachedList != null) {
                            returncachedList;
                       } else {
                           List list = delegate.query(ms,parameterObject, rowBounds, resultHandler);
                           tcm.putObject(cache,key, list);
                           return list;
                       }
                   } else {
                       returndelegate.query(ms,parameterObject, rowBounds, resultHandler);
                   }
                } finally {
                   cache.getReadWriteLock().readLock().unlock();
                }
            }
        }
        returndelegate.query(ms,parameterObject, rowBounds, resultHandler);
    }

普通Executor

普通Executor3類,他們都繼承於BaseExecutorBatchExecutor專門用於執行批量sql操作,ReuseExecutor會重用statement執行sql操作,SimpleExecutor只是簡單執行sql沒有什麼特別的。下面以SimpleExecutor爲例:

  1. public ListdoQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {  
  2.     Statementstmt = null;  
  3.     try {  
  4.        Configuration configuration = ms.getConfiguration();  
  5.        StatementHandler handler = configuration.newStatementHandler(this, ms,parameter, rowBounds,resultHandler);  
  6.        stmt =prepareStatement(handler);  
  7.        returnhandler.query(stmt, resultHandler);  
  8.     } finally {  
  9.        closeStatement(stmt);  
  10.     }  
  11. }  
    public ListdoQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {
        Statementstmt = null;
        try {
           Configuration configuration = ms.getConfiguration();
           StatementHandler handler = configuration.newStatementHandler(this, ms,parameter, rowBounds,resultHandler);
           stmt =prepareStatement(handler);
           returnhandler.query(stmt, resultHandler);
        } finally {
           closeStatement(stmt);
        }
    }

可以看出,Executor本質上也是個甩手掌櫃,具體的事情原來是StatementHandler來完成的。

StatementHandler

Executor將指揮棒交給StatementHandler後,接下來的工作就是StatementHandler的事了。我們先看看StatementHandler是如何創建的。

創建

  1. publicStatementHandler newStatementHandler(Executor executor, MappedStatementmappedStatement,  
  2.         ObjectparameterObject, RowBounds rowBounds, ResultHandler resultHandler) {  
  3.    StatementHandler statementHandler = newRoutingStatementHandler(executor, mappedStatement,parameterObject,rowBounds, resultHandler);  
  4.    statementHandler= (StatementHandler) interceptorChain.pluginAll(statementHandler);  
  5.    returnstatementHandler;  
  6. }  
    publicStatementHandler newStatementHandler(Executor executor, MappedStatementmappedStatement,
            ObjectparameterObject, RowBounds rowBounds, ResultHandler resultHandler) {
       StatementHandler statementHandler = newRoutingStatementHandler(executor, mappedStatement,parameterObject,rowBounds, resultHandler);
       statementHandler= (StatementHandler) interceptorChain.pluginAll(statementHandler);
       returnstatementHandler;
    }

可以看到每次創建的StatementHandler都是RoutingStatementHandler,它只是一個分發者,他一個屬性delegate用於指定用哪種具體的StatementHandler。可選的StatementHandlerSimpleStatementHandlerPreparedStatementHandlerCallableStatementHandler三種。選用哪種在mapper配置文件的每個statement裏指定,默認的是PreparedStatementHandler。同時還要注意到StatementHandler是可以被攔截器攔截的,和Executor一樣,被攔截器攔截後的對像是一個代理對象。由於mybatis沒有實現數據庫的物理分頁,衆多物理分頁的實現都是在這個地方使用攔截器實現的,本文作者也實現了一個分頁攔截器,在後續的章節會分享給大家,敬請期待。

初始化

StatementHandler創建後需要執行一些初始操作,比如statement的開啓和參數設置、對於PreparedStatement還需要執行參數的設置操作等。代碼如下:

  1. private StatementprepareStatement(StatementHandler handler) throwsSQLException {  
  2.     Statementstmt;  
  3.     Connectionconnection = transaction.getConnection();  
  4.     stmt =handler.prepare(connection);  
  5.     handler.parameterize(stmt);  
  6.     return stmt;  
  7. }  
    private StatementprepareStatement(StatementHandler handler) throwsSQLException {
        Statementstmt;
        Connectionconnection = transaction.getConnection();
        stmt =handler.prepare(connection);
        handler.parameterize(stmt);
        return stmt;
    }

statement的開啓和參數設置沒什麼特別的地方,handler.parameterize倒是可以看看是怎麼回事。handler.parameterize通過調用ParameterHandlersetParameters完成參數的設置,ParameterHandler隨着StatementHandler的創建而創建,默認的實現是DefaultParameterHandler

  1. publicParameterHandler newParameterHandler(MappedStatement mappedStatement, ObjectparameterObject, BoundSql boundSql) {  
  2.    ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,parameterObject,boundSql);  
  3.    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);  
  4.    returnparameterHandler;  
  5. }  
    publicParameterHandler newParameterHandler(MappedStatement mappedStatement, ObjectparameterObject, BoundSql boundSql) {
       ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,parameterObject,boundSql);
       parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
       returnparameterHandler;
    }

ExecutorStatementHandler一樣,ParameterHandler也是可以被攔截的。

參數設置

DefaultParameterHandler裏設置參數的代碼如下:

  1. publicvoidsetParameters(PreparedStatement ps) throwsSQLException {  
  2.    ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId());  
  3.    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  
  4.     if(parameterMappings != null) {  
  5.        MetaObject metaObject = parameterObject == null ? null :configuration.newMetaObject(parameterObject);  
  6.         for (int i = 0; i< parameterMappings.size(); i++) {  
  7.            ParameterMapping parameterMapping = parameterMappings.get(i);  
  8.             if(parameterMapping.getMode() != ParameterMode.OUT) {  
  9.                Object value;  
  10.                String propertyName = parameterMapping.getProperty();  
  11.                PropertyTokenizer prop = newPropertyTokenizer(propertyName);  
  12.                if (parameterObject == null) {  
  13.                    value = null;  
  14.                } elseif (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){  
  15.                    value = parameterObject;  
  16.                } elseif (boundSql.hasAdditionalParameter(propertyName)){  
  17.                    value = boundSql.getAdditionalParameter(propertyName);  
  18.                } elseif(propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)  
  19.                         && boundSql.hasAdditionalParameter(prop.getName())){  
  20.                    value = boundSql.getAdditionalParameter(prop.getName());  
  21.                    if (value != null) {  
  22.                         value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));  
  23.                    }  
  24.                } else {  
  25.                    value = metaObject == null ? null :metaObject.getValue(propertyName);  
  26.                }  
  27.                TypeHandler typeHandler = parameterMapping.getTypeHandler();  
  28.                if (typeHandler == null) {  
  29.                    thrownew ExecutorException("Therewas no TypeHandler found for parameter " + propertyName  + " of statement " + mappedStatement.getId());  
  30.                 }  
  31.                typeHandler.setParameter(ps, i + 1, value,parameterMapping.getJdbcType());  
  32.             }  
  33.   
  34.         }  
  35.   
  36.     }  
  37. }  
    publicvoidsetParameters(PreparedStatement ps) throwsSQLException {
       ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId());
       List<ParameterMapping> 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();
                   PropertyTokenizer prop = newPropertyTokenizer(propertyName);
                   if (parameterObject == null) {
                       value = null;
                   } elseif (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){
                       value = parameterObject;
                   } elseif (boundSql.hasAdditionalParameter(propertyName)){
                       value = boundSql.getAdditionalParameter(propertyName);
                   } elseif(propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)
                            && boundSql.hasAdditionalParameter(prop.getName())){
                       value = boundSql.getAdditionalParameter(prop.getName());
                       if (value != null) {
                            value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
                       }
                   } else {
                       value = metaObject == null ? null :metaObject.getValue(propertyName);
                   }
                   TypeHandler typeHandler = parameterMapping.getTypeHandler();
                   if (typeHandler == null) {
                       thrownew ExecutorException("Therewas no TypeHandler found for parameter " + propertyName  + " of statement " + mappedStatement.getId());
                    }
                   typeHandler.setParameter(ps, i + 1, value,parameterMapping.getJdbcType());
                }

            }

        }
    }

這裏面最重要的一句其實就是最後一句代碼,它的作用是用合適的TypeHandler完成參數的設置。那麼什麼是合適的TypeHandler呢,它又是如何決斷出來的呢?BaseStatementHandler的構造方法裏有這麼一句:

this.boundSql= mappedStatement.getBoundSql(parameterObject);

它觸發了sql 的解析,在解析sql的過程中,TypeHandler也被決斷出來了,決斷的原則就是根據參數的類型和參數對應的JDBC類型決定使用哪個TypeHandler。比如:參數類型是String的話就用StringTypeHandler,參數類型是整數的話就用IntegerTypeHandler等

參數設置完畢後,執行數據庫操作(update或query)。如果是query最後還有個查詢結果的處理過程。

結果處理

結果處理使用ResultSetHandler來完成,默認的ResultSetHandler是FastResultSetHandler,它在創建StatementHandler時一起創建,代碼如下

  1. publicResultSetHandler newResultSetHandler(Executor executor, MappedStatementmappedStatement,  
  2. RowBoundsrowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSqlboundSql) {  
  3.    ResultSetHandler resultSetHandler =mappedStatement.hasNestedResultMaps() ? newNestedResultSetHandler(executor, mappedStatement, parameterHandler,resultHandler, boundSql, rowBounds): new FastResultSetHandler(executor,mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);  
  4.    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);  
  5.    returnresultSetHandler;  
  6. }  
    publicResultSetHandler newResultSetHandler(Executor executor, MappedStatementmappedStatement,
    RowBoundsrowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSqlboundSql) {
       ResultSetHandler resultSetHandler =mappedStatement.hasNestedResultMaps() ? newNestedResultSetHandler(executor, mappedStatement, parameterHandler,resultHandler, boundSql, rowBounds): new FastResultSetHandler(executor,mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
       resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
       returnresultSetHandler;
    }

可以看出ResultSetHandler也是可以被攔截的,可以編寫自己的攔截器改變ResultSetHandler的默認行爲。

  1. ResultSetHandler內部一條記錄一條記錄的處理,在處理每條記錄的每一列時會調用TypeHandler轉換結果,如下:  
  2.   
  3.     protectedbooleanapplyAutomaticMappings(ResultSet rs, List<String> unmappedColumnNames,MetaObject metaObject) throws SQLException {  
  4.         booleanfoundValues = false;  
  5.         for (StringcolumnName : unmappedColumnNames) {  
  6.             final Stringproperty = metaObject.findProperty(columnName);  
  7.             if (property!= null) {  
  8.                 final ClasspropertyType =metaObject.getSetterType(property);  
  9.                 if (typeHandlerRegistry.hasTypeHandler(propertyType)) {  
  10.                    final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propertyType);  
  11.                    final Object value = typeHandler.getResult(rs,columnName);  
  12.                    if (value != null) {  
  13.                        metaObject.setValue(property, value);  
  14.                        foundValues = true;  
  15.                    }  
  16.                 }  
  17.             }  
  18.         }  
  19.         returnfoundValues;  
  20.    }  
ResultSetHandler內部一條記錄一條記錄的處理,在處理每條記錄的每一列時會調用TypeHandler轉換結果,如下:

    protectedbooleanapplyAutomaticMappings(ResultSet rs, List<String> unmappedColumnNames,MetaObject metaObject) throws SQLException {
        booleanfoundValues = false;
        for (StringcolumnName : unmappedColumnNames) {
            final Stringproperty = metaObject.findProperty(columnName);
            if (property!= null) {
                final ClasspropertyType =metaObject.getSetterType(property);
                if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
                   final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propertyType);
                   final Object value = typeHandler.getResult(rs,columnName);
                   if (value != null) {
                       metaObject.setValue(property, value);
                       foundValues = true;
                   }
                }
            }
        }
        returnfoundValues;
   }

從代碼裏可以看到,決斷TypeHandler使用的是結果參數的屬性類型。因此我們在定義作爲結果的對象的屬性時一定要考慮與數據庫字段類型的兼容性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章