blog

Mybatis ソースコード (II) --- parseConfiguration XML ファイルを読み込む

実際には、このタグはクラス内のパーサーによって読み込まれたコンフィギュレーション・ファイルを読み込むメソッドです。 それは、< >データソースで構成され、<マッパー> sqlステートメント内に書かれた...

Jun 18, 2020 · 21 min. read
シェア
parse() メソッドをタップしてください。
実際には、パーサが読み込んだコンフィグ設定ファイルのメソッドで XMLConfigBuilder クラスを読み込むマークです。

parseConfiguration(XNode root)

private void parseConfiguration(XNode root) {
 try {
 //issue #117 read properties first
 propertiesElement(root.evalNode("properties"));
 Properties settings = settingsAsProperties(root.evalNode("settings"));
 loadCustomVfs(settings);
 typeAliasesElement(root.evalNode("typeAliases"));
 pluginElement(root.evalNode("plugins"));
 objectFactoryElement(root.evalNode("objectFactory"));
 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
 reflectorFactoryElement(root.evalNode("reflectorFactory"));
 settingsElement(settings);
 // read it after objectFactory and objectWrapperFactory issue #631
 environmentsElement(root.evalNode("environments"));
 databaseIdProviderElement(root.evalNode("databaseIdProvider"));
 typeHandlerElement(root.evalNode("typeHandlers"));
 mapperElement(root.evalNode("mappers"));
 } catch (Exception e) {
 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
 }
 }
  • properties
  • settings
  • typeAliases タイプエイリアス
  • typeHandlers 型プロセッサ
  • objectFactory オブジェクト・ファクトリー
  • plugins
  • environments
  • databaseIdProvider データベース・ベンダーの識別
  • mappers

config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://.org/dtd/mybatis-3-.dtd">
<configuration>
 <environments default="development">
 <environment id="development">
 <transactionManager type="JDBC"></transactionManager>
 <dataSource type="POOLED">
 <property name="driver" value="com.mysql.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://localhost:3306/demo"/>
 <property name="username" value="root"/>
 <property name="password" value="98629"/>
 </dataSource>
 </environment>
 </environments>
 <mappers>
 <mapper resource="mapper/UserMapper.xml"></mapper>
 </mappers>
</configuration>

parseConfiguration に基づくコンフィグの結合.xml

< configuration >< environments >< mappers >あなたは、parseConfigurationメソッドは、設定ファイルを読み取るためにタグ名に基づいて、親タグで、それぞれ2つのサブタグがあり、次の2つのメソッドに対応している推測することができます。

< environments >< mappers >これは、タグ名に基づいて解析することが起こる、データソースで設定されているxmlファイルによると、設定されたマッパーマッピングファイルは、sqlステートメント内に書き込まれ、今、ブレークポイントを再生します。

environmentsElement(XNode context)---データ・ソースの読み込み

デバッグが開始され、ブレークポイントは、ルートノードの値の下に見つかったメソッドに設定され、それは、設定ファイルであることが起こるし、それはXNode.evalNode(文字列式)メソッドを呼び出すことが起こる さて、ルートオブジェクトは、変数パーサでXMLConfigBuilderクラスからであることが理解できます。XPathParserは、XMLファイルのコンテンツの値に格納されて

今すぐブレークポイントキャンセルは、あなたがソースコードを見ることができるenvironmentElementメソッドにポイント
private void environmentsElement(XNode context) throws Exception {
 if (context != null) {
 if (environment == null) {
 environment = context.getStringAttribute("default");
 }
 for (XNode child : context.getChildren()) {
 String id = child.getStringAttribute("id");
 if (isSpecifiedEnvironment(id)) {
 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
 DataSource dataSource = dsFactory.getDataSource();
 Environment.Builder environmentBuilder = new Environment.Builder(id)
 .transactionFactory(txFactory)
 .dataSource(dataSource);
 configuration.setEnvironment(environmentBuilder.build());
 }
 }
 }
 }

