サンプルコード
前の記事では、MyBatis の場合、insert、update、および delete はグループであり、MyBatis の場合はすべて更新であるため、select はグループであり、MyBatis の場合は選択であると述べました。
この記事では、select の実装プロセスについて説明します。サンプル コードは次のとおりです。
1 public void testSelectOne() { 2 System.out.println(mailDao.selectMailById(8)); 3 }
selectMailById メソッドの実装は、次のとおりです。メソッド: selectList と selectOne ただし、この記事では分析のみを行うため、selectOne メソッドのみを分析する必要があります。その理由は後で説明します。
selectOne メソッド フロー
まず、SqlSession の selectOne メソッド フローを見てください。このメソッドは DefaultSqlSession にあります。 2 つの選択リストselectList メソッドだけで十分である理由は、MyBatis のすべての selectOne 操作が最終的には selectList 操作 に変換され、操作が完了した後に結果セットの数を判断するだけだからです。結果セットの数が 1 を超える場合、エラーが報告されます。
次に、3 行目の selectList のコード実装を見てみましょう。このメソッドは DefaultSqlSession にもあります:
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 }
3 行目の MappedStatement の取得については説明しません。クエリ メソッドについて話しましょう。 4 行目の Executor の実装。ここでは、SimpleExecutor にキャッシュ機能を追加するためにデコレーター パターンが使用されています。コードは CachingExecutor にあります。 、となります、まとめもあります。 3 行目のコードは、入力パラメーターに基づいてキャッシュ キーを構築します。
4 行目のコードはクエリ操作を実行します。コードの実装も CachingExecutor にあります。
キャッシュへの設定と参照はありません。コードの 17 行目は、SimpleExecutor の親クラスである BaseExecutor にあり、ソース コードは次のように実装されます。 16 行目のコードはここで実行されます。 queryFromDatabase メソッドは次のように実装されます:
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 }コードは 5 行目に進み、最後に duQuery メソッドを実行します。 メソッドの実装は次のとおりです:
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 }
4 行目から 6 行目のコードが次のとおりです。前回の更新と同じなので、詳細は説明しません。ハンドラーの印象がある人は、それが PreparedStatementHandler であることを覚えておいてください。次のパートでは、Update との違いと、PreparedStatementHandler のクエリ メソッドがどのように実装されているかを分析します。
PreparedStatementHandlerのクエリメソッドの実装
PreparedStatementHandlerのクエリメソッドの最後に続くと、その最終実装は次のようになります:
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 }
クエリ操作を実行するには、3行目を参照してください。そして4行目コードは結果セットを処理し、結果セットを List に変換します。 handleResultSets メソッドは次のように実装されます。 この方法をまとめてみましょう。 コードの 7 行目は、PreparedStatement の getResultSet メソッドを通じて ResultSet を取得し、ResultSet を ResultSetWrapper にラップします。ResultSetWrapper は、ResultSet を含むだけでなく、各部分の行名と列名、および対応する列も定義します。データベースによって返されるデータ。JDBC の型と列に対応する Java クラスの型。さらに、最も重要なことは、TypeHandlerRegister (型プロセッサ、すべてのパラメータは TypeHandler を通じて設定される) も含まれていることです。 コードの 9 行目は、 コードの 11 行目は検証を行います。つまり、選択から返された結果が存在するが、それに対応する ResultMap または ResultType がない場合、例外がスローされます。理由は非常に簡単です。これら 2 つのうちの 1 つでは、MyBatis は戻り値の変換方法を知りません。 12行目から18行目のコードは、ResultSetWrapperの値をResultMapに基づいてJavaオブジェクトに変換し、まずmultipleResultsに格納します。これはList 20行目から33行目のコードは、 35 行目のコードは、size=1 の場合は要素 0 を取得し、size!=1 の場合は multipleResults を直接返します。 一般に、このメソッドは、データベースから返された結果に基づいて、それをカスタム ResultMap にカプセル化するプロセスに基本的に問題はありません。ここでの唯一の問題は、なぜ multipleResults を定義し、最終的に判断する必要があるのかということです。これは、multipleResults のサイズに基づいて行われます。そして、最終的な結果を分割することについては、まだ完全に理解できていません。この部分は、MyBatis アプリケーションの深化に応じて今後の作業で研究される予定です。 まとめ 前回の記事で、MyBatis 設定ファイルの読み込みと CRUD 操作を分析しましたが、私の感覚では、全体のプロセスはほぼ理解できましたが、まだいくつかの印象があります。重要なのは、どのようなデータがどこから取得され、どこで使用されるかということです。したがって、ここでは、キーにどのような内容が含まれているかを要約します。 MyBatis のクラス。 最初は SqlSessionFactory です。デフォルトは DefaultSqlSessionFactory です。SqlSessionFactory は毎回次のように保持されます。次に、Configuration があり、ここにすべての構成情報が含まれます。保管されています。ほとんどの属性、特にブール値は、
次に、構成されたデータベース環境情報を保存する環境があります。複数指定できますが、最終的に使用できるのは 1 つだけです。環境によって保持される重要な属性は次のとおりです。 次に MappedStatement が来ます。MappedStatement は、マッパー ファイル内の
、その後に BoundSql が続きます。BoundSql に保存される最も重要なものは、実行される現在の SQL ステートメントであり、残りにはパラメータ情報とパラメータ オブジェクトが含まれます。設定される、BoundSql 保持される属性は次のとおりです:
最后是ParameterMapping,ParameterMapping是待设置的参数映射,存储了待设置的参数的相关信息,ParameterMapping持有的属性有:
MyBatis中使用到的设计模式 下面来总结一下MyBatis中使用到的设计模式,有些设计模式可能在到目前位置的文章中没有体现,但是在之后的【MyBatis源码分析】系列文章中也会体现,这里一并先列举出来: 1、建造者模式 代码示例为SqlSessionFactoryBuilder,代码片段: 重载了大量的build方法,可以根据参数的不同构建出不同的SqlSessionFactory。 2、抽象工厂模式 代码示例为TransactionFactory,代码片段为: 抽象出事物工厂,不同的事物类型实现不同的事物工厂,像这里就是Jdbc事物工厂,通过Jdbc事物工厂去返回事物接口的具体实现。 其它的像DataSourceFactory也是抽象工厂模式的实现。 3、模板模式 代码示例为BaseExecutor,代码片段: BaseExecutor封装好方法流程,子类例如SimpleExecutor去实现。 4、责任链模式 代码示例为InterceptorChain,代码片段为: 可以根据需要添加自己的Interceptor,最终按照定义的Interceptor的顺序逐一嵌套执行。 5、装饰器模式 代码示例为CachingExecutor,代码片段为: 给Executor添加上了缓存的功能,update与query的时候会根据用户配置先尝试操作缓存。 在MyBatis中还有很多地方使用到了装饰器模式,例如StatementHandler、Cache。 6、代理模式 代码示例为PooledConnection,代码片段为: 这层代理的作用主要是为了让Connection使用完毕之后从栈中弹出来。 MyBatis中的插件也是使用代理模式实现的,这个在后面会说到。 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 }
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 }
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;
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 }
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 }
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 }
以上がソースコードの分析と概要を選択しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。