タイトル: 最近、仕事後に MyBatis のソースコードを解析しました。ソースコードを読むきっかけとなったのは、MyBatis の物理ページングを実現するためでした。 MyBatis が論理的にページングし、ユーザー クエリを通じて結果をキャッシュし、RowBounds オブジェクトが渡されたかどうかを確認し、その中のオフセット値と制限値を確認していることがわかります。これらの 2 つの値を通じて、返された結果セットから値をインターセプトします。期間内に。しかし、これはあまり良いことではありません。クエリされるデータの量が多くても、最初のいくつかの項目が役立つ場合、これは少し無駄になります。以前、インターネット上でページングを実装する方法を調べたことがありますが、最も一般的に使用されている方法は、MyBatis プラグインを追加し、Interceptor インターフェースを実装し、StatementHandler インターフェースで prepare メソッドをインターセプトする方法である理由を紹介します。このインターフェースのインターセプトについては後で紹介します。 ResultSetHandler インターフェースの handlerResultSet メソッドをインターセプトする理由は後で紹介します。ただし、このメソッドはページング SQL ステートメントを追加できますが、Mybatis が SQL にページング オフセットと制限値を動的に追加することはできません。StatementHandler インターフェイスをインターセプトするときにそれらをアセンブルできると言う人もいます。ただし、これにより SQL インジェクションの問題が発生しやすくなります。したがって、これにより、MyBatis の内部原理をさらに理解する必要があります。この記事では、MyBatis の内部実装を次の側面から分析します
数据管家——Configuration: MyBatis在运行期的基本上所有的数据都会汇总到这个类。它的初始数据是来自开发人员配置在configuration的xml配置文件。通过用户配置的environments来获得系统运行的数据库环境,如事物管理以及数据源。下面给出了最基本的配置: [html] <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=GBK"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/bieber/mybatis/io/user-mapper.xml"/> </mappers> </configuration>
这些配置对于MyBatis需要做哪些工作呢?通过阅读Configuration的源码会发现,Mybatis其实为configuration标签下面的子标签都有一个对应的变量来进行存储,例如: [java] protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
则是存储<typeHandlers></typeHandlers>标签下面配置的所有信息。其他的也类似可以找到。负责创建Configuration对象的则是XMLConfigurationBuilder,这里将完成从配置的XML数据映射到Configuration对象的数据。通过一下方法完成数据的映射: [java] private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); 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); } }
ご覧のとおり、各要素の処理メソッドに対応しており、これらのメソッドは、設定した XML ファイルの解析を担当します。ここでは主に、いくつかのメソッド www.2cto.com
(mapperElement、typeHandlerElement、typeAliasesElement、environmentElement) の実行を追跡します
mapperElement - ORM
MyBatis がアノテーション形式と XML 形式で ORM 構成をサポートしていることはわかっています。もちろん、これら 2 つの動作を処理するクラスは 2 つあります。それは、XMLMapperBuilder と MapperAnnotationBuilder です。それぞれがどのようなタイプを処理するかを言う必要はないと思います。 configuration/mappers 要素を解析して、ORM 構成情報を取得します。
1) XML ORM の設定と方法。マッパー/マッパーの属性に URL またはリソース情報を設定すると、MyBatis がトリガーされて処理に XML を使用し、指定したマッパー パスを読み取ります。 XMLMapperBuilder クラスには次のメソッドがあります:
[java]
private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); parameterMapElement(context.evalNodes("/mapper/parameterMap")); resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new RuntimeException("Error parsing Mapper XML. Cause: " + e, e); } }
这个方法便是读取你mapper文件中所有制的ORM信息。该方法将通过调用XMLMapperBuilder的parse()方法触发。
2)注解方式配置ORM信息加载,当你配置了mappers/package或者在mapper里面配置了class属性的时候将触发信息的读取,具体的过程我就再描述了,基本和上面差不多,只是读取的是注解的信息。
注意:MyBatis优先处理的是注解形式的方式,并且在mapper配置中,当配置了多个属性时,resource属性优先处理。
那么在这样处理后Configuration会得到怎样的数据呢?通过这些处理在Configuration里面将会获得几个主要的变量值:sqlFragments,resultMaps,mappedStatements。其中sqlFragments就是我们定义在mapper里面的sql标签或者注解的内容,而resultMaps也是定义在mapper里面或者注解的resultMap内容。最重要的是mappedStatements,这是ORM的最关键部分。它里面通过键值对的方式存储,key这是我们配置的id属性加上namespace,而value则是MappedStatement对象,这个对象这就对应了我们配置的select/update/delete/insert标签的值。
MappedStatement对象包含这条slq语句的ID,执行的类型(Inser,update,delte,select),statementType(指定产生Statement的类型,如PreparedStatement),还有一个就是SqlSource接口的子类对象,在MyBatis中有两种SqlSource,一种是动态的,另一种是静态的。不用解释,应该都明白,一个是生成动态SQL用的,另一个这是简单静态的SQL。在SqlSource中,包括你定义的SQL语句,以及引入的外部SQL语句块。MappedStatement最后还要包括一个重要的信息,这就是ParameterMap,这直接关系你定义的SQL语句中通过#{propertyName}定义的动态填充值。如果你的是一个POJO对象,那么MyBatis将会通过反射获得这个对象的属性,并依次填入到对应的propertyName所在的位置。
注意:此时的MappedStatement中的SQL语句还是带有#{propertyName}这样占位符的字符串,还并没有解析成待问号(?)的占位符。要执行该操作是在执行具体的数据库操作的时候才替换成(?),只是为了很好的找到这个propertyName所对应的值所在的位置。
以上就将整个SqlSession的初始化过程所做的操作进行了解剖。完成这些操作之后,那么就等待用户触发对数据库的操作了。
后续将会给出,MyBatis是如何触发用户自定义的插件的过程以及开发自己的TypeHandler。MyBatis允许用户的插件可以拦截ParameterHandler,ResultSetHandler,StatementHandler,Executor接口,从而进行一些操作。
本文到此继续,后续会有新的更新。如有严重不对的地方,还望各位能够及时提出,毕竟对MyBatis的接触也只有一个星期,未免有些地方不对,还望大家谅解。
以上就是MyBatis整体预览(一)的内容,更多相关内容请关注PHP中文网(www.php.cn)!