コードから、最初のタグ要素は、デフォルトの属性を取得することです、この属性の役割は、どのデータベースの設定を使用して、現在の状況を指定することです、つまり、どのノードの設定の使用は、デフォルトの値は、タグ要素のid値の構成です。そして、isSpecifiedEnvironmentメソッドが呼び出されます。

private boolean isSpecifiedEnvironment(String id) {
 if (environment == null) {
 throw new BuilderException("No environment specified.");
 } else if (id == null) {
 throw new BuilderException("Environment requires an id attribute.");
 } else if (environment.equals(id)) {
 return true;
 }
 return false;
 }

対応するidがデフォルトで設定された値であるかどうかを判断するために、1つずつすべてをトラバースしながら、もしそうであれば、現在の要素がデータベース接続を初期化するために使用されます。

transactionManagerElement---トランザクションのロード

< environments >パラメータchildの値を観察し、もう一度推測を検証します。
次に、型の値を取得する方法を見てください transactionManagerElementのソースコードを見てください。
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
 if (context != null) {
 String type = context.getStringAttribute("type");
 Properties props = context.getChildrenAsProperties();
 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
 factory.setProperties(props);
 return factory;
 }
 throw new BuilderException("Environment declaration requires a TransactionFactory.");
 }

Mybatisでは、JDBCとMANAGEDの2つの設定をサポートしています。ここでは、設定値の型に応じて、適切なトランザクションマネージャを返すために、JDBCとMANAGEDは、実際にConfigurationクラスで設定されていることがわかります。記事XMLConfigBuilderの親クラスBaseBuilderでBaseBuilderコンストラクタのパラメータは、Configurationオブジェクトの初期化であり、Configurationオブジェクトの初期化時に、組み込みのエイリアスレジストリTypeAliasRegistryは、JDBCとMANAGEDを含むデフォルトのエイリアスを登録します。JDBCやMANAGEDを含みます。

JDBC:この構成では、JDBCのコミットとロールバックの設定を直接使用し、データソースからの接続に依存してトランザクションスコープを管理します。接続のコミットやロールバックは行わず、コンテナにトランザクションのライフサイクル全体を管理させます。 デフォルトでは接続を閉じますが、これを望まないコンテナもあるため、closeConnection プロパティを false に設定して、デフォルトの閉じる動作を防ぐ必要があります。

dataSourceElement---データソースをロード

dataSourceElement のソースコードを見る

private DataSourceFactory dataSourceElement(XNode context) throws Exception {
 if (context != null) {
 String type = context.getStringAttribute("type");
 Properties props = context.getChildrenAsProperties();
 DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
 factory.setProperties(props);
 return factory;
 }
 throw new BuilderException("Environment declaration requires a DataSourceFactory.");
 }
データのXML構成では、すべて取得されている、ソースコードによると、最初にDataSourceFactoryのPOOLED型を作成し、その後、ファクトリーに設定された小道具の接続情報 Mybatisは、それぞれ、UNPOOLED、POOLED、およびJNDIの3つの組み込みデータソースの種類をサポートしていることについて構成オブジ ェ ク ト が初期化 さ れ る 際に、 内蔵エ イ リ ア ス レ ジ ス ト リ TypeAliasRegistry は、 UNPOOLED ・ POOLED ・ JNDI を含むデフ ォ ル ト エ イ リ ア ス を登録 し ます。
UNPOOLED このデータソースの実装は、要求されるたびに接続をオープン/クローズするだけです。少し遅いですが、タイムリーに利用可能な接続という点で、性能を必要としない単純なアプリケーションには良い選択です。 この点についてはデータベースによって挙動が異なるため、データベースによってはコネクションプールを使用することが重要でない場合もあり、この構成が理想的です。このデータソースの POOLED 実装では、"プーリング" の概念を使用して JDBC 接続オブジェクトを整理し、新しい接続インスタンスの作成に必要な初期化と認証の時間を回避します。 これは、同時実行中の Web アプリケーションがリクエストに迅速に応答できるようにするための一般的な方法です。JNDI データソースは、EJB やアプリケーションサーバーなどのコンテナーで使用するために実装されており、中央または外部でデータソースを構成し、JNDI コンテキストへの参照を配置することができます。作成したデータ・ソースは POOLED 型であることが分かっています。 そこから、environmentElement コードを使用して次のことを行います。
DataSource dataSource = dsFactory.getDataSource();
 Environment.Builder environmentBuilder = new Environment.Builder(id)
 .transactionFactory(txFactory)
 .dataSource(dataSource);
 configuration.setEnvironment(environmentBuilder.build());

