Mybatis框架原理(基於mybatis-3.5.2.jar分析)

mybatis是一套ORM框架,也就是把字段映射爲對象的屬性。
大學學過,最基礎要操作一個數據庫步驟:
引入JDBC包——DriverManager註冊驅動——創建連接——創建Statement——CRUD操作——操作結果集——關閉連接。
爲了省去上面每次操作的繁瑣過程,而且相比hibernate更加靈活,方便維護。mybatis出現了。

首先pom.xml要引入mybatis。
框架的大致邏輯:

package com.stu.demo;

import com.stu.demo.pojo.User;
import org.apache.ibatis.annotations.Select;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

interface UserMapper{
    @Select("select * from t_user where id = #{id} and grade = #{grade}")
    List<User> selectUsers(Integer id, String grade);
}
public class Application {
    public static void main(String[] args) {
        // JDK動態代理,反射實現
        UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{UserMapper.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("方法名字:"+method.getName());
                System.out.println("方法入參:" + Arrays.toString(args));
                // sql參數和方法參數的對應鍵值關係
                Map<String, Object> mapData = buildSQLData(method, args);
                Select annotions = method.getAnnotation(Select.class);
                if (annotions != null) {
                    String[] strs = annotions.value(); // 這個sql可以是多條
                    System.out.println("Mybatis種sql語句:" + Arrays.toString(strs));
                    // 根據鍵值關係賦值得到sql
                    String sql = bulidSQL(strs[0], mapData);
                    System.out.println("最終得到要在數據庫執行的sql語句:" + sql);
                }
                return null;
            }
        });
        userMapper.selectUsers(1, "303班");
    }

    private static String bulidSQL(String str, Map<String, Object> mapData) {
        // 頻繁字符操作
        StringBuilder sb = new StringBuilder();
        int length = str.length();
        for (int i = 0; i < length; i++) {
            char ch = str.charAt(i);
            if (ch == '#') {
                int nextIndex = i + 1;
                char nextChar = str.charAt(nextIndex);
                if ('{' != nextChar) {
                    throw new RuntimeException("sql異常:" + str);
                }
                // 找到當前}結束位置 argStr爲返回的參數key,比如id
                StringBuilder argStr = new StringBuilder();
                i = parseArgStr(argStr,str,nextIndex);
                // 根據key找到對應的值。
                Object argValue = mapData.get(argStr.toString());
                if (argValue == null) {
                    throw new RuntimeException("argValue爲null");
                }
                sb.append(argValue);
                continue;
            }
            sb.append(ch);
        }
        return sb.toString();
    }

    private static int parseArgStr(StringBuilder argStr, String str, int nextIndex) {
        // 從nextIndex++循環
        nextIndex++;
        for (;nextIndex < str.length(); nextIndex++) {
            char ch = str.charAt(nextIndex);
            if (ch != '}') {
                argStr.append(ch);
                continue;
            }
            if (ch == '}') {
                return nextIndex;
            }
        }
        // 最後都沒有} 異常處理。
        throw new RuntimeException("缺少}:"+ str);
    }

    /**
     * sql參數和方法參數的對應鍵值關係
     * @param method
     * @param args
     * @return
     */
    public static Map<String, Object> buildSQLData(Method method, Object[] args) {
        Map<String, Object> mapData = new HashMap<>();
        // 參數獲得
        Parameter[] parameters = method.getParameters();
        // 將我們的參數賦值給sql上變量。
        int index[] = {0}; // 因爲裏面用的時引用類型。
        // 當數據量大時,並行運行。
        // Arrays.asList(parameters).parallelStream().forEach
        // 與之對應Map<String, Object> mapData = new ConcurrentHashMap<>();
        Arrays.asList(parameters).forEach((parameter)->{
            String name = parameter.getName();
            System.out.println("SQL上參數的名字:" + name);
            // 得到鍵值關係--sql參數和方法參數的對應鍵值關係
            mapData.put(name, args[index[0]]);
            index[0]++;
        });
        return mapData;
    }
    public static String parseIntoSQL(String sql, Map<String, Object> mapData){

        return "";
    }
}

運行結果:(很清晰的看到這個過程)
在這裏插入圖片描述
在框架種處理更簡單:通過反射直接拿到返回類型和參數類型。

UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{UserMapper.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println(method.getReturnType());
                    System.out.println(method.getGenericReturnType());
                }
                return null;
            }
        });

結果:(找到了這兩個,get和set進去就行了)
在這裏插入圖片描述
Mybatis架構設計:

