ホームページ >Java >&#&チュートリアル >ソースコードの分析と概要を選択します

ソースコードの分析と概要を選択します

巴扎黑
巴扎黑オリジナル
2017-06-26 09:32:121578ブラウズ

サンプルコード

前の記事では、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 行目は、 タグは 1 つの resultMap 属性のみを定義する必要があります。は List ですが、あまり明確ではありません。

コードの 11 行目は検証を行います。つまり、選択から返された結果が存在するが、それに対応する ResultMap または ResultType がない場合、例外がスローされます。理由は非常に簡単です。これら 2 つのうちの 1 つでは、MyBatis は戻り値の変換方法を知りません。

12行目から18行目のコードは、ResultSetWrapperの値をResultMapに基づいてJavaオブジェクトに変換し、まずmultipleResultsに格納します。これはListです。

20行目から33行目のコードは、 に対応します。 MappedStatement が実行されると、MappedStatement が保持するいくつかの重要な属性は次のとおりです:

、その後に BoundSql が続きます。BoundSql に保存される最も重要なものは、実行される現在の SQL ステートメントであり、残りにはパラメータ情報とパラメータ オブジェクトが含まれます。設定される、BoundSql 保持される属性は次のとおりです:

最后是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中的插件也是使用代理模式实现的,这个在后面会说到。

以上がソースコードの分析と概要を選択しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。