JDBCで必要なデータを構成に設定します。

概要

mapperElement(root.evalNode("mappers"))-マッピング XML ファイルを読み込みます。

いわゆるMybatisの本質は、xmlコンフィギュレーション・ファイルの下にあるmappersノードかもしれません!

<mappers>
 <mapper resource="mapper/UserMapper.xml"></mapper>
</mappers>

mapper/UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://.org/dtd/mybatis-3-.dtd">
<mapper namespace="mapper.UserMapper">
 <!-- FindUserbyid-->
 <select id="selectById" resultType="domain.User" parameterType="int">
 select * from user where id = #{id}
 </select>
 <!-- FindAll-->
 <select id="selectAll" resultType="domain.User">
 select * from user
 </select>
 <!-- 一対一のルックアップ>
 <select id="selectInfo" parameterType="int" resultMap="StudentAndUserSelect">
 select student.*,user.*
 from student,user
 where student.id=user.id and user.id = #{id}
 </select>
 <resultMap id="StudentAndUserSelect" type="domain.Json.UserAndStudent">
 <association property="user" javaType="domain.User">
 <id property="id" column="id"></id>
 <result property="username" column="username"></result>
 <result property="password" column="password"></result>
 </association>
 <association property="student" javaType="domain.Student">
 <id property="id" column="id"></id>
 <result property="age" column="age"></result>
 <result property="sex" column="sex"></result>
 </association>
 </resultMap>
<!-- 一対多ルックアップ>
 <select id="selectCourse" parameterType="int" resultMap="StudentAndCourseSelect">
 SELECT s.*,c.*
 from student s
 INNER JOIN course c
 on s.id = c.student_id
 where c.student_id =#{id}
 </select>
 <resultMap id="StudentAndCourseSelect" type="domain.Json.StudentAndCourse">
 <collection property="students" ofType="domain.Student">
 <id property="id" column="id"></id>
 <result property="age" column="age"></result>
 <result property="sex" column="sex"></result>
 </collection>
 <collection property="courses" ofType="domain.Course">
 <id property="id" column="id"></id>
 <result property="student_id" column="student_id"></result>
 <result property="course" column="course"></result>
 </collection>
 </resultMap>
</mapper>

ソースコードを見てみましょう

