Exemple de code
L'article précédent disait que pour MyBatis, insérer, mettre à jour et supprimer sont un groupe, car pour MyBatis, il est dit qu'ils sont tous mis à jour ; select est un groupe, car pour MyBatis, c'est select.
Cet article étudie le processus d'implémentation de select L'exemple de code est :
1 public void testSelectOne() { 2 System.out.println(mailDao.selectMailById(8)); 3 }
L'implémentation de. la méthode selectMailById est :
1 public Mail selectMailById(long id) {2 SqlSession ss = ssf.openSession();3 try {4 return ss.selectOne(NAME_SPACE + "selectMailById", id);5 } finally {6 ss.close();7 }8 }
Nous savons que la sélection fournie par MyBatis a deux méthodes, selectList et selectOne, mais cet article n'analyse et n'a besoin que de pour analyser la méthode selectOne La raison sera expliquée plus tard.
flux de méthode selectOne
Premièrement, regardez le flux de méthode selectOne de SqlSession, le La méthode se trouve dans DefaultSqlSession :
1 public <T> T selectOne(String statement, Object parameter) { 2 // Popular vote was to return null on 0 results and throw exception on too many. 3 List<T> list = this.<T>selectList(statement, parameter); 4 if (list.size() == 1) { 5 return list.get(0); 6 } else if (list.size() > 1) { 7 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); 8 } else { 9 return null;10 }11 }
C'est pourquoi j'ai dit que les méthodes selectOne et selectList n'ont besoin que d'analyser la méthode selectList, car Dans MyBatis, toutes les opérations selectOne seront finalement converties en opérations selectList Il ne s'agit rien de plus que de juger du nombre d'ensembles de résultats une fois l'opération terminée. Si le nombre d'ensembles de résultats dépasse un, un. l'erreur sera signalée.
Regardez ensuite l'implémentation du code de selectList à la ligne 3. La méthode se trouve également dans DefaultSqlSession :
1 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 2 try { 3 MappedStatement ms = configuration.getMappedStatement(statement); 4 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 5 } catch (Exception e) { 6 throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); 7 } finally { 8 ErrorContext.instance().reset(); 9 }10 }
Je ne parlerai pas de l'acquisition de MappedStatement à la ligne 3. Suivons l'implémentation de la méthode de requête d'Executor à la ligne 4. Un mode décorateur est utilisé ici pour ajouter une fonction de mise en cache à SimpleExecutor. Le code se trouve dans CachingExecutor :
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {2 BoundSql boundSql = ms.getBoundSql(parameterObject);3 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);4 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);5 }
Le code de la ligne 2 obtient BoundSql Le contenu de BoundSql a été mentionné ci-dessus et sera résumé à la fin.
Le code de la ligne 3 construit la clé de cache en fonction des paramètres d'entrée.
Le code de la ligne 4 exécute l'opération de requête. Jetez un œil à l'implémentation du code. Le code se trouve également dans CachingExecutor :
1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 2 throws SQLException { 3 Cache cache = ms.getCache(); 4 if (cache != null) { 5 flushCacheIfRequired(ms); 6 if (ms.isUseCache() && resultHandler == null) { 7 ensureNoOutParams(ms, parameterObject, boundSql); 8 @SuppressWarnings("unchecked") 9 List<E> list = (List<E>) tcm.getObject(cache, key);10 if (list == null) {11 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);12 tcm.putObject(cache, key, list); // issue #578 and #11613 }14 return list;15 }16 }17 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);18 }.
Il n'y a pas de configuration ni de référence au Cache, donc le jugement sur la ligne 4 n'est pas exécuté et le code sur la ligne 17 est exécuté. Le code est situé dans BaseExecutor, la classe parent de SimpleExecutor. le code source est implémenté comme :
1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2 ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); 3 if (closed) { 4 throw new ExecutorException("Executor was closed."); 5 } 6 if (queryStack == 0 && ms.isFlushCacheRequired()) { 7 clearLocalCache(); 8 } 9 List<E> list;10 try {11 queryStack++;12 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;13 if (list != null) {14 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);15 } else {16 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);17 }18 } finally {19 queryStack--;20 }21 if (queryStack == 0) {22 for (DeferredLoad deferredLoad : deferredLoads) {23 deferredLoad.load();24 }25 // issue #60126 deferredLoads.clear();27 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {28 // issue #48229 clearLocalCache();30 }31 }32 return list;33 }
Le code de la ligne 16 est exécuté ici. La méthode queryFromDatabase est implémentée comme :
<.>1 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2 List<E> list; 3 localCache.putObject(key, EXECUTION_PLACEHOLDER); 4 try { 5 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); 6 } finally { 7 localCache.removeObject(key); 8 } 9 localCache.putObject(key, list);10 if (ms.getStatementType() == StatementType.CALLABLE) {11 localOutputParameterCache.putObject(key, parameter);12 }13 return list;14 }
1 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { 2 Statement stmt = null; 3 try { 4 Configuration configuration = ms.getConfiguration(); 5 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); 6 stmt = prepareStatement(handler, ms.getStatementLog()); 7 return handler.<E>query(stmt, resultHandler); 8 } finally { 9 closeStatement(stmt);10 }11 }
Voir. que les codes des lignes 4 à 6 sont les mêmes que ceux de la mise à jour précédente, je n'entrerai pas dans les détails. Les amis qui ont une impression de handler doivent se rappeler qu'il s'agit de PreparedStatementHandler. La partie suivante analysera la différence avec la mise à jour et comment le gérer. La méthode de requête de PreparedStatementHandler est implémentée.
L'implémentation de la méthode de requête de PreparedStatementHandler
Suivez la méthode de requête de PreparedStatementHandler jusqu'au end. L'implémentation finale est :
1 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {2 PreparedStatement ps = (PreparedStatement) statement;3 ps.execute();4 return resultSetHandler.<E> handleResultSets(ps);5 }
Voyez que la ligne 3 effectue l'opération de requête et que le code de la ligne 4 traite l'ensemble de résultats et convertit le résultat défini dans un List , la méthode handleResultSets est implémentée comme :
1 public List<Object> handleResultSets(Statement stmt) throws SQLException { 2 ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); 3 4 final List<Object> multipleResults = new ArrayList<Object>(); 5 6 int resultSetCount = 0; 7 ResultSetWrapper rsw = getFirstResultSet(stmt); 8 9 List<ResultMap> resultMaps = mappedStatement.getResultMaps();10 int resultMapCount = resultMaps.size();11 validateResultMapsCount(rsw, resultMapCount);12 while (rsw != null && resultMapCount > resultSetCount) {13 ResultMap resultMap = resultMaps.get(resultSetCount);14 handleResultSet(rsw, resultMap, multipleResults, null);15 rsw = getNextResultSet(stmt);16 cleanUpAfterHandlingResultSet();17 resultSetCount++;18 }19 20 String[] resultSets = mappedStatement.getResultSets();21 if (resultSets != null) {22 while (rsw != null && resultSetCount < resultSets.length) {23 ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);24 if (parentMapping != null) {25 String nestedResultMapId = parentMapping.getNestedResultMapId();26 ResultMap resultMap = configuration.getResultMap(nestedResultMapId);27 handleResultSet(rsw, resultMap, null, parentMapping);28 }29 rsw = getNextResultSet(stmt);30 cleanUpAfterHandlingResultSet();31 resultSetCount++;32 }33 }34 35 return collapseSingleResultList(multipleResults);36 }
Résumez cette méthode.
La ligne 7 du code, obtient le ResultSet via la méthode getResultSet de PreparedStatement et enveloppe le ResultSet dans un ResultSetWrapper En plus de contenir le ResultSet, le ResultSetWrapper définit également chaque élément de. données renvoyées par la base de données. Le nom de chaque ligne et colonne, le type JDBC correspondant à la colonne et le type de classe Java correspondant à la colonne. De plus, le plus important est qu'elle contienne également TypeHandlerRegister (type processeur, tous les paramètres. sont définis via TypeHandler).
La 9ème ligne de code obtient le ResultMap défini dans la balise