1. <legend id="omqbf"></legend><samp id="omqbf"><sup id="omqbf"><del id="omqbf"></del></sup></samp>
      2. <ol id="omqbf"><menu id="omqbf"></menu></ol>
          <input id="omqbf"></input>
        1. <samp id="omqbf"></samp>
        2. 還未設置個性簽名
          成為VIP會員 享9項特權: 開通會員

          完善資料讓更多小伙伴認識你,還能領取20積分哦, 立即完善>

          3天內不再提示

          一文了解MyBatis的查詢原理

          OSC開源社區 ? 來源:京東技術 ? 作者:李春廷 ? 2022-10-10 11:42 ? 次閱讀

          導讀

          本文通過MyBatis一個低版本的bug(3.4.5之前的版本)入手,分析MyBatis的一次完整的查詢流程,從配置文件的解析到一個查詢的完整執行過程詳細解讀MyBatis的一次查詢流程,通過本文可以詳細了解MyBatis的一次查詢過程。在平時的代碼編寫中,發現了MyBatis一個低版本的bug(3.4.5之前的版本),由于現在很多工程中的版本都是低于3.4.5的,因此在這里用一個簡單的例子復現問題,并且從源碼角度分析MyBatis一次查詢的流程,讓大家了解MyBatis的查詢原理。

          01 問題現象

          在今年的敏捷團隊建設中,我通過Suite執行器實現了一鍵自動化單元測試。Juint除了Suite執行器還有哪些執行器呢?由此我的Runner探索之旅開始了!

          1.1 場景問題復現

          如下圖所示,在示例Mapper中,下面提供了一個方法queryStudents,從student表中查詢出符合查詢條件的數據,入參可以為student_name或者student_name的集合,示例中參數只傳入的是studentName的List集合

           List studentNames = new LinkedList<>();
           studentNames.add("lct");
           studentNames.add("lct2");
           condition.setStudentNames(studentNames);

          期望運行的結果是
          select * from student WHERE student_name IN ( 'lct' , 'lct2' )
          但是實際上運行的結果是

          ==> Preparing: select * from student WHERE student_name IN ( ? , ? ) AND student_name = ?

          ==> Parameters: lct(String), lct2(String), lct2(String)

          <== Columns: id, student_name, age

          <== Row: 2, lct2, 2

          <== Total: 1

          通過運行結果可以看到,沒有給student_name單獨賦值,但是經過MyBatis解析以后,單獨給student_name賦值了一個值,可以推斷出MyBatis在解析SQL并對變量賦值的時候是有問題的,初步猜測是foreach循環中的變量的值帶到了foreach外邊,導致SQL解析出現異常,下面通過源碼進行分析驗證

          02 MyBatis查詢原理

          理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板后進行模板加載,加載階段會將產物轉換為視圖樹的結構,轉換完成后將通過表達式引擎解析表達式并取得正確的值,通過事件解析引擎解析用戶自定義事件并完成事件的綁定,完成解析賦值以及事件綁定后進行視圖的渲染,最終將目標頁面展示到屏幕。

          2.1 MyBatis架構

          2.1.1 架構圖

          先簡單來看看MyBatis整體上的架構模型,從整體上看MyBatis主要分為四大模塊:

          接口層:主要作用就是和數據庫打交道

          數據處理層:數據處理層可以說是MyBatis的核心,它要完成兩個功能:

          通過傳入參數構建動態SQL語句;

          SQL語句的執行以及封裝查詢結果集成List

          框架支撐層:主要有事務管理、連接池管理、緩存機制和SQL語句的配置方式

          引導層:引導層是配置和啟動MyBatis 配置信息的方式。MyBatis 提供兩種方式來引導MyBatis :基于XML配置文件的方式和基于Java API 的方式

          2.1.2 MyBatis四大對象

          貫穿MyBatis整個框架的有四大核心對象,ParameterHandler、ResultSetHandler、StatementHandler和Executor,四大對象貫穿了整個框架的執行過程,四大對象的主要作用為:

          ParameterHandler:設置預編譯參數

          ResultSetHandler:處理SQL的返回結果集

          StatementHandler:處理sql語句預編譯,設置參數等相關工作

          Executor:MyBatis的執行器,用于執行增刪改查操作

          2.2從源碼解讀MyBatis的一次查詢過程

          首先給出復現問題的代碼以及相應的準備過程

          2.2.1 數據準備

          CREATE TABLE `student`  (
            `id` bigint(20) NOT NULL AUTO_INCREMENT,
            `student_name` varchar(255) NULL DEFAULT NULL,
            `age` int(11) NULL DEFAULT NULL,
            PRIMARY KEY (`id`) USING BTREE
          ) ENGINE = InnoDB AUTO_INCREMENT = 1;
          
          
          -- ----------------------------
          -- Records of student
          -- ----------------------------
          INSERT INTO `student` VALUES (1, 'lct', 1);
          INSERT INTO `student` VALUES (2, 'lct2', 2);

          2.2.2 代碼準備

          1.mapper配置文件

          2.示例代碼

          public static void main(String[] args) throws IOException {
                  String resource = "mybatis-config.xml";
                  InputStream inputStream = Resources.getResourceAsStream(resource);
                  //1.獲取SqlSessionFactory對象
                  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                  //2.獲取對象
                  SqlSession sqlSession = sqlSessionFactory.openSession();
                  //3.獲取接口的代理類對象
                  StudentDao mapper = sqlSession.getMapper(StudentDao.class);
                  StudentCondition condition = new StudentCondition();
                  List studentNames = new LinkedList<>();
                  studentNames.add("lct");
                  studentNames.add("lct2");
                  condition.setStudentNames(studentNames);
                  //執行方法
                  List students = mapper.queryStudents(condition);
              }

          2.2.3 查詢過程分析

          1.SqlSessionFactory的構建

          先看SqlSessionFactory的對象的創建過程

          //1.獲取SqlSessionFactory對象
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

          代碼中首先通過調用SqlSessionFactoryBuilder中的build方法來獲取對象,進入build方法

           public SqlSessionFactory build(InputStream inputStream) {
              return build(inputStream, null, null);
            }

          調用自身的build方法

          bb27fc9c-484c-11ed-a3b6-dac502259ad0.png

          圖1 build方法自身調用調試圖例

          在這個方法里會創建一個XMLConfigBuilder的對象,用來解析傳入的MyBatis的配置文件,然后調用parse方法進行解析

          bb586698-484c-11ed-a3b6-dac502259ad0.png

          圖2 parse解析入參調試圖例

          在這個方法中,會從MyBatis的配置文件的根目錄中獲取xml的內容,其中parser這個對象是一個XPathParser的對象,這個是專門用來解析xml文件的,具體怎么從xml文件中獲取到各個節點這里不再進行講解。這里可以看到解析配置文件是從configuration這個節點開始的,在MyBatis的配置文件中這個節點也是根節點

           
          
          
          
          
              
                     
              
          然后將解析好的xml文件傳入parseConfiguration方法中,在這個方法中會獲取在配置文件中的各個節點的配置

          bb98ffe6-484c-11ed-a3b6-dac502259ad0.png

          圖3 解析配置調試圖例

          以獲取mappers節點的配置來看具體的解析過程

          進入mapperElement方法

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

          bbb688ae-484c-11ed-a3b6-dac502259ad0.png

          圖4 mapperElement方法調試圖例

          看到MyBatis還是通過創建一個XMLMapperBuilder對象來對mappers節點進行解析,在parse方法中

          public void parse() {
            if (!configuration.isResourceLoaded(resource)) {
              configurationElement(parser.evalNode("/mapper"));
              configuration.addLoadedResource(resource);
              bindMapperForNamespace();
            }
          
          
            parsePendingResultMaps();
            parsePendingCacheRefs();
            parsePendingStatements();
          }

          通過調用configurationElement方法來解析配置的每一個mapper文件

          private void configurationElement(XNode context) {
            try {
              String namespace = context.getStringAttribute("namespace");
              if (namespace == null || namespace.equals("")) {
                throw new BuilderException("Mapper's namespace cannot be empty");
              }
              builderAssistant.setCurrentNamespace(namespace);
              cacheRefElement(context.evalNode("cache-ref"));
              cacheElement(context.evalNode("cache"));
              parameterMapElement(context.evalNodes("/mapper/parameterMap"));
              resultMapElements(context.evalNodes("/mapper/resultMap"));
              sqlElement(context.evalNodes("/mapper/sql"));
              buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
            } catch (Exception e) {
              throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
            }
          }

          以解析mapper中的增刪改查的標簽來看看是如何解析一個mapper文件的

          進入buildStatementFromContext方法

          private void buildStatementFromContext(List list, String requiredDatabaseId) {
            for (XNode context : list) {
              final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
              try {
                statementParser.parseStatementNode();
              } catch (IncompleteElementException e) {
                configuration.addIncompleteStatement(statementParser);
              }
            }
          }

          可以看到MyBatis還是通過創建一個XMLStatementBuilder對象來對增刪改查節點進行解析,通過調用這個對象的parseStatementNode方法,在這個方法里會獲取到配置在這個標簽下的所有配置信息,然后進行設置

          bbf41318-484c-11ed-a3b6-dac502259ad0.png

          圖5 parseStatementNode方法調試圖例

          解析完成以后,通過方法addMappedStatement將所有的配置都添加到一個MappedStatement中去,然后再將mappedstatement添加到configuration中去

          builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
              fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
              resultSetTypeEnum, flushCache, useCache, resultOrdered, 
              keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

          bc14bf64-484c-11ed-a3b6-dac502259ad0.png

          圖6 增加解析完成的mapper方法調試圖例

          可以看到一個mappedstatement中包含了一個增刪改查標簽的詳細信息

          bc7ef898-484c-11ed-a3b6-dac502259ad0.png

          圖7 mappedstatement對象方法調試圖例

          而一個configuration就包含了所有的配置信息,其中mapperRegistertry和mappedStatements

          bcbddaea-484c-11ed-a3b6-dac502259ad0.png

          圖8 config對象方法調試圖例

          具體的流程

          bcebfc40-484c-11ed-a3b6-dac502259ad0.png

          圖9 SqlSessionFactory對象的構建過程

          2.SqlSession的創建過程

          SqlSessionFactory創建完成以后,接下來看看SqlSession的創建過程

          SqlSession sqlSession = sqlSessionFactory.openSession();

          首先會調用DefaultSqlSessionFactory的openSessionFromDataSource方法

          @Override
          public SqlSession openSession() {
            return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
          }

          在這個方法中,首先會從configuration中獲取DataSource等屬性組成對象Environment,利用Environment內的屬性構建一個事務對象TransactionFactory

          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);
              final Executor executor = configuration.newExecutor(tx, execType);
              return new DefaultSqlSession(configuration, executor, autoCommit);
            } catch (Exception e) {
              closeTransaction(tx); // may have fetched a connection so lets call close()
              throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
            } finally {
              ErrorContext.instance().reset();
            }
          }

          事務創建完成以后開始創建Executor對象,Executor對象的創建是根據 executorType創建的,默認是SIMPLE類型的,沒有配置的情況下創建了SimpleExecutor,如果開啟二級緩存的話,則會創建CachingExecutor

          public Executor newExecutor(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);
            } else if (ExecutorType.REUSE == executorType) {
              executor = new ReuseExecutor(this, transaction);
            } else {
              executor = new SimpleExecutor(this, transaction);
            }
            if (cacheEnabled) {
              executor = new CachingExecutor(executor);
            }
            executor = (Executor) interceptorChain.pluginAll(executor);
            return executor;
          }

          創建executor以后,會執行executor = (Executor) interceptorChain.pluginAll(executor)方法,這個方法對應的含義是使用每一個攔截器包裝并返回executor,最后調用DefaultSqlSession方法創建SqlSession

          bd01a34c-484c-11ed-a3b6-dac502259ad0.png

          圖10 SqlSession對象的創建過程

          3.Mapper的獲取過程

          有了SqlSessionFactory和SqlSession以后,就需要獲取對應的Mapper,并執行mapper中的方法

          StudentDao mapper = sqlSession.getMapper(StudentDao.class);

          在第一步中知道所有的mapper都放在MapperRegistry這個對象中,因此通過調用org.apache.ibatis.binding.MapperRegistry#getMapper方法來獲取對應的mapper

          public  T getMapper(Class type, SqlSession sqlSession) {
            final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
            if (mapperProxyFactory == null) {
              throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
            }
            try {
              return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception e) {
              throw new BindingException("Error getting mapper instance. Cause: " + e, e);
            }
          }

          在MyBatis中,所有的mapper對應的都是一個代理類,獲取到mapper對應的代理類以后執行newInstance方法,獲取到對應的實例,這樣就可以通過這個實例進行方法的調用

          public class MapperProxyFactory {
          
          
            private final Class mapperInterface;
            private final Map methodCache = new ConcurrentHashMap();
          
          
            public MapperProxyFactory(Class mapperInterface) {
              this.mapperInterface = mapperInterface;
            }
          
          
            public Class getMapperInterface() {
              return mapperInterface;
            }
          
          
            public Map getMethodCache() {
              return methodCache;
            }
          
          
            @SuppressWarnings("unchecked")
            protected T newInstance(MapperProxy mapperProxy) {
              return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
            }
          
          
            public T newInstance(SqlSession sqlSession) {
              final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
              return newInstance(mapperProxy);
            }
          
          
          }

          獲取mapper的流程為

          bd1adefc-484c-11ed-a3b6-dac502259ad0.png

          圖11 Mapper的獲取過程

          4.查詢過程

          獲取到mapper以后,就可以調用具體的方法

          //執行方法
          List students = mapper.queryStudents(condition);

          首先會調用org.apache.ibatis.binding.MapperProxy#invoke的方法,在這個方法中,會調用org.apache.ibatis.binding.MapperMethod#execute

          public Object execute(SqlSession sqlSession, Object[] args) {
            Object result;
            switch (command.getType()) {
              case INSERT: {
             Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.insert(command.getName(), param));
                break;
              }
              case UPDATE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.update(command.getName(), param));
                break;
              }
              case DELETE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.delete(command.getName(), param));
                break;
              }
              case SELECT:
                if (method.returnsVoid() && method.hasResultHandler()) {
                  executeWithResultHandler(sqlSession, args);
                  result = null;
                } else if (method.returnsMany()) {
                  result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) {
                  result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                  result = executeForCursor(sqlSession, args);
                } else {
                  Object param = method.convertArgsToSqlCommandParam(args);
                  result = sqlSession.selectOne(command.getName(), param);
                }
                break;
              case FLUSH:
                result = sqlSession.flushStatements();
                break;
              default:
                throw new BindingException("Unknown execution method for: " + command.getName());
            }
            if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
              throw new BindingException("Mapper method '" + command.getName() 
                  + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
            }
            return result;
          }

          首先根據SQL的類型增刪改查決定執行哪個方法,在此執行的是SELECT方法,在SELECT中根據方法的返回值類型決定執行哪個方法,可以看到在select中沒有selectone單獨方法,都是通過selectList方法,通過調用org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)方法來獲取到數據

          @Override
          public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
            try {
              MappedStatement ms = configuration.getMappedStatement(statement);
              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();
            }
          }

          在selectList中,首先從configuration對象中獲取MappedStatement,在statement中包含了Mapper的相關信息,然后調用org.apache.ibatis.executor.CachingExecutor#query()方法

          bd79d010-484c-11ed-a3b6-dac502259ad0.png

          圖12 query()方法調試圖示

          在這個方法中,首先對SQL進行解析根據入參和原始SQL,對SQL進行拼接

          bdc8acf8-484c-11ed-a3b6-dac502259ad0.png

          圖13 SQL拼接過程代碼圖示

          調用MapperedStatement里的getBoundSql最終解析出來的SQL為

          bde17670-484c-11ed-a3b6-dac502259ad0.png

          圖14 SQL拼接過程結果圖示

          接下來調用org.apache.ibatis.parsing.GenericTokenParser#parse對解析出來的SQL進行解析

          be180d5c-484c-11ed-a3b6-dac502259ad0.png

          圖15 SQL解析過程圖示

          最終解析的結果為

          be404ad8-484c-11ed-a3b6-dac502259ad0.png

          圖16 SQL解析結果圖示

          最后會調用SimpleExecutor中的doQuery方法,在這個方法中,會獲取StatementHandler,然后調用org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize這個方法進行參數和SQL的處理,最后調用statement的execute方法獲取到結果集,然后 利用resultHandler對結進行處理

          bef01c9c-484c-11ed-a3b6-dac502259ad0.png

          圖17 SQL處理結果圖示

          查詢的主要流程為

          bf1a73a2-484c-11ed-a3b6-dac502259ad0.png

          bf2f3a6c-484c-11ed-a3b6-dac502259ad0.png

          圖18 查詢流程處理圖示

          5.查詢流程總結

          總結整個查詢流程如下

          bf749d46-484c-11ed-a3b6-dac502259ad0.png

          圖19 查詢流程抽象

          2.3場景問題原因及解決方案

          2.3.1 個人排查

          這個問bug出現的地方在于綁定SQL參數的時候再源碼中位置為

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

          由于所寫的SQL是一個動態綁定參數的SQL,因此最終會走到org.apache.ibatis.scripting.xmltags.DynamicSqlSource#getBoundSql這個方法中去

          public BoundSql getBoundSql(Object parameterObject) {
            BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
            List parameterMappings = boundSql.getParameterMappings();
            if (parameterMappings == null || parameterMappings.isEmpty()) {
              boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
            }
          
          
            // check for nested result maps in parameter mappings (issue #30)
            for (ParameterMapping pm : boundSql.getParameterMappings()) {
              String rmId = pm.getResultMapId();
              if (rmId != null) {
                ResultMap rm = configuration.getResultMap(rmId);
                if (rm != null) {
                  hasNestedResultMaps |= rm.hasNestedResultMaps();
                }
              }
            }
          
          
            return boundSql;
          }

          在這個方法中,會調用 rootSqlNode.apply(context)方法,由于這個標簽是一個foreach標簽,因此這個apply方法會調用到org.apache.ibatis.scripting.xmltags.ForEachSqlNode#apply這個方法中去

          @Override
          public boolean apply(DynamicContext context) {
            Map bindings = context.getBindings();
            final Iterable  iterable = evaluator.evaluateIterable(collectionExpression, bindings);
            if (!iterable.iterator().hasNext()) {
              return true;
            }
            boolean first = true;
            applyOpen(context);
            int i = 0;
            for (Object o : iterable) {
              DynamicContext oldContext = context;
              if (first) {
                context = new PrefixedContext(context, "");
              } else if (separator != null) {
                context = new PrefixedContext(context, separator);
              } else {
                  context = new PrefixedContext(context, "");
              }
              int uniqueNumber = context.getUniqueNumber();
              // Issue #709 
              if (o instanceof Map.Entry) {
                @SuppressWarnings("unchecked") 
                Map.Entry mapEntry = (Map.Entry) o;
                applyIndex(context, mapEntry.getKey(), uniqueNumber);
                applyItem(context, mapEntry.getValue(), uniqueNumber);
              } else {
                applyIndex(context, i, uniqueNumber);
                applyItem(context, o, uniqueNumber);
              }
              contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));
              if (first) {
                first = !((PrefixedContext) context).isPrefixApplied();
              }
              context = oldContext;
              i++;
            }
            applyClose(context);
            return true;
          }

          當調用appItm方法的時候將參數進行綁定,參數的變量問題都會存在bindings這個參數中區

          private void applyItem(DynamicContext context, Object o, int i) {
            if (item != null) {
              context.bind(item, o);
              context.bind(itemizeItem(item, i), o);
            }
          }

          進行綁定參數的時候,綁定完成foreach的方法的時候,可以看到bindings中不止綁定了foreach中的兩個參數還額外有一個參數名字studentName->lct2,也就是說最后一個參數也是會出現在bindings這個參數中的,

          private void applyItem(DynamicContext context, Object o, int i) {
            if (item != null) {
              context.bind(item, o);
              context.bind(itemizeItem(item, i), o);
            }
          }

          bf86da60-484c-11ed-a3b6-dac502259ad0.png

          圖20 參數綁定過程

          最后判定

          org.apache.ibatis.scripting.xmltags.IfSqlNode#apply

          @Override
          public boolean apply(DynamicContext context) {
            if (evaluator.evaluateBoolean(test, context.getBindings())) {
              contents.apply(context);
              return true;
            }
            return false;
          }
          
          

          可以看到在調用evaluateBoolean方法的時候會把context.getBindings()就是前邊提到的bindings參數傳入進去,因為現在這個參數中有一個studentName,因此在使用Ognl表達式的時候,判定為這個if標簽是有值的因此將這個標簽進行了解析

          bfb17dba-484c-11ed-a3b6-dac502259ad0.png

          圖21 單個參數綁定過程

          最終綁定的結果為

          c015c9be-484c-11ed-a3b6-dac502259ad0.png

          圖22 全部參數綁定過程

          因此這個地方綁定參數的地方是有問題的,至此找出了問題的所在。

          2.3.2 官方解釋

          翻閱MyBatis官方文檔進行求證,發現在3.4.5版本發行中bug fixes中有這樣一句

          c05977d6-484c-11ed-a3b6-dac502259ad0.png

          圖23 此問題官方修復github記錄

          修復了foreach版本中對于全局變量context的修改的bug

          issue地址為https://github.com/mybatis/mybatis-3/pull/966

          修復方案為https://github.com/mybatis/mybatis-3/pull/966/commits/84513f915a9dcb97fc1d602e0c06e11a1eef4d6a

          可以看到官方給出的修改方案,重新定義了一個對象,分別存儲全局變量和局部變量,這樣就會解決foreach會改變全局變量的問題。

          c07f0e10-484c-11ed-a3b6-dac502259ad0.png

          圖24 此問題官方修復代碼示例

          2.3.3 修復方案

          升級MyBatis版本至3.4.5以上

          如果保持版本不變的話,在foreach中定義的變量名不要和外部的一致

          03 源碼閱讀過程總結

          理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板后進行模板加載,加載階段會將產物轉換為視圖樹的結構,轉換完成后將通過表達式引擎解析表達式并取得正確的值,通過事件解析引擎解析用戶自定義事件并完成事件的綁定,完成解析賦值以及事件綁定后進行視圖的渲染,最終將目標頁面展示到屏幕。

          MyBatis源代碼的目錄是比較清晰的,基本上每個相同功能的模塊都在一起,但是如果直接去閱讀源碼的話,可能還是有一定的難度,沒法理解它的運行過程,本次通過一個簡單的查詢流程從頭到尾跟下來,可以看到MyBatis的設計以及處理流程,例如其中用到的設計模式:

          c0bbc3fa-484c-11ed-a3b6-dac502259ad0.png

          圖25 MyBatis代碼結構圖

          組合模式:如ChooseSqlNode,IfSqlNode等

          模板方法模式:例如BaseExecutor和SimpleExecutor,還有BaseTypeHandler和所有的子類例如IntegerTypeHandler

          Builder模式:例如 SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder

          工廠模式:例如SqlSessionFactory、ObjectFactory、MapperProxyFactory

          代理模式:MyBatis實現的核心,比如MapperProxy、ConnectionLogger

          審核編輯:湯梓紅

          • 源碼
            +關注

            關注

            3

            文章

            308

            瀏覽量

            25974
          • mybatis
            +關注

            關注

            0

            文章

            28

            瀏覽量

            6011
          收藏 人收藏

            評論

            相關推薦

            一文了解電動汽車的中樞神經

            電子發燒友網站提供《一文了解電動汽車的中樞神經.pdf》資料免費下載
            發表于 10-14 11:16 ? 次 閱讀
            <b>一文</b><b>了解</b>電動汽車的中樞神經

            詳細解讀MyBatis查詢流程

            如下圖所示,在示例Mapper中,下面提供了個方法queryStudents,從student表中查詢出符合查詢條件的數據,入參可以為student_name或者student_name的集合,示例中參數只傳入的是studentName的List集合
            的頭像 發表于 10-10 11:43 ? 次 閱讀

            Fluent Mybatis、原生MybatisMybatis Plus對比

            使用fluent mybatis可以不用寫具體的xml文件,通過java api可以構造出比較復雜的業務sql語句,做到代碼邏輯和sql邏輯的合。不再需要在Dao中組裝查詢或更新操作,在xml或
            的頭像 發表于 09-15 15:41 ? 次 閱讀

            一文讀懂汽輪機轉子軸油封磨損修復的方法

            一文讀懂,汽輪機轉子軸油封磨損修復的方法
            發表于 06-24 15:42 ? 次 閱讀

            easy-mybatis Mybatis的增強框架

            ./oschina_soft/gitee-easy-mybatis.zip
            發表于 06-14 09:45 ? 次 閱讀
            easy-<b>mybatis</b> <b>Mybatis</b>的增強框架

            Mybatis-Plus Mybatis增強工具包

            ./oschina_soft/gitee-mybatis-plus.zip
            發表于 06-13 11:34 ? 次 閱讀
            <b>Mybatis</b>-Plus <b>Mybatis</b>增強工具包

            什么是FOC?一文帶你了解

            轉子的位置、電機轉速、電流大小等信息作為反饋輸入,需要采集電機相電流,對其進行系列的數學變換和估算算法后得到解耦的易用控制的反饋量??刂破鞲鶕答伭颗c目標值的誤差進行動態調節,最終輸出 3 相正弦波
            發表于 06-10 11:36 ? 次 閱讀

            CAD軟件中如何統一文字字高?

            文字修改之統一文字字高的相關操作技巧吧!CAD文字修改:統字高浩辰CAD給排水軟件中統字高命令將涉及 CAD 文字,浩辰文字的文字字高按給定尺寸進行統。具體操作步驟如下:首先打開浩辰CAD給排水
            發表于 05-10 14:20 ? 次 閱讀

            一文看動大華股份2021年環境、社會及管治(ESG)報告

            小編帶你一文看動大華股份2021年環境、社會及管治(ESG)報告:
            的頭像 發表于 04-25 18:14 ? 次 閱讀
            <b>一文</b>看動大華股份2021年環境、社會及管治(ESG)報告

            一文詳解示波器的觸發功能(下)

            在上期文章《RIGOL技術站 | 示波器的觸發功能》一文中,小編介紹了示波器觸發的原理、概念、重要性等知識,對示波器的觸發功能有了初步的了解和認識。本篇文章小編將繼續為大家介紹模擬觸發、數字觸發及數字觸發的幾種不同類型,充分了解示波器的觸發功能,以便在測量的過程中更好地幫助工程師!
            的頭像 發表于 02-08 13:30 ? 次 閱讀
            <b>一文</b>詳解示波器的觸發功能(下)

            一文讀懂傳感器的原理與結構

            一文讀懂傳感器傳感器在原理與結構上千差萬別,如何根據具體的測量目的、測量對象以及測量環境合理地選用傳感器,是在進行某個量的測量時首先要解決的問題。當傳感器確定之后,與之相配套的測量方法和測量設備也就
            發表于 01-13 07:08 ? 次 閱讀

            一文了解錘式破碎機軸承位維修的過程

            一文了解錘式破碎機軸承位維修的過程
            發表于 01-10 14:46 ? 次 閱讀

            一文搞懂開關電源波紋的產生

            參考一文搞懂開關電源波紋的產生、測量及抑制開關電源紋波的產生上圖是開關電源中最簡單的拓撲結構-buck降壓型電源。隨著SWITCH的開關,電感L中的電流也是在輸出電流的有效值上下波動的。所以在輸出端
            發表于 12-30 08:31 ? 次 閱讀

            一文幫你梳理Cortex與ARMv8等基礎概念

            到底什么是Cortex、ARMv8、arm架構、ARM指令集、soc?一文幫你梳理基礎概念【科普】1. 從0開始學ARM-安裝Keil MDK uVision集成開發環境
            發表于 12-14 08:20 ? 次 閱讀

            一文讀懂中斷方式和輪詢操作有什么區別嗎

            一文讀懂中斷方式和輪詢操作有什么區別嗎?
            發表于 12-10 06:00 ? 次 閱讀

            一文了解LVGL的學習路線

            “本文大部分內容來自LVGL官方文檔,手翻版,如有錯誤歡迎指正?!毕盗形恼履夸?b>一、LVGL系列(一文了解LVGL的學習路線輕松了解LVGL的全部二、LVGL系列(二)之 LVGL必讀介紹
            發表于 12-07 12:55 ? 次 閱讀

            一文讀懂MCU的特點、功能及如何編寫

            一文讀懂MCU的特點、功能及如何編寫
            發表于 12-05 09:51 ? 次 閱讀
            <b>一文</b>讀懂MCU的特點、功能及如何編寫

            一文弄懂LDO

            一文弄懂LDO前言、LDO是什么?二、 LDO工作原理三、LDO特點1.外圍電路簡單,價格便宜2.效率較低3.噪聲低、紋波小四、LDO選型需考慮的重要參數1.壓降(Dropout Voltage
            發表于 12-03 09:06 ? 次 閱讀
            <b>一文</b>弄懂LDO

            7大主流單片機優缺點分析,一文打盡趕緊收藏!

            7大主流單片機優缺點分析,一文打盡趕緊收藏!
            發表于 11-15 13:21 ? 次 閱讀
            7大主流單片機優缺點分析,<b>一文</b>打盡趕緊收藏!

            一文讀懂什么是NEC協議

            一文讀懂什么是NEC協議?
            發表于 10-15 09:22 ? 次 閱讀

            一文了解航空線束知識及測試方法

            嗎?今天Aigtek安泰電子小編就帶你一文懂所有! 航空線束插頭有公母? 航天線束的插頭是有公母之分的,具體分辨方法如下: 1、插頭的方(凸出來的接頭)為公頭,插孔的方(凹進去的接頭)為母頭。 2、從外觀形狀來區分,頭
            的頭像 發表于 09-27 15:45 ? 次 閱讀

            一文搞懂UPS主要內容

            導讀:UPS是系統集成項目中常用到的設備,也是機房必備的設備。本文簡單介紹了UPS的種類、功能、原理,品質選擇與配置選擇方式,基礎維護等相關的內容。一文搞懂UPS本文主要內容:UPS種類、功能
            發表于 09-15 07:49 ? 次 閱讀

            一文了解BLDC與PMSM的區別

            參考文件:一文了解BLDC與PMSM的區別? ?????BLDC和PMSM電機區別???? ? STM32 FOC BLDC與PMSM的區別PS:總結語句用紅色標出,看紅色字體即可?,F代電機與控制
            發表于 08-30 08:38 ? 次 閱讀

            MyBatis流式查詢輕松幫你解決分頁慢的問題

            作者丨捏造的信仰 segmentfault.com/a/1190000022478915 Part1基本概念 流式查詢指的是查詢成功后不是返回個集合而是返回個迭代器,應用每次從迭代器取查詢
            的頭像 發表于 08-04 15:52 ? 次 閱讀

            一文講解單片機、ARM、MCU、嵌入式錯綜復雜的關系

            一文講解單片機、ARM、MCU、DSP、FPGA、嵌入式錯綜復雜的關系首先,“嵌入式”這是個概念,準確的定義沒有,各個書上都有各自的定義。但是主要思想是樣的,就是相比較PC機這種通用系統來說
            發表于 07-13 09:23 ? 次 閱讀

            一文帶你了解步進電機的相關知識

            一文帶你了解步進電機的相關知識:相、線、極性和步進方式2017-09-07 16:45這里不說步進電機的 “細分” 實驗,只說下有關步進電機的基礎概念以及步進電機的三種工作方式——單拍、雙拍、單雙
            發表于 07-08 06:48 ? 次 閱讀

            一文讀懂DS18B20溫度傳感器及編程

            一文讀懂DS18B20溫度傳感器及編程對于新手而言,DS18B20基本概念僅做了解,最重要的是利用單片機對DS18B20進行編程,讀取溫度信息,并把讀取到的溫度信息利用數碼管,LCD1602或者上位
            發表于 07-06 07:10 ? 次 閱讀

            一文讀懂如何去優化AC耦合電容?

            一文讀懂如何去優化AC耦合電容?
            發表于 06-08 07:04 ? 次 閱讀

            一文解讀HEVC視頻標準的環內濾波,看完你就懂了

            一文解讀HEVC視頻標準的環內濾波,看完你就懂了
            發表于 06-03 06:08 ? 次 閱讀

            篇讓你熟練掌握 MyBatis-Plus!

            MyBatis-plus 是Mybatis 增強工具,用于簡化開發,提高效率。下文使用縮寫 mp來簡化表示 MyBatis-plus,本文主要介紹 mp 搭配 Spring Boot
            的頭像 發表于 06-01 09:30 ? 次 閱讀
            <b>一</b>篇讓你熟練掌握 <b>MyBatis</b>-Plus!

            一文讀懂接口模塊的組合應用有哪些?

            一文讀懂接口模塊的組合應用有哪些?
            發表于 05-17 07:15 ? 次 閱讀

            一文了解SiP封裝資料下載

            電子發燒友網為你提供一文了解SiP封裝資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-25 08:52 ? 次 閱讀
            <b>一文</b><b>了解</b>SiP封裝資料下載

            一文了解I/Q 信號資料下載

            電子發燒友網為你提供一文了解I/Q 信號資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-25 08:44 ? 次 閱讀
            <b>一文</b><b>了解</b>I/Q 信號資料下載

            一文了解4-20 mA電流環路發送器資料下載

            電子發燒友網為你提供一文了解4-20 mA電流環路發送器資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-23 08:54 ? 次 閱讀
            <b>一文</b><b>了解</b>4-20 mA電流環路發送器資料下載

            一文了解藍牙配對的三個階段資料下載

            電子發燒友網為你提供一文了解藍牙配對的三個階段資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-23 08:47 ? 次 閱讀
            <b>一文</b><b>了解</b>藍牙配對的三個階段資料下載

            一文了解IGBT基礎知識資料下載

            電子發燒友網為你提供一文了解IGBT基礎知識資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-22 08:52 ? 次 閱讀
            <b>一文</b><b>了解</b>IGBT基礎知識資料下載

            一文總結整流濾波電路資料下載

            電子發燒友網為你提供一文總結整流濾波電路資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-22 08:52 ? 次 閱讀
            <b>一文</b>總結整流濾波電路資料下載

            一文了解人工智能的概念元素資料下載

            電子發燒友網為你提供一文了解人工智能的概念元素資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-18 08:47 ? 次 閱讀
            <b>一文</b><b>了解</b>人工智能的概念元素資料下載

            一文了解車載充電機資料下載

            電子發燒友網為你提供一文了解車載充電機資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-17 08:43 ? 次 閱讀
            <b>一文</b><b>了解</b>車載充電機資料下載

            一文了解無線傳感器網絡(WSN)結構特點資料下載

            電子發燒友網為你提供一文了解無線傳感器網絡(WSN)結構特點資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-14 08:51 ? 次 閱讀
            <b>一文</b><b>了解</b>無線傳感器網絡(WSN)結構特點資料下載

            什么是晶振工作原理 + 晶振ppm?一文了解晶振這四個方面資料下載

            電子發燒友網為你提供什么是晶振工作原理 + 晶振ppm?一文了解晶振這四個方面資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-12 08:49 ? 次 閱讀
            什么是晶振工作原理 + 晶振ppm?<b>一文</b><b>了解</b>晶振這四個方面資料下載

            一文了解 IC 產業專業名詞和產業鏈關系資料下載

            電子發燒友網為你提供一文了解 IC 產業專業名詞和產業鏈關系資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-12 08:40 ? 次 閱讀
            <b>一文</b><b>了解</b> IC 產業專業名詞和產業鏈關系資料下載

            一文詳解開漏電路資料下載

            電子發燒友網為你提供一文詳解開漏電路資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-10 08:53 ? 次 閱讀
            <b>一文</b>詳解開漏電路資料下載

            一文了解ARM、FPGA 、嵌入式的特點資料下載

            電子發燒友網為你提供一文了解ARM、FPGA 、嵌入式的特點資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-08 08:50 ? 次 閱讀
            <b>一文</b><b>了解</b>ARM、FPGA 、嵌入式的特點資料下載

            一文了解欠壓保護資料下載

            電子發燒友網為你提供一文了解欠壓保護資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-04 08:42 ? 次 閱讀
            <b>一文</b><b>了解</b>欠壓保護資料下載

            一文了解加性高斯白噪聲(AWGN)資料下載

            電子發燒友網為你提供一文了解加性高斯白噪聲(AWGN)資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-03 08:55 ? 次 閱讀
            <b>一文</b><b>了解</b>加性高斯白噪聲(AWGN)資料下載

            一文了解多頻帶或多模式信號處理資料下載

            電子發燒友網為你提供一文了解多頻帶或多模式信號處理資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
            發表于 04-03 08:40 ? 次 閱讀
            <b>一文</b><b>了解</b>多頻帶或多模式信號處理資料下載

            一文讀懂ARM微處理器指令系統

            一文叫你如何讀懂ARM微處理器指令系統。
            發表于 03-26 14:30 ? 次 閱讀

            紅外熱像儀的測溫是什么原理?有什么注意事項?

            一文了解紅外熱像儀的測溫原理及其注意事項
            發表于 03-18 08:12 ? 次 閱讀

            什么是靜電屏蔽?

            一文帶你了解靜電屏蔽
            發表于 03-16 11:37 ? 次 閱讀

            什么是跟隨器電路運放輸出軌至軌?

            一文了解跟隨器電路運放輸出軌至軌
            發表于 03-16 08:27 ? 次 閱讀

            “它”能讓你的鍛件一文不值——氧化皮

            `力泰科技資訊:一文不值顧名思義就是沒有利用價值,不能滿足要求的意思。在很多制造單位,無論是食品、日用品、工具在生產過程中多多少少會出現廢品。今天我們就鍛造廠來分析下鍛件生產過程中廢品產生的原因
            發表于 03-09 09:05 ? 次 閱讀

            在使用MyBatis中SQL語句優化總結

            MyBatis 作為款優秀的持久層框架,它支持自定義SQL、存儲過程以及高級映射。它免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。還可以通過簡單的 XML 或注解來配置和映射原始
            的頭像 發表于 02-04 15:20 ? 次 閱讀

            一文詳解藍牙模塊原理與結構

            電子發燒友網站提供《一文詳解藍牙模塊原理與結構.pdf》資料免費下載
            發表于 11-26 16:40 ? 次 閱讀

            一文搞懂幾種常見的射頻電路類型及主要指標

            一文搞懂幾種常見的射頻電路類型及主要指標。
            發表于 07-27 10:26 ? 次 閱讀
            <b>一文</b>搞懂幾種常見的射頻電路類型及主要指標

            一文了解通信技術的常用名詞解釋

            一文了解通信技術的常用名詞解釋
            的頭像 發表于 06-19 17:55 ? 次 閱讀

            Mybatis是什么

            Mybatis
            發表于 06-04 15:33 ? 次 閱讀

            MyBatis學習筆記-HelloWorld

            (二)MyBatis學習筆記-HelloWorld
            發表于 06-02 14:51 ? 次 閱讀

            自然語言處理的功能合一文

            自然語言處理——82 功能合一文法(Function Unification Grammar, FUG)
            發表于 03-25 11:19 ? 次 閱讀

            一文了解MCM厚膜集成電路

            一文了解MCM厚膜集成電路 MCM多芯片組件。 將多塊半導體裸芯片組裝在塊布線基板上的種封裝。根據基板材料可分為MCM-L,MCM-C和MCM-D三大類。 多芯片模塊在這種技術中,IC模片不是
            發表于 03-10 11:49 ? 次 閱讀

            一文讀懂NB-IoT 的現狀、挑戰和前景

            一文讀懂 NB-IoT 的現狀、挑戰和前景
            的頭像 發表于 02-28 15:42 ? 次 閱讀

            MyBatis的整合

            SpringBoot-15-之整合MyBatis-注解篇+分頁
            發表于 10-28 08:09 ? 次 閱讀

            mybatis的最簡兩種模式

            【本人禿頂程序員】springboot專輯:如何優雅的使用mybatis
            發表于 10-23 09:01 ? 次 閱讀

            MyBatis Oracle解析Excel文件

            MyBatis Oracle批量插入數據
            發表于 09-06 09:10 ? 次 閱讀

            一文揭開U盤閃存的級別謎底

            趕緊看!一文為你揭曉U盤閃存的級別內幕……
            的頭像 發表于 08-27 10:20 ? 次 閱讀

            Mybatis的特點和核心配置文件

            Mybatis—01基礎&動態SQL
            發表于 08-07 14:20 ? 次 閱讀

            數據庫整合Mybatis框架

            微服務 SpringBoot 20(九):整合Mybatis
            發表于 07-16 11:03 ? 次 閱讀

            Mybatis的內部設計介紹

            Mybatis源碼分析-整體設計()
            發表于 06-06 09:43 ? 次 閱讀

            模擬對話ADC設計一文中,公式是如何推導出來的??

            Amplifier and RC Filter Design for a Precision SAR Analog-toDigital Converter精密模數轉換器的前端放大器和阻容濾波電路設計一文中,作者列出了大量的公式,請問這些公式是如何推導出來的?
            發表于 08-22 06:31 ? 次 閱讀

            一文匯總十家新興造車企業的資金來源

            一文匯總十家新興造車企業的資金來源
            的頭像 發表于 07-30 11:38 ? 次 閱讀

            一文了解ADAS與自動駕駛的現狀、算法和技術路線

            一文了解ADAS與自動駕駛的現狀、算法和技術路線
            的頭像 發表于 07-30 09:58 ? 次 閱讀

            一文詳解MCS-51單片機的中斷系統

            一文詳解MCS-51單片機的中斷系統,具體的跟隨小編來了解下。
            的頭像 發表于 07-28 11:26 ? 次 閱讀
            <b>一文</b>詳解MCS-51單片機的中斷系統

            一文了解西門子V20變頻器與S7-200之間的USS通訊

            一文了解西門子V20變頻器與S7-200之間的USS通訊,具體的跟隨小編一起來看一下。
            的頭像 發表于 07-23 11:53 ? 次 閱讀
            <b>一文</b><b>了解</b>西門子V20變頻器與S7-200之間的USS通訊

            一文了解PCB中常見14大錯誤

            一文了解PCB中常見14大錯誤,具體的跟隨小編一起來看一下。
            的頭像 發表于 07-22 11:29 ? 次 閱讀

            一文解析PLC的應用

            一文解析PLC的應用,具體的跟隨小編一起來了解下。
            的頭像 發表于 07-19 11:21 ? 次 閱讀
            <b>一文</b>解析PLC的應用

            重磅!一文匯總50家造車新勢力

            一文匯總50家造車新勢力
            的頭像 發表于 07-16 11:27 ? 次 閱讀

            一文了解高速差分ADC驅動器設計考慮

            一文了解高速差分ADC驅動器設計考慮
            發表于 04-08 14:07 ? 次 閱讀
            <b>一文</b><b>了解</b>高速差分ADC驅動器設計考慮

            mybatis對多配置

            本文詳細介紹了mybatis關聯配置(對多配置)。
            發表于 02-24 13:53 ? 次 閱讀

            mybatis動態sql詳解

            本文詳細介紹了mybatis執行動態sql語句的方法。
            發表于 02-24 11:37 ? 次 閱讀

            MyBatis的實現原理

            本文主要詳細介紹了MyBatis的實現原理。mybatis底層還是采用原生jdbc來對數據庫進行操作的,只是通過 SqlSessionFactory,SqlSession Executor
            的頭像 發表于 02-24 11:25 ? 次 閱讀
            <b>MyBatis</b>的實現原理

            mybatis快速入門

            本文詳細介紹了mybatis相關知識,以及mybatis快速入門步驟詳解。
            的頭像 發表于 02-24 09:41 ? 次 閱讀
            <b>mybatis</b>快速入門

            mybatis是什么_MyBatis的優缺點詳解_mybatis框架入門詳解

            Mybatis框架是別人開發的種半成品軟件,可以用來通過定制輔助快速開發是工具。MyBatis應用程序根據XML配置文件創建SqlSessionFactory,SqlSessionFactory在
            發表于 02-24 09:16 ? 次 閱讀

            mybatis使用經驗小結

            本文是對mybatis使用經驗小結。
            發表于 02-24 08:46 ? 次 閱讀
            <b>mybatis</b>使用經驗小結

            mybatis配置文件詳解

            MyBatis 本是apache的個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,并且改名為MyBatis
            的頭像 發表于 02-12 11:16 ? 次 閱讀
            <b>mybatis</b>配置文件詳解

            jdbc與mybatis的區別

            MyBatis款優秀的持久層框架,它支持定制化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。JDBC是種用于執行SQL語句的Java API,可以為多種關系數據庫提供統訪問.
            發表于 02-02 17:43 ? 次 閱讀
            jdbc與<b>mybatis</b>的區別

            一文讀懂無線充電產業鏈

            一文讀懂無線充電產業鏈,新用戶關注【電子發燒友網】微信公眾號,輸入“積分”,立送10積分!
            發表于 12-04 19:13 ? 次 閱讀

            Mybatis緩存之級緩存

            本文主要講mybatis級緩存,級緩存是SqlSession級別的緩存。mybatis提供查詢緩存,用于減輕數據壓力,提高數據庫性能。mybaits提供級緩存,和二級緩存。級緩存
            發表于 11-27 20:44 ? 次 閱讀
            <b>Mybatis</b>緩存之<b>一</b>級緩存

            【轉帖】一文讀懂電阻和電容的不同

            的主要參數。般情況下,對電阻器應考慮其標稱阻值、允許偏差和標稱功率;對電容器則需了解其標稱容量、允許偏差和耐壓。一文讀懂電阻和電容的不同電阻器和電容器的標稱值和允許偏差般都標在電阻體和電容體上,而在
            發表于 11-14 15:43 ? 次 閱讀

            一文讀懂電阻和電容的不同

            了解它們的主要參數。般情況下,對電阻器應考慮其標稱阻值、允許偏差和標稱功率;對電容器則需了解其標稱容量、允許偏差和耐壓。一文讀懂電阻和電容的不同  電阻器和電容器的標稱值和允許偏差般都標在電阻體
            發表于 11-14 10:25 ? 次 閱讀

            一文讀懂如何解決MySQL數據庫超時配置問題

            1. JDBC超時設置 2. 連接池超時設置 3. MyBatis查詢超時 4. 事務超時
            發表于 10-25 16:28 ? 次 閱讀
            <b>一文</b>讀懂如何解決MySQL數據庫超時配置問題

            C語言教程之產生唯一文

            C語言教程之產生唯一文件,很好的C語言資料,快來學習吧。
            發表于 04-25 16:09 ? 次 閱讀

            如何連續采集存貯,同一文件夾下保存不同音頻文件

            我現在能夠保存,但是不知道如何連續采集存貯,同一文件夾下保存不同音頻文件
            發表于 11-17 20:44 ? 次 閱讀

            今想發表一文章,然后說權限不行!哎!

            今想發表一文章,然后說權限不行!哎!
            發表于 06-29 20:26 ? 次 閱讀

            下載硬聲App