private void mapperElement(XNode parent) throws Exception {
 if (parent != null) {
 for (XNode child : parent.getChildren()) {
 if ("package".equals(child.getName())) {
 //パッケージ・ノードかどうかを検出する
 String mapperPackage = child.getStringAttribute("name");
 configuration.addMappers(mapperPackage);
 } else {
 // <mapper resource="mapper/UserMapper.xml"></mapper>のマッパー/UserMapper は、XML ファイルを読み込む。.xml,すなわちリソース= "mapper/UserMapper.xml"
 String resource = child.getStringAttribute("resource");
 //マッパー・ノードの url 属性を読み込む
 String url = child.getStringAttribute("url");
 //マッパー・ノードの class 属性を読み込む
 String mapperClass = child.getStringAttribute("class");
 if (resource != null && url == null && mapperClass == null) {
 //rusource に基づいてマッパーファイルを読み込む
 ErrorContext.instance().resource(resource);
 //ファイルのバイトストリームを読み込む
 InputStream inputStream = Resources.getResourceAsStream(resource);
 //マッパー・パーサーのインスタンス化
 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
 //マッパーファイルのパース、すなわちマッパー/ユーザーダオマッピングのパースを実行する.xml, 
 mapperParser.parse();
 } else if (resource == null && url != null && mapperClass == null) {
 //Web urlリソースからマッパーファイルをロードする
 ErrorContext.instance().resource(url);
 InputStream inputStream = Resources.getUrlAsStream(url);
 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
 mapperParser.parse();
 } else if (resource == null && url == null && mapperClass != null) {
 //mapperClass を使ってファイルを読み込む
 Class<?> mapperInterface = Resources.classForName(mapperClass);
 configuration.addMapper(mapperInterface);
 } else {
 //resource,url,mapperClass3つのコンフィギュレーション・メソッドのうち1つしか使用できない。
 throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
 }
 }
 }
 }
 }

マッパー内のマッパーノードを繰り返し、一つずつ解析するのは明らかです。マッパー設定ファイルにはマッパーノードが1つしかないので、ここで解析するのはmapper/UserMapper.xmlファイルです。ここでデバッグモードでブレークポイントを作ります。

値を見ると
parsing mappersノードの下にあるコアコードは次のとおりです。
//マッパー・パーサーのインスタンス化
&emsp;&emsp;&emsp;&emsp;XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
&emsp;&emsp;&emsp;&emsp;//マッパーファイルのパース、すなわちマッパー/ユーザーダオマッピングのパースを実行する.xml, 
&emsp;&emsp;&emsp;&emsp;mapperParser.parse();

マッパー・パーサーのインスタンス化: XMLMapperBuilder

public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
 this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
 configuration, resource, sqlFragments);
 }

そのthisキーワードでプライベートメソッドを呼び出します。

 private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
 super(configuration);
 this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
 this.parser = parser;
 this.sqlFragments = sqlFragments;
 this.resource = resource;
 }

新しいクラスMapperBuilderAssistantの内部では、その役割はXMLファイルの解析を支援することです。 同様に、MapperBuilderAssistantクラスはBaseBuilderから継承されています。しかし、次の分析をよりよく行うためには、Configuration クラスのプロパティとメソッドのいくつかを知っておく必要があります。

public class Configuration {
 protected Environment environment;
 protected Properties variables = new Properties();
 ......
 //値をnullに初期化する
 protected String databaseId;
 protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
 protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
 // これは構文解析された sql 宣言を格納する HashMap で、キーは文字列型である。.zcz.learnmybatis.entity.User.findUserById,値は MappedStatement インスタンス・オブジェクトである。
 protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
 protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
 ......
 //これは、ロードされパースされたマッパーファイルの名前を保持する、順序付けされていない非繰り返しの Set コレクションである。例えば<mapper resource="mapper/userDao-mapping.xml"/>のマッパー/ユーザーダオマッピングは、XML ファイルを読み込む。.xml
 protected final Set<String> loadedResources = new HashSet<String>();
 ......
 //sqlフラグメントマップ、キー文字列値XNode、このマップは、前のマッパーで解析されたフラグメントに格納されている
 protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
 ......
 public Configuration() {
 .....
 // デフォルトの XML 言語ドライバを登録する
 languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
 languageRegistry.register(RawLanguageDriver.class);
 }
 ......
 //LoadParsingCompleteSet loadedResources にリソースを追加する。
 public void addLoadedResource(String resource) {
 loadedResources.add(resource);
 }
 //マッパーファイルがロードされ、パースされたかどうかを検出する。<mapper resource="mapper/userDao-mapping.xml"/>リソース内の
 public boolean isResourceLoaded(String resource) {
 return loadedResources.contains(resource);
 }
 ......
 
