zoukankan      html  css  js  c++  java
  • mybatis(二)创建代理类执行sql

    创建SqlSession

    public static void main(String[] args) throws IOException {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                Employee employeeMapper = sqlSession.getMapper(Employee.class);
                List<Employee> all = employeeMapper.getAll();
                for (Employee item : all)
                    System.out.println(item);
            } finally {
                sqlSession.close();
            }
        }
    
    //DefaultSqlSessionFactory
        public SqlSession openSession() {
            return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
        }
    
     /**
         * ExecutorType 指定Executor的类型,分为三种:SIMPLE, REUSE, BATCH,默认使用的是SIMPLE
         * TransactionIsolationLevel 指定事务隔离级别,使用null,则表示使用数据库默认的事务隔离界别
         * autoCommit 是否自动提交,传过来的参数为false,表示不自动提交
         */
        private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
            Transaction tx = null;
            try {
                // 获取配置中的环境信息,包括了数据源信息、事务等
                final Environment environment = configuration.getEnvironment();
                // 创建事务工厂
                final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
                // 创建事务,配置事务属性
                tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
                // 创建Executor,即执行器
                // 它是真正用来Java和数据库交互操作的类,后面会展开说。
                final Executor executor = configuration.newExecutor(tx, execType);//入口
                // 创建DefaultSqlSession对象返回,其实现了SqlSession接口
                return new DefaultSqlSession(configuration, executor, autoCommit);
            } catch (Exception e) {
                closeTransaction(tx);
                throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
            } finally {
                ErrorContext.instance().reset();
            }
        }
    
    //Configuration
     //创建一个执行器,默认是SIMPLE
        public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
            executorType = executorType == null ? defaultExecutorType : executorType;
            executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
            Executor executor;
            //根据executorType来创建相应的执行器,Configuration默认是SIMPLE
            if (ExecutorType.BATCH == executorType) {
                executor = new BatchExecutor(this, transaction);
            } else if (ExecutorType.REUSE == executorType) {
                executor = new ReuseExecutor(this, transaction);
            } else {
                //创建SimpleExecutor实例,并且包含Configuration和transaction属性
                executor = new SimpleExecutor(this, transaction);
            }
    
            //如果要求缓存,生成另一种CachingExecutor,装饰者模式,默认都是返回CachingExecutor
            /**
             * 二级缓存开关配置示例
             * <settings>
             *   <setting name="cacheEnabled" value="true"/>
             * </settings>
             */
            if (cacheEnabled) {
                //CachingExecutor使用装饰器模式,将executor的功能添加上了二级缓存的功能,二级缓存会单独文章来讲
                executor = new CachingExecutor(executor);
            }
            //此处调用插件,通过插件可以改变Executor行为,此处我们后面单独文章讲
            executor = (Executor) interceptorChain.pluginAll(executor);
            return executor;
        }
    
    1. 从configuration获取Environment对象,从Environment获取TransactionFactory,创建Transaction,默认是JdbcTransaction
    2. 创建Executor。如果开启二级缓存会创建CachingExecutor,CachingExecutor通过装饰者模式给执行器添加缓存功能。Executor支持三种:
      • SimpleExecutor:默认的,根据对应的sql直接执行,每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象
      • BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中,等待统一执行,它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor相当于维护了多个桶,每个桶里都装了很多属于自己的SQL,就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库。
      • ReuseExecutor:可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用flushStatements来清除Map。
    3. 创建DefaultSqlSession对象

    返回顶部

    创建Mapper代理类

    //DefaultSqlSessionFactory
    public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }
    //Configuration
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            return this.mapperRegistry.getMapper(type, sqlSession);
        }
        //MapperRegistry
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            //获取代理类工厂
            MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
            if (mapperProxyFactory == null) {
                throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
            } else {
                try {
                    //通过反射返回一个代理对象
                    //入口
                    return mapperProxyFactory.newInstance(sqlSession);
                } catch (Exception var5) {
                    throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
                }
            }
        }
    
        public T newInstance(SqlSession sqlSession) {
            MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
            return this.newInstance(mapperProxy);
        }
    
    1. knowMappers中获取代理工厂

    返回顶部

    通过代理类执行sql

    对目标方法的拦截执行在自定义拦截器MapperProxy的invoke方法。

    
    public class MapperProxy<T> implements InvocationHandler, Serializable {
        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;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 如果方法是定义在 Object 类中的,则直接调用
            if (Object.class.equals(method.getDeclaringClass())) {
                try {
                    return method.invoke(this, args);
                } catch (Throwable var5) {
                    throw ExceptionUtil.unwrapThrowable(var5);
                }
            } else {
                // 从缓存中获取 MapperMethod 对象,若缓存未命中,则创建 MapperMethod 对象
                MapperMethod mapperMethod = this.cachedMapperMethod(method);//入口1
                // 调用 execute 方法执行 SQL
                return mapperMethod.execute(this.sqlSession, args);//入口2
            }
        }
    
        private MapperMethod cachedMapperMethod(Method method) {
            MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
            if (mapperMethod == null) {
                //创建一个MapperMethod,参数为mapperInterface和method还有Configuration
                mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
                this.methodCache.put(method, mapperMethod);
            }
    
            return mapperMethod;
        }
    }
    
    //MapperMethod 
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        
        // 根据 SQL 类型执行相应的数据库操作
        switch (command.getType()) {
            case INSERT: {
                // 对用户传入的参数进行转换,下同
                Object param = method.convertArgsToSqlCommandParam(args);
                // 执行插入操作,rowCountResult 方法用于处理返回值
                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()) {
                    // 执行查询操作,并将结果封装在 Map 中返回
                    result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                    // 执行查询操作,并返回一个 Cursor 对象
                    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());
        }
        return result;
    }
    
    1. 检测被拦截的方法是不是定义在 Object 中的,比如 equals、hashCode 方法等。对于这类方法,直接执行即可
    2. 如果sql类型是:
      • INSERT:先通过convertArgsToSqlCommandParam方法处理参数,然后通过sqlSession执行insert,最后通过rowCountResult对结果转换处理
      • UPDATE:先通过convertArgsToSqlCommandParam方法处理参数,然后通过sqlSession执行update,最后通过rowCountResult对结果转换处理
      • DELETE: 先通过convertArgsToSqlCommandParam方法处理参数,然后通过sqlSession执行delete,最后通过rowCountResult对结果转换处理
      • SELECT:
        • 如果无返回值或者hasResultHandler
        • 有多个返回值
        • 返回的是map
        • 返回Cursor
        • 返回一个结果:先通过convertArgsToSqlCommandParam方法处理参数,然后通过sqlSession执行selectOne

    返回顶部

    INSERT

    UPDATE

    DELETE

    SELECT

    如果无返回值或者hasResultHandler

    有多个返回值

    返回一个结果

    //DefaultSqlSession
    public <T> T selectOne(String statement, Object parameter) {
        // 调用 selectList 获取结果
        List<T> list = this.<T>selectList(statement, parameter);//入口
        if (list.size() == 1) {
            // 返回结果
            return list.get(0);
        } else if (list.size() > 1) {
            // 如果查询结果大于1则抛出异常
            throw new TooManyResultsException(
                "Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
            return null;
        }
    }
    
    private final Executor executor;
    public <E> List<E> selectList(String statement, Object parameter) {
        // 调用重载方法
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
    }
    
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            // 通过MappedStatement的Id获取 MappedStatement
            MappedStatement ms = configuration.getMappedStatement(statement);
            // 调用 Executor 实现类中的 query 方法
            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();
        }
    }
    
    //CachingExecutor
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 获取 BoundSql
        //入口
        BoundSql boundSql = ms.getBoundSql(parameterObject);
       // 创建 CacheKey
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        // 调用重载方法
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    

    创建BoundSql对象

    //MappedStatement
        public BoundSql getBoundSql(Object parameterObject) {
            BoundSql boundSql = this.sqlSource.getBoundSql(parameterObject);//入口
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            if (parameterMappings == null || parameterMappings.isEmpty()) {
                boundSql = new BoundSql(this.configuration, boundSql.getSql(), this.parameterMap.getParameterMappings(), parameterObject);
            }
    
            Iterator var4 = boundSql.getParameterMappings().iterator();
    
            while(var4.hasNext()) {
                ParameterMapping pm = (ParameterMapping)var4.next();
                String rmId = pm.getResultMapId();
                if (rmId != null) {
                    ResultMap rm = this.configuration.getResultMap(rmId);
                    if (rm != null) {
                        this.hasNestedResultMaps |= rm.hasNestedResultMaps();
                    }
                }
            }
    
            return boundSql;
        }
    
    //DynamicSqlSource
        public BoundSql getBoundSql(Object parameterObject) {
            // 创建 DynamicContext
            DynamicContext context = new DynamicContext(configuration, parameterObject);
    
            // 解析 SQL 片段,并将解析结果存储到 DynamicContext 中,这里会将${}替换成method对应的运行时参数,也会解析<if><where>等SqlNode
            rootSqlNode.apply(context);
    
            SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
            Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
            /*
             * 构建 StaticSqlSource,在此过程中将 sql 语句中的占位符 #{} 替换为问号 ?,
             * 并为每个占位符构建相应的 ParameterMapping
             */
            SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());//入口
    
            // 调用 StaticSqlSource 的 getBoundSql 获取 BoundSql
            BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    
            // 将 DynamicContext 的 ContextMap 中的内容拷贝到 BoundSql 中
            for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
                boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
            }
            return boundSql;
        }
    
    
    
    //SqlSourceBuilder
    public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
        // 创建 #{} 占位符处理器
        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
        // 创建 #{} 占位符解析器
        GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
        // 解析 #{} 占位符,并返回解析结果字符串
        String sql = parser.parse(originalSql);//入口
        // 封装解析结果到 StaticSqlSource 中,并返回,因为所有的动态参数都已经解析了,可以封装成一个静态的SqlSource
        return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
    }
    
    
        public String parse(String text) {
            if (text != null && !text.isEmpty()) {
                char[] src = text.toCharArray();
                int offset = 0;
                int start = text.indexOf(this.openToken, offset);
                if (start == -1) {
                    return text;
                } else {
                    StringBuilder builder = new StringBuilder();
    
                    for(StringBuilder expression = null; start > -1; start = text.indexOf(this.openToken, offset)) {
                        if (start > 0 && src[start - 1] == '\') {
                            builder.append(src, offset, start - offset - 1).append(this.openToken);
                            offset = start + this.openToken.length();
                        } else {
                            if (expression == null) {
                                expression = new StringBuilder();
                            } else {
                                expression.setLength(0);
                            }
    
                            builder.append(src, offset, start - offset);
                            offset = start + this.openToken.length();
    
                            int end;
                            for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
                                if (end <= offset || src[end - 1] != '\') {
                                    expression.append(src, offset, end - offset);
                                    int var10000 = end + this.closeToken.length();
                                    break;
                                }
    
                                expression.append(src, offset, end - offset - 1).append(this.closeToken);
                                offset = end + this.closeToken.length();
                            }
    
                            if (end == -1) {
                                builder.append(src, start, src.length - start);
                                offset = src.length;
                            } else {
                                builder.append(this.handler.handleToken(expression.toString()));//入口
                                offset = end + this.closeToken.length();
                            }
                        }
                    }
    
                    if (offset < src.length) {
                        builder.append(src, offset, src.length - offset);
                    }
    
                    return builder.toString();
                }
            } else {
                return "";
            }
        }
    
    
    public String handleToken(String content) {
        // 获取 content 的对应的 ParameterMapping
        parameterMappings.add(buildParameterMapping(content));//入口
        // 返回 ?
        return "?";
    }
    
    
    
    private ParameterMapping buildParameterMapping(String content) {
        /*
         * 将#{xxx} 占位符中的内容解析成 Map。
         *   #{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
         *      上面占位符中的内容最终会被解析成如下的结果:
         *  {
         *      "property": "age",
         *      "typeHandler": "MyTypeHandler", 
         *      "jdbcType": "NUMERIC", 
         *      "javaType": "int"
         *  }
         */
        Map<String, String> propertiesMap = parseParameterMapping(content);
        String property = propertiesMap.get("property");
        Class<?> propertyType;
        // metaParameters 为 DynamicContext 成员变量 bindings 的元信息对象
        if (metaParameters.hasGetter(property)) {
            propertyType = metaParameters.getGetterType(property);
        
        /*
         * parameterType 是运行时参数的类型。如果用户传入的是单个参数,比如 Employe 对象,此时 
         * parameterType 为 Employe.class。如果用户传入的多个参数,比如 [id = 1, author = "chenhao"],
         * MyBatis 会使用 ParamMap 封装这些参数,此时 parameterType 为 ParamMap.class。
         */
        } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
            propertyType = parameterType;
        } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
            propertyType = java.sql.ResultSet.class;
        } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
            propertyType = Object.class;
        } else {
            /*
             * 代码逻辑走到此分支中,表明 parameterType 是一个自定义的类,
             * 比如 Employe,此时为该类创建一个元信息对象
             */
            MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
            // 检测参数对象有没有与 property 想对应的 getter 方法
            if (metaClass.hasGetter(property)) {
                // 获取成员变量的类型
                propertyType = metaClass.getGetterType(property);
            } else {
                propertyType = Object.class;
            }
        }
        
        ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
        
        // 将 propertyType 赋值给 javaType
        Class<?> javaType = propertyType;
        String typeHandlerAlias = null;
        
        // 遍历 propertiesMap
        for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
            String name = entry.getKey();
            String value = entry.getValue();
            if ("javaType".equals(name)) {
                // 如果用户明确配置了 javaType,则以用户的配置为准
                javaType = resolveClass(value);
                builder.javaType(javaType);
            } else if ("jdbcType".equals(name)) {
                // 解析 jdbcType
                builder.jdbcType(resolveJdbcType(value));
            } else if ("mode".equals(name)) {...} 
            else if ("numericScale".equals(name)) {...} 
            else if ("resultMap".equals(name)) {...} 
            else if ("typeHandler".equals(name)) {
                typeHandlerAlias = value;    
            } 
            else if ("jdbcTypeName".equals(name)) {...} 
            else if ("property".equals(name)) {...} 
            else if ("expression".equals(name)) {
                throw new BuilderException("Expression based parameters are not supported yet");
            } else {
                throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content
                    + "}.  Valid properties are " + parameterProperties);
            }
        }
        if (typeHandlerAlias != null) {
            builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
        }
        
        // 构建 ParameterMapping 对象
        return builder.build();
    }
    
    1. 从configuration中获取MappedStatement
    2. 将数据从MappedStatement转换到BoundSql
    3. 调用Executor的query方法

    Executor.query

    //CachingExecutor
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        // 从 MappedStatement 中获取缓存
        Cache cache = ms.getCache();
        // 若映射文件中未配置缓存或参照缓存,此时 cache = null
        if (cache != null) {
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                ensureNoOutParams(ms, boundSql);
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    // 若缓存未命中,则调用被装饰类的 query 方法,也就是SimpleExecutor的query方法
                    list = delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
            }
        }
        // 调用被装饰类的 query 方法,这里的delegate我们知道应该是SimpleExecutor
        return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    
    
    
    //BaseExecutor
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            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();
            }
            deferredLoads.clear();
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                clearLocalCache();
            }
        }
        return list;
    }
    
    //BaseExecutor
    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 进行查询
            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;
    }
    
    
    
    //SimpleExecutor
    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
            //入口1
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 创建 Statement
            //入口2
            stmt = prepareStatement(handler, ms.getStatementLog());
            // 执行查询操作
            return handler.query(stmt, resultHandler);
        } finally {
            // 关闭 Statement
            closeStatement(stmt);
        }
    }
    
    
    
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
        Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 创建具有路由功能的 StatementHandler
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        // 应用插件到 StatementHandler 上
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }
    
    
    
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        // 获取数据库连接
        Connection connection = getConnection(statementLog);
        // 创建 Statement,
        stmt = handler.prepare(connection, transaction.getTimeout());
      // 为 Statement 设置参数
        handler.parameterize(stmt);
        return stmt;
    }
    
    1. 先从一级缓存查询,没查询到从二级缓存查询,如果还为查询到则从数据库查询
    2. 创建RoutingStatementHandler
    3. 创建prepareStatement
    4. 通过RoutingStatementHandler执行query

    创建prepareStatement

    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        Statement statement = null;
        try {
            // 创建 Statement
            statement = instantiateStatement(connection);//入口
            // 设置超时和 FetchSize
            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);
        }
    }
    
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        //获取sql字符串,比如"select * from user where id= ?"
        String sql = boundSql.getSql();
        // 根据条件调用不同的 prepareStatement 方法创建 PreparedStatement
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = mappedStatement.getKeyColumns();
            if (keyColumnNames == null) {
                //通过connection获取Statement,将sql语句传进去
                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);
        }
    }
    
    
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
           
        boolean canServerPrepare = true;
    
        String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql) : sql;
    
        if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) {
            canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
        }
    
        if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) {
            canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
        }
    
        if (this.useServerPreparedStmts && canServerPrepare) {
            if (this.getCachePreparedStatements()) {
                ......
            } else {
                try {
                    //这里使用的是ServerPreparedStatement创建PreparedStatement
                    pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType, resultSetConcurrency);
    
                    pStmt.setResultSetType(resultSetType);
                    pStmt.setResultSetConcurrency(resultSetConcurrency);
                } catch (SQLException sqlEx) {
                    // Punt, if necessary
                    if (getEmulateUnsupportedPstmts()) {
                        pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
                    } else {
                        throw sqlEx;
                    }
                }
            }
        } else {
            pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
        }
    }
    
    
    public class ServerPreparedStatement extends PreparedStatement {
        //存放运行时参数的数组
        private ServerPreparedStatement.BindValue[] parameterBindings;
        //服务器预编译好的sql语句返回的serverStatementId
        private long serverStatementId;
        private void serverPrepare(String sql) throws SQLException {
            synchronized(this.connection.getMutex()) {
                MysqlIO mysql = this.connection.getIO();
                try {
                    //向sql服务器发送了一条PREPARE指令
                    Buffer prepareResultPacket = mysql.sendCommand(MysqlDefs.COM_PREPARE, sql, (Buffer)null, false, characterEncoding, 0);
                    //记录下了预编译好的sql语句所对应的serverStatementId
                    this.serverStatementId = prepareResultPacket.readLong();
                    this.fieldCount = prepareResultPacket.readInt();
                    //获取参数个数,比喻 select * from user where id= ?and name = ?,其中有两个?,则这里返回的参数个数应该为2
                    this.parameterCount = prepareResultPacket.readInt();
                    this.parameterBindings = new ServerPreparedStatement.BindValue[this.parameterCount];
    
                    for(int i = 0; i < this.parameterCount; ++i) {
                        //根据参数个数,初始化数组
                        this.parameterBindings[i] = new ServerPreparedStatement.BindValue();
                    }
    
                } catch (SQLException var16) {
                    throw sqlEx;
                } finally {
                    this.connection.getIO().clearInputStream();
                }
    
            }
        }
    }
    
    1. 最终是通过ServerPreparedStatement来创建PreparedStatement的
      ServerPreparedStatement初始化的时候就向sql服务器发送了一条PREPARE指令,把SQL语句传到mysql服务器,如select * from user where id= ?and name = ?,mysql服务器会对sql进行编译,并保存在服务器,返回预编译语句对应的id,并保存在ServerPreparedStatement中,同时创建BindValue[] parameterBindings数组,后面设置参数就直接添加到此数组中。

    将运行时参数设置到PreparedStatement

    //
    public void parameterize(Statement statement) throws SQLException {
        // 通过参数处理器 ParameterHandler 设置运行时参数到 PreparedStatement 中
        parameterHandler.setParameters((PreparedStatement) statement);
    }
    
    public class DefaultParameterHandler implements ParameterHandler {
        private final TypeHandlerRegistry typeHandlerRegistry;
        private final MappedStatement mappedStatement;
        private final Object parameterObject;
        private final BoundSql boundSql;
        private final Configuration configuration;
    
        public void setParameters(PreparedStatement ps) {
            /*
             * 从 BoundSql 中获取 ParameterMapping 列表,每个 ParameterMapping 与原始 SQL 中的 #{xxx} 占位符一一对应
             */
            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)) {
                            value = boundSql.getAdditionalParameter(propertyName);
                        } else if (parameterObject == null) {
                            value = null;
                        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                            value = parameterObject;
                        } else {
                            // 为用户传入的参数 parameterObject 创建元信息对象
                            MetaObject metaObject = configuration.newMetaObject(parameterObject);
                            // 从用户传入的参数中获取 propertyName 对应的值
                            value = metaObject.getValue(propertyName);
                        }
    
                        TypeHandler typeHandler = parameterMapping.getTypeHandler();
                        JdbcType jdbcType = parameterMapping.getJdbcType();
                        if (value == null && jdbcType == null) {
                            jdbcType = configuration.getJdbcTypeForNull();
                        }
                        try {
                            // 由类型处理器 typeHandler 向 ParameterHandler 设置参数
                            typeHandler.setParameter(ps, i + 1, value, jdbcType);//入口
                        } catch (TypeException e) {
                            throw new TypeException(...);
                        } catch (SQLException e) {
                            throw new TypeException(...);
                        }
                    }
                }
            }
        }
    }
    
    1. 首先从boundSql中获取parameterMappings 集合,然后遍历获取 parameterMapping中的propertyName ,如#{name} 中的name,然后从运行时参数parameterObject中获取name对应的参数值,最后设置到PreparedStatement 中,我们主要来看是如何设置参数的。

    typeHandler.setParameter(ps, i + 1, value, jdbcType);,这句代码最终会向我们例子中一样执行,如下

    //
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }
    

    还记得我们的PreparedStatement是什么吗?是ServerPreparedStatement,那我们就来看看ServerPreparedStatement的setString方法

    //
    public void setString(int parameterIndex, String x) throws SQLException {
        this.checkClosed();
        if (x == null) {
            this.setNull(parameterIndex, 1);
        } else {
            //根据参数下标从parameterBindings数组总获取BindValue
            ServerPreparedStatement.BindValue binding = this.getBinding(parameterIndex, false);
            this.setType(binding, this.stringTypeCode);
            //设置参数值
            binding.value = x;
            binding.isNull = false;
            binding.isLongData = false;
        }
    
    }
    
    protected ServerPreparedStatement.BindValue getBinding(int parameterIndex, boolean forLongData) throws SQLException {
        this.checkClosed();
        if (this.parameterBindings.length == 0) {
            throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.8"), "S1009", this.getExceptionInterceptor());
        } else {
            --parameterIndex;
            if (parameterIndex >= 0 && parameterIndex < this.parameterBindings.length) {
                if (this.parameterBindings[parameterIndex] == null) {
                    this.parameterBindings[parameterIndex] = new ServerPreparedStatement.BindValue();
                } else if (this.parameterBindings[parameterIndex].isLongData && !forLongData) {
                    this.detectedLongParameterSwitch = true;
                }
    
                this.parameterBindings[parameterIndex].isSet = true;
                this.parameterBindings[parameterIndex].boundBeforeExecutionNum = (long)this.numberOfExecutions;
                //根据参数下标从parameterBindings数组总获取BindValue
                return this.parameterBindings[parameterIndex];
            } else {
                throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.9") + (parameterIndex + 1) + Messages.getString("ServerPreparedStatement.10") + this.parameterBindings.length, "S1009", this.getExceptionInterceptor());
            }
        }
    }
    

    就是根据参数下标从ServerPreparedStatement的参数数组parameterBindings中获取BindValue对象,然后设置值,好了现在ServerPreparedStatement包含了预编译SQL语句的Id和参数数组,最后一步便是执行SQL了。

    RoutingStatementHandler.query

    //
    return handler.<E>query(stmt, resultHandler);
    

    我们来看看query是怎么做的

    //
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement)statement;
        //直接执行ServerPreparedStatement的execute方法
        ps.execute();
        return this.resultSetHandler.handleResultSets(ps);
    }
    
    public boolean execute() throws SQLException {
        this.checkClosed();
        ConnectionImpl locallyScopedConn = this.connection;
        if (!this.checkReadOnlySafeStatement()) {
            throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), "S1009", this.getExceptionInterceptor());
        } else {
            ResultSetInternalMethods rs = null;
            CachedResultSetMetaData cachedMetadata = null;
            synchronized(locallyScopedConn.getMutex()) {
                //略....
                rs = this.executeInternal(rowLimit, sendPacket, doStreaming, this.firstCharOfStmt == 'S', metadataFromCache, false);
                //略....
            }
    
            return rs != null && rs.reallyResult();
        }
    }
    

    省略了很多代码,只看最关键的executeInternal

    //ServerPreparedStatement
    protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, Buffer sendPacket, boolean createStreamingResultSet, boolean queryIsSelectOnly, Field[] metadataFromCache, boolean isBatch) throws SQLException {
        try {
            return this.serverExecute(maxRowsToRetrieve, createStreamingResultSet, metadataFromCache);
        } catch (SQLException var11) {
            throw sqlEx;
        } 
    }
    
    private ResultSetInternalMethods serverExecute(int maxRowsToRetrieve, boolean createStreamingResultSet, Field[] metadataFromCache) throws SQLException {
        synchronized(this.connection.getMutex()) {
            //略....
            MysqlIO mysql = this.connection.getIO();
            Buffer packet = mysql.getSharedSendPacket();
            packet.clear();
            packet.writeByte((byte)MysqlDefs.COM_EXECUTE);
            //将该语句对应的id写入数据包
            packet.writeLong(this.serverStatementId);
    
            int i;
            //将对应的参数写入数据包
            for(i = 0; i < this.parameterCount; ++i) {
                if (!this.parameterBindings[i].isLongData) {
                    if (!this.parameterBindings[i].isNull) {
                        this.storeBinding(packet, this.parameterBindings[i], mysql);
                    } else {
                        nullBitsBuffer[i / 8] = (byte)(nullBitsBuffer[i / 8] | 1 << (i & 7));
                    }
                }
            }
            //发送数据包,表示执行id对应的预编译sql
            Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE, (String)null, packet, false, (String)null, 0);
            //略....
            ResultSetImpl rs = mysql.readAllResults(this,  this.resultSetType,  resultPacket, true, (long)this.fieldCount, metadataFromCache);
            //返回结果
            return rs;
        }
    }
    

    ServerPreparedStatement在记录下serverStatementId后,对于相同SQL模板的操作,每次只是发送serverStatementId和对应的参数,省去了编译sql的过程。 至此我们的已经从数据库拿到了查询结果。

    返回顶部

  • 相关阅读:
    javascript:showModelDialog注意点
    VS.NET 查找未使用过的方法
    JAVASCRIPT:style 中visibility和display之间的区别
    基于MapServer的WebGIS开发http://www.gisforum.net/show.aspx?id=1491&cid=27
    POJ 1845 Sumdiv
    xmu 1254.异或求和
    hdu 4282 A very hard mathematic problem
    POJ Longge's problem 2480
    hdu 1199 Color the Ball
    HDU 1492 The number of divisors(约数) about Humble Numbers
  • 原文地址:https://www.cnblogs.com/yanhui007/p/12601031.html
Copyright © 2011-2022 走看看