Heim  >  Artikel  >  Java  >  Wählen Sie eine Analyse und Zusammenfassung des Quellcodes aus

Wählen Sie eine Analyse und Zusammenfassung des Quellcodes aus

巴扎黑
巴扎黑Original
2017-06-26 09:32:121502Durchsuche

Beispielcode

Im vorherigen Artikel hieß es, dass für MyBatis Einfügen, Aktualisieren und Löschen eine Gruppe sind, denn für MyBatis heißt es dass sie alle aktualisiert sind; select ist eine Gruppe, da es sich bei MyBatis um select handelt.

Dieser Artikel untersucht den Implementierungsprozess von select. Der Beispielcode lautet:

 1 public void testSelectOne() { 2     System.out.println(mailDao.selectMailById(8)); 3 }

Die Implementierung von Die Methode selectMailById lautet:

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 }

Wir wissen, dass die von MyBatis bereitgestellte Auswahl zwei Methoden hat, selectList und selectOne, aber dieser Artikel analysiert nur und benötigt nur Um die SelectOne-Methode zu analysieren, wird der Grund erläutert.

selectOne-Methodenfluss

Schauen Sie sich zunächst den selectOne-Methodenfluss von SqlSession an Die Methode befindet sich in 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 }

Aus diesem Grund habe ich gesagt, dass die Methoden selectOne und selectList nur die Methode selectList analysieren müssen, weil In MyBatis werden alle selectOne-Operationen schließlich in selectList-Operationen umgewandelt. Es handelt sich lediglich um die Beurteilung der Anzahl der Ergebnismengen nach Abschluss der Operation Fehler wird gemeldet.

Dann schauen Sie sich die Code-Implementierung von selectList in Zeile 3 an. Die Methode befindet sich auch in 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 }

Ich werde nicht über die Erfassung von MappedStatement in Zeile 3 sprechen. Folgen wir der Implementierung der Abfragemethode von Executor in Zeile 4. Hier wird ein Dekoratormodus verwendet, um eine Caching-Funktion zu SimpleExecutor hinzuzufügen. Der Code befindet sich in 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 }

Der Code in Zeile 2 erhält BoundSql. Der Inhalt in BoundSql wurde oben erwähnt und wird am Ende zusammengefasst.

Der Code in Zeile 3 erstellt den Cache-Schlüssel basierend auf den Eingabeparametern.

Der Code in Zeile 4 führt die Abfrageoperation aus. Der Code befindet sich auch in 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 }

Es gibt keine Konfiguration und keinen Verweis auf Cache, daher wird das Urteil in Zeile 4 nicht ausgeführt und der Code in Zeile 17 befindet sich in BaseExecutor, der übergeordneten Klasse von SimpleExecutor Der Quellcode ist implementiert als:

 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 }

Der Code in Zeile 16 wird hier ausgeführt. Die queryFromDatabase-Methode wird implementiert als:

 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 }

Der Code erreicht Zeile 5 und führt schließlich die duQuery-Methode aus. Die Implementierung der Methode ist:

 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 }

Sehen Da die Codes in den Zeilen 4 bis 6 mit denen des vorherigen Updates identisch sind, werde ich nicht auf Details eingehen. Freunde, die einen Eindruck vom Handler haben, sollten sich daran erinnern, dass es sich um PreparedStatementHandler handelt Die Abfragemethode von PreparedStatementHandler ist implementiert.

Abfragemethodenimplementierung von PreparedStatementHandler

Folgen Sie der Abfragemethode von PreparedStatementHandler bis zum Ende . Die endgültige Implementierung lautet:

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 }

Beachten Sie, dass Zeile 3 die Abfrageoperation ausführt und der Code in Zeile 4 die Ergebnismenge verarbeitet und das Ergebnis konvertiert In eine Liste gesetzt, wird die Methode handleResultSets wie folgt implementiert:

 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 }

Fassen Sie diese Methode zusammen.