 //ラベル宣言クラスインスタンスオブジェクトIDに従って、解析されたラベル宣言クラスインスタンスオブジェクトを取得する
 // タグ宣言とは何か?
 public MappedStatement getMappedStatement(String id) {
 return this.getMappedStatement(id, true);
 }
 
 //ラベル宣言クラスインスタンスオブジェクトIDに従って、解析されたラベル宣言クラスインスタンスオブジェクトを取得する
 public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
 if (validateIncompleteStatements) {
 buildAllStatements();
 }
 return mappedStatements.get(id);
 }
 
 //sql フラグメントを取得する
 public Map<String, XNode> getSqlFragments() {
 return sqlFragments;
 }
 ......
 // statementNameという名前のタグ宣言が存在するかどうかのチェックに基づいている。
 public boolean hasStatement(String statementName) {
 return hasStatement(statementName, true);
 }
 // statementNameという名前のタグ宣言が存在するかどうかのチェックに基づいている。
 public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
 if (validateIncompleteStatements) {
 buildAllStatements();
 }
 return mappedStatements.containsKey(statementName);
 }
 ......
}

mapperParser.parse();; mapperParser.parse(); mapperParser.parse()

さて、ブレークポイント前のメソッドでxmlファイルをどのようにパースしているか詳しく見ることができます。

parseメソッドのソースコード
 public void parse() {
 // 最初にマッパーファイルが解析されたかどうかを判断する
 if (!configuration.isResourceLoaded(resource)) {
 //構文解析を行う
 configurationElement(parser.evalNode("/mapper"));
 //解析レコードを保存する
 configuration.addLoadedResource(resource);
 // すでにパースされた名前空間をバインドする
 bindMapperForNamespace();
 }
 parsePendingResultMaps();
 parsePendingCacheRefs();
 parsePendingStatements();
 }

マッパーファイルをパースする実際のコードはconfigurationElementメソッドであることがわかります。

private void configurationElement(XNode context) {
 try {
 //マッパー・ファイルのマッパー・ノードの namespace 属性を取得する mapper.UserMapper
 String namespace = context.getStringAttribute("namespace");
 if (namespace.equals("")) {
 throw new BuilderException("Mapper's namespace cannot be empty");
 }
 //マッピング・マッパー・パーサー・アシスタント builderAssistant に名前空間を割り当てる.currentNameSpace,つまり、マッパー・パーサー・アシスタントがマッパー・ファイルを解析していることを伝える。
 builderAssistant.setCurrentNamespace(namespace);
 //キャッシュ参照ノードを解析する
 cacheRefElement(context.evalNode("cache-ref"));
 //キャッシュ・ノードを解析する
 cacheElement(context.evalNode("cache"));
 //parameterMap "を直接使うのではなく、"/mapper/parameterMap "を使うのは、parameterMap は複数設定できるからである。.evalNodesメソッドは evalNode ではなく evalNodes になっている。
 parameterMapElement(context.evalNodes("/mapper/parameterMap"));
 //resultMap ノードを解析する
 resultMapElements(context.evalNodes("/mapper/resultMap"));
 //sql ノードを解析する
 sqlElement(context.evalNodes("/mapper/sql"));
 //ParseSelect|insert|update|deleteノード、コンテキストに注意.evalNodes()メソッドを呼び出す。
 buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
 } catch (Exception e) {
 throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
 }
 }

上記のコードから、名前空間を処理した後、マッパーファイルのノードを解決することです ノードを解決した後、buildStatementFromContextメソッドのソースコードを分析してみましょう。

private void buildStatementFromContext(List<XNode> list) {
&emsp;&emsp;&emsp;//これがコンフィギュレーションだ。.getDatabaseId なぜなら、コンフィギュレーションが初期化されるときにはデフォルト値が与えられず、コンフィギュレーション・オブジェクトが仮想マシンによってインスタンス化されるときにはデフォルト値として null が与えられるからである。
 if (configuration.getDatabaseId() != null) {
 buildStatementFromContext(list, configuration.getDatabaseId());
 }
 buildStatementFromContext(list, null);
 }