源碼包如下,ibatis包下的包:
annotations: 註解包
Arg、AutomapConstructor、CacheNamespace、CacheNamespaceRef、Case、ConstructorArgs、Delete、
DeleteProvider、Flush、Insert、InsertProvider、Lang、Many、MapKey、Mapper、One、Options、
Param、Property、Result、ResultMap、Results、ResultType、Select、SelectKey、SelectProvider、
TypeDiscriminator、Update、UpdateProvider

binding:綁定包
BindingException、MapperMethod、MapperProxy、MapperProxyFactory、MapperRegistry

builder:構建包
cache:緩衝存儲包
cursor:遊標包
datasource:數據源包
exceptions:異常包
executor:執行器包
io:io包
javassist:java助手
jdbc:連接包
lang:基礎類型包
logging:日誌包
mapping:映射包
ognl:語義解析包
parsing:解析包。用於解析mapper樹結構。
plugin:依賴包
reflection:反射包
scripting:腳本包
session:session包
transaction:事務包
type:各種類型包

源碼分析:

返回結果集ResultContext:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.session;

public interface ResultContext<T> {
    T getResultObject();

    int getResultCount();

    boolean isStopped();

    void stop();
}

返回處理類ResultHandler:

package org.apache.ibatis.session;

public interface ResultHandler<T> {
    void handleResult(ResultContext<? extends T> var1);
}

SqlSessionFactoryBuilder 構建類:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

public class SqlSessionFactoryBuilder {
    public SqlSessionFactoryBuilder() {
    }

    public SqlSessionFactory build(Reader reader) {
        return this.build((Reader)reader, (String)null, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, String environment) {
        return this.build((Reader)reader, environment, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, Properties properties) {
        return this.build((Reader)reader, (String)null, properties);
    }

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                reader.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }

    public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment) {
        return this.build((InputStream)inputStream, environment, (Properties)null);
    }

    public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return this.build((InputStream)inputStream, (String)null, properties);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

我們調用裏面的build方法,複用方式調用

 public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                reader.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }

裏面XMLConfigBuilder對象:
XMLConfigBuilder類中:對XPathParser引用。

public XMLConfigBuilder(Reader reader, String environment, Properties props) {
        this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
    }

XPathParser類中:

public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
        this.commonConstructor(validation, variables, entityResolver);
        this.document = this.createDocument(new InputSource(reader));
    }

commonConstructor得到Xpath實例:

XPathFactory factory = XPathFactory.newInstance();
        this.xpath = factory.newXPath();

拿到返回的實例,創建結構樹this.createDocument(new InputSource(reader));
可以發現這裏面全是原生JDK代碼的引用。InputSource是A single input source for an XML entity.
最後得到xml解析後的Document 結構:這個如何去解析,參照最上的解析方式。

private Document createDocument(InputSource inputSource) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(this.validation);
            factory.setNamespaceAware(false);
            factory.setIgnoringComments(true);
            factory.setIgnoringElementContentWhitespace(false);
            factory.setCoalescing(false);
            factory.setExpandEntityReferences(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            builder.setEntityResolver(this.entityResolver);
            builder.setErrorHandler(new ErrorHandler() {
                public void error(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                public void fatalError(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                public void warning(SAXParseException exception) throws SAXException {
                }
            });
            return builder.parse(inputSource);
        } catch (Exception var4) {
            throw new BuilderException("Error creating document instance.  Cause: " + var4, var4);
        }
    }

調用XMLConfigBuilder類的parse方法:

public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }

這個調用this.parser.evalNode("/configuration"):

public XNode evalNode(Object root, String expression) {
        Node node = (Node)this.evaluate(expression, root, XPathConstants.NODE);
        return node == null ? null : new XNode(this, node, this.variables);
    }

他在引用XNode類:
這裏有走到了JDK。拿到xpathParser,結點node,結點名getNodeName,變量等。

 public XNode(XPathParser xpathParser, Node node, Properties variables) {
        this.xpathParser = xpathParser;
        this.node = node;
        this.name = node.getNodeName();
        this.variables = variables;
        this.attributes = this.parseAttributes(node);
        this.body = this.parseBody(node);
    }

將返回的值設給parseConfiguration:

private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

主要核心:迭代解析出,設置好transcational,dataSource在set進configuration