Zeile 7 des Codes ruft das ResultSet über die getResultSet-Methode von PreparedStatement ab und verpackt das ResultSet in einen ResultSetWrapper. Der ResultSetWrapper enthält nicht nur das ResultSet, sondern definiert auch jeden Teil davon Von der Datenbank zurückgegebene Daten, der JDBC-Typ, der der Spalte entspricht, und der Typ der Java-Klasse, der der Spalte entspricht. Das Wichtigste ist außerdem, dass sie auch TypeHandlerRegister (Typprozessor, alle Parameter) enthält werden über TypeHandler festgelegt).

Die 9. Codezeile erhält die Definition im definierten resultSets zu verarbeiten. Da er hier nicht definiert ist, wird er übersprungen.

Der Code in Zeile 35 multipliziert die Ergebnisse entsprechend seiner Größe. Wenn size=1, holt sich das 0. Element und erzwingt, dass es auf List; zurückgegeben wird direkt.

Im Allgemeinen ist der Prozess der Einkapselung dieser Methode in eine benutzerdefinierte ResultMap, basierend auf den von der Datenbank zurückgegebenen Ergebnissen, grundsätzlich kein Problem. Das einzige Problem besteht darin, warum dies erforderlich ist Um ein multipleResults zu definieren und schließlich die Endergebnisse entsprechend der Größe von multipleResults zu beurteilen und aufzuteilen, muss ich diesen Teil in der zukünftigen Arbeit noch erlernen, wenn die MyBatis-Anwendung vertieft wird.

Zusammenfassung

Das Laden der MyBatis-Konfigurationsdatei und CRUD-Vorgänge wurden im durchgeführt Vorheriger Artikel Analyse, aus meiner eigenen Sicht habe ich ein grundlegendes Verständnis des gesamten Prozesses, bin aber in vielen Teilen immer noch etwas unbeeindruckt. Das Wichtigste ist, wo man welche Daten erhält und wo man die erhaltenen Daten verwendet Hier ist eine Zusammenfassung und Vertiefung des Eindrucks. Die Hauptzusammenfassung ist, welche Inhalte in den Schlüsselklassen in MyBatis enthalten sind.

Die erste ist SqlSessionFactory. Wir verwenden sie, um jedes Mal eine SqlSessionFactory zu öffnen >

Dann kommt die Konfiguration, der letzte Ort, an dem alle Konfigurationsinformationen gespeichert werden, insbesondere boolesche Werte, die über das Tag konfiguriert werden können Vorgänge (z. B. das Öffnen einer SQL-Sitzung, das Durchführen von Hinzufügungen, Löschungen, Änderungen usw.) müssen relevante Informationen von der Konfiguration erhalten. Einige wichtige Attribute sind:

Dann gibt es Environment, das die konfigurierten Datenbankumgebungsinformationen speichert. Sie können mehrere angeben, aber am Ende kann nur eines verwendet werden. Einige wichtige Attribute, die von Environment gehalten werden, sind:

Dann kommt das MappedStatement, das einem , , , <. entspricht ;select> in der Mapper-Datei ;, jedes Mal, wenn eine MyBatis-Operation ausgeführt wird, wird zuerst das entsprechende MappedStatement abgerufen. Einige wichtige Attribute sind:

gefolgt von BoundSql, BoundSql Das Wichtigste darin ist die aktuell auszuführende SQL-Anweisung, der Rest enthält Parameterinformationen und festzulegende Parameterobjekte. Die von BoundSql gehaltenen Attribute sind:

最后是ParameterMapping,ParameterMapping是待设置的参数映射,存储了待设置的参数的相关信息,ParameterMapping持有的属性有:

 

MyBatis中使用到的设计模式

下面来总结一下MyBatis中使用到的设计模式,有些设计模式可能在到目前位置的文章中没有体现,但是在之后的【MyBatis源码分析】系列文章中也会体现,这里一并先列举出来:

1、建造者模式