そして、buildStatementFromContextオーバーロードメソッドを呼び出し、このメソッドはすべてのラベル宣言を繰り返し、1つずつ解析します。

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
 for (XNode context : list) {
&emsp;&emsp;&emsp;&emsp;//タグ宣言パーサーを初期化する
 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
 try {
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;// タグ宣言の解析を実行する
 statementParser.parseStatementNode();
 } catch (IncompleteElementException e) {
 configuration.addIncompleteStatement(statementParser);
 }
 }
 }
案の定、それはXMLを解析するためにトラバーサルを通じて犬泥棒です。

XMLStatementBuilder正式に XML の走査を開始

同じXMLStatementBuilderはまた、BaseBuilderから継承されています。次のコンストラクタを見るにはクリックしてください

public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
 super(configuration);
 this.builderAssistant = builderAssistant;
 this.context = context;
 this.requiredDatabaseId = databaseId;
 }
 public void parseStatementNode() {
 String id = context.getStringAttribute("id");
 String databaseId = context.getStringAttribute("databaseId");
 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
 return;
 }
 Integer fetchSize = context.getIntAttribute("fetchSize");
 Integer timeout = context.getIntAttribute("timeout");
 String parameterMap = context.getStringAttribute("parameterMap");
 String parameterType = context.getStringAttribute("parameterType");
 Class<?> parameterTypeClass = resolveClass(parameterType);
 String resultMap = context.getStringAttribute("resultMap");
 String resultType = context.getStringAttribute("resultType");
 String lang = context.getStringAttribute("lang");
 LanguageDriver langDriver = getLanguageDriver(lang);
 Class<?> resultTypeClass = resolveClass(resultType);
 String resultSetType = context.getStringAttribute("resultSetType");
 StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
 ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
 String nodeName = context.getNode().getNodeName();
 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
 boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
 boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
 boolean useCache = context.getBooleanAttribute("useCache", isSelect);
 boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
 // Include Fragments before parsing
 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
 includeParser.applyIncludes(context.getNode());
 // Parse selectKey after includes and remove them.
 processSelectKeyNodes(id, parameterTypeClass, langDriver);
 
 // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
 String resultSets = context.getStringAttribute("resultSets");
 String keyProperty = context.getStringAttribute("keyProperty");
 String keyColumn = context.getStringAttribute("keyColumn");
 KeyGenerator keyGenerator;
 String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
 keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
 if (configuration.hasKeyGenerator(keyStatementId)) {
 keyGenerator = configuration.getKeyGenerator(keyStatementId);
 } else {
 keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
 configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
 ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
 }
 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
 fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
 resultSetTypeEnum, flushCache, useCache, resultOrdered, 
 keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
 }

一見したところ、タグの内容を取得するためのものです。

databaseIdMatchesCurrent
//databaseIdとrequiredDatabaseIdの両方がNULLの状態で、必要なdatabaseIdをタグ宣言のdatabaseIdと比較する。
 private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
 if (requiredDatabaseId != null) {
 if (!requiredDatabaseId.equals(databaseId)) {
 //異なる場合はfalseを返し、パースを停止する
 return false;
 }
 } else {
 if (databaseId != null) {
 // 今回は requiredDatabaseId== null, このrequiredDatabaseIdの場合nullに等しいdatabaseIdがnullに等しくない、つまり、databaseIdを使用する必要性とdatabaseIdのラベル宣言が同じではない、falseに戻し、解析を停止する。
 return false;
 }
 // skip this statement if there is a previous one with a not null databaseId すでに解析済みのタグ宣言があり、databaseId が NULL でない場合は、false を返して解析をスキップする。
 // idを取得する。このidはラベル・パーサーのidで、次の分析ではっきりわかる:このidは、ラベル宣言クラスによってインスタンス化されたオブジェクトのidでもある。
 id = builderAssistant.applyCurrentNamespace(id, false);
 
 if (this.configuration.hasStatement(id, false)) {
 MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
 if (previous.getDatabaseId() != null) {
 return false;
 }
 }
 }
 return true;
 }
langDriver.createSqlSource();sql 文の処理

このメソッドは#{}, {"が存在するかどうかを処理してSQL文が動的SQL文かどうかを判定し、select * from user where id = #{id}をselect * from user where id = ?また、idを#{id}に保存します。

builderAssistant.addMappedStatementMappedStatement オブジェクトへの変換例

もっと簡単に言うと

<select id="selectById" resultType="domain.User" parameterType="int">
 select * from user where id = #{id}
</select>

をMappedStatementオブジェクトに変換し、以下のようにコンフィギュレーションに保持するだけです。

public MappedStatement addMappedStatement(
 String id,
 SqlSource sqlSource,
 StatementType statementType,
 SqlCommandType sqlCommandType,
 Integer fetchSize,
 Integer timeout,
 String parameterMap,
 Class<?> parameterType,
 String resultMap,
 Class<?> resultType,
 ResultSetType resultSetType,
 boolean flushCache,
 boolean useCache,
 boolean resultOrdered,
 KeyGenerator keyGenerator,
 String keyProperty,
 String keyColumn,
 String databaseId,
 LanguageDriver lang,
 String resultSets) {
 
 if (unresolvedCacheRef) throw new IncompleteElementException("Cache-ref not yet resolved");
 
 id = applyCurrentNamespace(id, false);
 boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
&emsp;&emsp;//MappenStatement を初期化する.Builder
 MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
 statementBuilder.resource(resource);
 statementBuilder.fetchSize(fetchSize);
 statementBuilder.statementType(statementType);
 statementBuilder.keyGenerator(keyGenerator);
 statementBuilder.keyProperty(keyProperty);
 statementBuilder.keyColumn(keyColumn);
 statementBuilder.databaseId(databaseId);
 statementBuilder.lang(lang);
 statementBuilder.resultOrdered(resultOrdered);
 statementBuilder.resulSets(resultSets);
 setStatementTimeout(timeout, statementBuilder);
 setStatementParameterMap(parameterMap, parameterType, statementBuilder);
 setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
 setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
&emsp;&emsp;// MappedStatement を構築する
 MappedStatement statement = statementBuilder.build();
&emsp;&emsp;//  
 configuration.addMappedStatement(statement);
 return statement;
 }

ここで、最初の select id="selectById" タグ宣言の解析が完了し、XMLStatementBuilder クラスを経由して、Mapper ファイルの解析が終了するまで、次をトラバースしていきます。

概要:

XMLConfigBuilderオブジェクトのparseConfigurationメソッドを介して、xmlファイルを解析するノードによると、マッパーのノード解決が完了し、すべてのmybatis設定ファイルが解析されている 彼らは最終的にオブジェクトを提供する最終的な設定オブジェクトです。

Mybatisのソースコード--- SqlSessionFactoryBuilder(設定ファイルを取得するには)

Mybatisソースコード --- parseConfiguration 読み込み XML ファイル

Mybatis のソース・コード --- SqlSession による sql クエリの実行

Mybatis のソースコード --- SqlSession による動的エージェントの完成

Mybatisのソースコード --- SqlSessionの4つの主要オブジェクト

Mybatis ソース・コード --- キャッシュ

個人的な意見です。議論へようこそ、個人ブログ

Read next

Flask開発フォーラム - はじめに

前に書く\n\n\nこのプロジェクトの完全なディレクトリは\nもし何か間違っていたり、もっと良い解決策があれば、ご教示ください!\n本題に入りましょう。\nまず、PycharmでFlaskプロジェクトを作成し、Flaskを選択し、フォーラムの名前を選択します:\nもし

Jun 18, 2020 · 7 min read