private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
            if (this.environment == null) {
                this.environment = context.getStringAttribute("default");
            }

            Iterator var2 = context.getChildren().iterator();

            while(var2.hasNext()) {
                XNode child = (XNode)var2.next();
                String id = child.getStringAttribute("id");
                if (this.isSpecifiedEnvironment(id)) {
                    TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
                    DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
                    DataSource dataSource = dsFactory.getDataSource();
                    Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
                    this.configuration.setEnvironment(environmentBuilder.build());
                }
            }
        }

    }

到此我們就得到了:Configuration config。

再調用build方法:

 public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

引用Configuration類:在這個類中需要MapperRegistry將mapper接口註冊進來:
可以看到MapperRegistry類中有個addMapper方法。他直接接受接口類。

public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }

            boolean loadCompleted = false;

            try {
                this.knownMappers.put(type, new MapperProxyFactory(type));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }

這裏就出現了Mybatis的一個優化點:
場景:knownMappers集合種有很多mapper,問題如何高效有序加載執行。因爲我們配置都是

mybatis.mapper-locations=classpath:mapper/*.xml

經過掃描@Mapper註解,加載了上千個怎麼辦?
上面將返回的值設給parseConfiguration時,

this.mapperElement(root.evalNode("mappers"));

上面的方法種調用his.configuration.addMappers(resource):

private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }

                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

上面如何來優化:
利用反射修改,進行小數據量和大數量分離,分別用單線程和多線程去處理,可以發現小數據單線程快,大數據多線程快,因此採取一個臨界值去分開處理。
另外我們還要考慮:
線程優化,考慮加鎖解鎖時間,集合擴容時間,線程切換,可以考慮自旋鎖。多線程不一定比單線程快。CAS樂觀鎖等。

回到上面addMapper方法種 parser.parse(),又在parse方法種調用bindMapperForNamespace,通過namespace反射到綁定的類,放入到configuration集合中,同時namespace放入addLoadedResource加載資源中:

private void bindMapperForNamespace() {
        String namespace = this.builderAssistant.getCurrentNamespace();
        if (namespace != null) {
            Class boundType = null;

            try {
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException var4) {
            }

            if (boundType != null && !this.configuration.hasMapper(boundType)) {
                this.configuration.addLoadedResource("namespace:" + namespace);
                this.configuration.addMapper(boundType);
            }
        }

    }

上面有寫道返回new DefaultSqlSessionFactory(config):這個過程又做了什麼呢?

Mapper實例生成:
在binding包中有一個類叫MapperProxy類:裏面有反射實現

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            if (method.isDefault()) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }

        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
            return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
        });
    }

如上,MapperProxy的生產過程,自定義Mapper類的對象是通過動態代理生產的,調用自定義方法的時候實際上是調用了MapperMethod的execute方法

MapperMethod類中,execute實行交互傳入得到sqlSession,args。可以發現裏面CRUD都有了。
在實行CRUD之前:
會執行SqlCommand方法:
這裏將namespace和方法名拼接爲statement:查看configuration有無,有取返回,無則拿到所有的接口遍歷遞歸。

private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {
            String statementId = mapperInterface.getName() + "." + methodName;
            if (configuration.hasStatement(statementId)) {
                return configuration.getMappedStatement(statementId);
            } else if (mapperInterface.equals(declaringClass)) {
                return null;
            } else {
                Class[] var6 = mapperInterface.getInterfaces();
                int var7 = var6.length;

                for(int var8 = 0; var8 < var7; ++var8) {
                    Class<?> superInterface = var6[var8];
                    if (declaringClass.isAssignableFrom(superInterface)) {
                        MappedStatement ms = this.resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
                        if (ms != null) {
                            return ms;
                        }
                    }
                }

                return null;
            }
        }

再執行MethodSignature方法拿到返回的類型。

拿着準備好的sqlSession,args反射執行execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        Object param;
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

現在是查詢:走convertArgsToSqlCommandParam將參數sqlCommand。在執行selectOne(this.command.getName(), param):
再走到selectList: 得到ms ,它是信息的封裝,比如resource、configuration、sqlSource、parameter等等,你執行當前sql所需要的信息。

 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
        } finally {
            ErrorContext.instance().reset();
        }

        return var5;
    }

接下來就是核心了:

this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)

BaseExecutor類中的query方法:
先查mybatis一級緩存,創建緩存key。進入複用方法,查詢queryStack。執行queryFromDatabase方法

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    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 (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator var8 = this.deferredLoads.iterator();

                while(var8.hasNext()) {
                    BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }

根據情況localCache增加移除:最終執行doQuery

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            this.localCache.removeObject(key);
        }

        this.localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            this.localOutputParameterCache.putObject(key, parameter);
        }

        return list;
    }

BatchExecutor類中:
可發現這裏誰執行誰關閉,怎麼做到的,因爲一次ms,只有一次事務。
將configuration 交由StatementHandler 來處理。這裏的過程就是原生sql的創建到查詢的過程。
創建連接Connection --通過handler.prepare得到Statement –

public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;

        List var10;
        try {
            this.flushStatements();
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
            Connection connection = this.getConnection(ms.getStatementLog());
            stmt = handler.prepare(connection, this.transaction.getTimeout());
            handler.parameterize(stmt);
            var10 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var10;
    }

方法會走到eparePreparedStatementHandler類中instantiateStatement方法,以及並調用parameterize向下轉型。這個是不是就很眼熟了呢。

 protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = this.boundSql.getSql();
        if (this.mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = this.mappedStatement.getKeyColumns();
            return keyColumnNames == null ? connection.prepareStatement(sql, 1) : connection.prepareStatement(sql, keyColumnNames);
        } else {
            return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareStatement(sql) : connection.prepareStatement(sql, this.mappedStatement.getResultSetType().getValue(), 1007);
        }
    }

    public void parameterize(Statement statement) throws SQLException {
        this.parameterHandler.setParameters((PreparedStatement)statement);
    }

在執行handler的query方法:
從boundSql中得到sql語句,execute執行sql

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = this.boundSql.getSql();
        statement.execute(sql);
        return this.resultSetHandler.handleResultSets(statement);
    }

DefaultResultSetHandler類中拿到我們返回結果集。
我們可以發現該方法中ResultMap,這個是不是更眼熟了呢。這個就是xsm中的ResultMap。ResultMap類就是對動態映射的封裝。

public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
        List<Object> multipleResults = new ArrayList();
        int resultSetCount = 0;
        ResultSetWrapper rsw = this.getFirstResultSet(stmt);
        List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        this.validateResultMapsCount(rsw, resultMapCount);

        while(rsw != null && resultMapCount > resultSetCount) {
            ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
            this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
            rsw = this.getNextResultSet(stmt);
            this.cleanUpAfterHandlingResultSet();
            ++resultSetCount;
        }

        String[] resultSets = this.mappedStatement.getResultSets();
        if (resultSets != null) {
            while(rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                    this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
                }

                rsw = this.getNextResultSet(stmt);
                this.cleanUpAfterHandlingResultSet();
                ++resultSetCount;
            }
        }

        return this.collapseSingleResultList(multipleResults);
    }

通過映射,調用實體類的所有構造器,返回對象。到此結束。
構造主要體現在DefaultResultSetHandler類中如下方法:

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
        Class<?> resultType = resultMap.getType();
        MetaClass metaType = MetaClass.forClass(resultType, this.reflectorFactory);
        List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        if (this.hasTypeHandlerForResultObject(rsw, resultType)) {
            return this.createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } else if (!constructorMappings.isEmpty()) {
            return this.createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
        } else if (!resultType.isInterface() && !metaType.hasDefaultConstructor()) {
            if (this.shouldApplyAutomaticMappings(resultMap, false)) {
                return this.createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
            } else {
                throw new ExecutorException("Do not know how to create an instance of " + resultType);
            }
        } else {
            return this.objectFactory.create(resultType);
        }
    }
private Object createByConstructorSignature(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
        Constructor<?>[] constructors = resultType.getDeclaredConstructors();
        Constructor<?> defaultConstructor = this.findDefaultConstructor(constructors);
        if (defaultConstructor != null) {
            return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, defaultConstructor);
        } else {
            Constructor[] var7 = constructors;
            int var8 = constructors.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                Constructor<?> constructor = var7[var9];
                if (this.allowedConstructorUsingTypeHandlers(constructor, rsw.getJdbcTypes())) {
                    return this.createUsingConstructor(rsw, resultType, constructorArgTypes, constructorArgs, constructor);
                }
            }

            throw new ExecutorException("No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames());
        }
    }

    private Object createUsingConstructor(ResultSetWrapper rsw, Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor) throws SQLException {
        boolean foundValues = false;

        for(int i = 0; i < constructor.getParameterTypes().length; ++i) {
            Class<?> parameterType = constructor.getParameterTypes()[i];
            String columnName = (String)rsw.getColumnNames().get(i);
            TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
            Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
            constructorArgTypes.add(parameterType);
            constructorArgs.add(value);
            foundValues = value != null || foundValues;
        }

        return foundValues ? this.objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章