代码示例为SqlSessionFactoryBuilder,代码片段:

 1 public SqlSessionFactory build(Reader reader) { 2     return build(reader, null, null); 3   } 4  5   public SqlSessionFactory build(Reader reader, String environment) { 6     return build(reader, environment, null); 7   } 8  9   public SqlSessionFactory build(Reader reader, Properties properties) {10     return build(reader, null, properties);11   }12 13   public SqlSessionFactory build(Reader reader, String environment, Properties properties) {14     try {15       XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);16       return build(parser.parse());17     } catch (Exception e) {18       throw ExceptionFactory.wrapException("Error building SqlSession.", e);19     } finally {20       ErrorContext.instance().reset();21       try {22         reader.close();23       } catch (IOException e) {24         // Intentionally ignore. Prefer previous error.25       }26     }27   }

重载了大量的build方法,可以根据参数的不同构建出不同的SqlSessionFactory。

2、抽象工厂模式

代码示例为TransactionFactory,代码片段为:

 1 public class JdbcTransactionFactory implements TransactionFactory { 2  3   @Override 4   public void setProperties(Properties props) { 5   } 6  7   @Override 8   public Transaction newTransaction(Connection conn) { 9     return new JdbcTransaction(conn);10   }11 12   @Override13   public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {14     return new JdbcTransaction(ds, level, autoCommit);15   }16 }

抽象出事物工厂,不同的事物类型实现不同的事物工厂,像这里就是Jdbc事物工厂,通过Jdbc事物工厂去返回事物接口的具体实现。

其它的像DataSourceFactory也是抽象工厂模式的实现。

3、模板模式

代码示例为BaseExecutor,代码片段:

 1 protected abstract int doUpdate(MappedStatement ms, Object parameter) 2       throws SQLException; 3  4 protected abstract List<BatchResult> doFlushStatements(boolean isRollback) 5       throws SQLException; 6  7 protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 8       throws SQLException; 9 10 protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)11       throws SQLException;

BaseExecutor封装好方法流程,子类例如SimpleExecutor去实现。

4、责任链模式

代码示例为InterceptorChain,代码片段为:

 1 public class InterceptorChain { 2  3   private final List<Interceptor> interceptors = new ArrayList<Interceptor>(); 4  5   public Object pluginAll(Object target) { 6     for (Interceptor interceptor : interceptors) { 7       target = interceptor.plugin(target); 8     } 9     return target;10   }11 12   public void addInterceptor(Interceptor interceptor) {13     interceptors.add(interceptor);14   }15   16   public List<Interceptor> getInterceptors() {17     return Collections.unmodifiableList(interceptors);18   }19 20 }

可以根据需要添加自己的Interceptor,最终按照定义的Interceptor的顺序逐一嵌套执行。

5、装饰器模式

代码示例为CachingExecutor,代码片段为:

 1 public class CachingExecutor implements Executor { 2  3   private Executor delegate; 4   private TransactionalCacheManager tcm = new TransactionalCacheManager(); 5  6   public CachingExecutor(Executor delegate) { 7     this.delegate = delegate; 8     delegate.setExecutorWrapper(this); 9   }10 11   ...12 }

给Executor添加上了缓存的功能,update与query的时候会根据用户配置先尝试操作缓存。

在MyBatis中还有很多地方使用到了装饰器模式,例如StatementHandler、Cache。

6、代理模式

代码示例为PooledConnection,代码片段为:

 1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2     String methodName = method.getName(); 3     if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { 4       dataSource.pushConnection(this); 5       return null; 6     } else { 7       try { 8         if (!Object.class.equals(method.getDeclaringClass())) { 9           // issue #579 toString() should never fail10           // throw an SQLException instead of a Runtime11           checkConnection();12         }13         return method.invoke(realConnection, args);14       } catch (Throwable t) {15         throw ExceptionUtil.unwrapThrowable(t);16       }17     }18 }

这层代理的作用主要是为了让Connection使用完毕之后从栈中弹出来。

MyBatis中的插件也是使用代理模式实现的,这个在后面会说到。

Das obige ist der detaillierte Inhalt vonWählen Sie eine Analyse und Zusammenfassung des Quellcodes aus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn