一:mybatis流程图
二:mybatis配置文件详解(读取Mybatis的内部核心文件并加载映射文件)
1 mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接、属性、类型别名、类型处理器、插件、环境配置、映射器(mapper.xml)等信息。
1.2 映射文件其实是在mybatis-config.xml里配置的。
(1)映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句, 映射文件是在mybatis-config.xml中加载;
(2)可以加载多个映射文件。常见的配置的方式有两种,一种是package扫描包,一种是mapper找到配置文件的位置。
1.3 这个核心配置文件最终会被封装成一个Configuration对象(在构建会话工厂的时候,会解析mybatis-config.xml,然后将相关信息存储到Configuration中)
(1)构建会话工厂类时,解析全局配置文件。
String resource = "mybatis-config.xml";//全局配置文件路径
InputStream inputStream = Resources.getResourceAsStream(resource);//读取xml文件
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//构建会话工厂类
(2)sqlSessionFactory .build(InputStream inputStream);
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
(3)sqlSessionFactory .build(InputStream inputStream,String environment, Properties properties);
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties){
try {
//委托XMLConfigBuilder来解析XML文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//XMLConfigBuilder#parse()将配置文件解析
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
(4)XMLConfigBuilder.parse()
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//mybatis的配置文件的根节点是configuration,从根节点开始解析配置文件
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
(5)XMLConfigBuilder.parseConfiguration(),这里的XNode是指根节点configuration下所有的节点内容
private void parseConfiguration(XNode root) {
try {
//解析子节点properties,该方法是解析properties节点内容;
propertiesElement(root.evalNode("properties"));
//解析子节点typeAliases 别名,<typeAliases>节点解析
typeAliasesElement(root.evalNode("typeAliases"));
//解析子节点plugins 插件,
//MyBatis允许你在映射语句执行过程中的某一点进行拦截调用。
//默认情况下,MyBatis允许使用插件来拦截的方法调用包括:
//Executor:拦截执行器的方法 (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
//ParameterHandler:拦截参数的处理 (getParameterObject, setParameters)
//ResultSetHandler:拦截结果集的处理 (handleResultSets, handleOutputParameters)
//StatementHandler:拦截Sql语法构建的处理 (prepare, parameterize, batch, update, query)
pluginElement(root.evalNode("plugins"));
//解析子节点objectFactory mybatis为结果创建对象时都会用到objectFactory
//每次MyBatis创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作
objectFactoryElement(root.evalNode("objectFactory"));
//解析子节点objectWrapperFactory
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析settings定义一些全局性的配置
settingsElement(root.evalNode("settings"));
//解析environments 可以配置多个运行环境,解析<environments>节点。
//但是每个SqlSessionFactory 实例只能选择一个运行环境
environmentsElement(root.evalNode("environments"));
//解析databaseIdProvider MyBatis能够执行不同的语句取决于你提供的数据库供应商。
//许多数据库供应商的支持是基于databaseId映射
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析typeHandlers(类型处理器) 当MyBatis设置参数到PreparedStatement
//或者从ResultSet 结果集中取得值时,就会使用TypeHandler 来处理数据库类型与java 类型之间转换
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers 主要的crud操作都是在mappers中定义的,定义SQL映射语句,告诉MyBatis到哪里去找到这些语句。
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
举例:用配置文件列举XMLConfigBuilder.parseConfiguration()都干了什么?
<configuration>
<!-- 引入jdbc配置文件 -->
<properties resource="db.properties"/>
<!-- settings配置信息 -->
<settings>
<!-- 该配置影响的所有映射器中配置的缓存的全局开关。 -->
<setting name="cacheEnabled" value="true"/>
<!-- 延迟加载的全局开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 是否允许单一语句返回多结果集(需要兼容驱动) -->
<setting name="multipleResultSetsEnabled" value="true"/>
<!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找,可选值:SLF4J|LOG4J|LOG4J2|JDK_LOGGING|COMMONS_LOGGING|STDOUT_LOGGING|NO_LOGGING-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 还有很多,在此就不一一列举了 -->
</settings>
<!-- 定义别名 -->
<typeAliases>
<!-- <typeAlias type="org.mybatis.example.pojo.Blog" alias="blog" /> --><!-- 手动定义别名 -->
<!-- 扫描包,自动以类名作别名 -->
<package name="org.mybatis.example.pojo"/>
</typeAliases>
<!-- 插件扩展 -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
<!-- 定义数据源 -->
<environments default="development">
<environment id="development">
<!-- 配置事务管理 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!--下面的属性值必须和db.properties中的key对应 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 定义映射文件 -->
<mappers>
<!-- 手动绑定映射文件 <mapper resource="mapper/BlogMapper.xml"/> -->
<!-- 扫描包,自动绑定映射文件 -->
<package name="org.mybatis.example.dao"/>
</mappers>
</configuration>
①propertiesElement
//该方法是解析properties节点内容
<properties resource="db.properties"/>
//或者
<properties resource="org/mybatis/example/db.properties">
<property name="username" value="xxx"/>
<property name="password" value="xxxxxx"/>
</properties>
②typeAliasesElement
//该方法用于<typeAliases>节点解析
//类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写;
<!-- 定义别名 -->
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
<typeAliases>
<!-- <typeAlias type="org.mybatis.example.pojo.Blog" alias="blog" /> -->
<!-- 扫描包,自动以类名作别名 -->
<package name="org.mybatis.example.pojo"/>
</typeAliases>
③pluginElement
MyBatis允许你在映射语句执行过程中的某一点进行拦截调用。
默认情况下,MyBatis允许使用插件来拦截的方法调用包括:
1.Executor:拦截执行器的方法 (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2.ParameterHandler:拦截参数的处理 (getParameterObject, setParameters)
3.ResultSetHandler:拦截结果集的处理 (handleResultSets, handleOutputParameters)
4.StatementHandler:拦截Sql语法构建的处理 (prepare, parameterize, batch, update, query)
④objectFactoryElement
private void objectFactoryElement(XNode context) throws Exception {
//开始解析
if (context != null) {
//获取type的值
String type = context.getStringAttribute("type");
//子节点作为属性存在
Properties properties = context.getChildrenAsProperties();
//同样这里可以用别名
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
//注入属性
factory.setProperties(properties);
//非常重要
configuration.setObjectFactory(factory);
}
}
⑤objectWrapperFactoryElement
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
//获取type的值
String type = context.getStringAttribute("type");
//实例化
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
//非常重要
configuration.setObjectWrapperFactory(factory);
}
}
⑥settingsElement
<settings>
<!-- 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
<setting name="cacheEnabled" value="true"/><!-- 默认true-->
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。-->
<!-- 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。-->
<setting name="lazyLoadingEnabled" value="false"/><!-- 默认false-->
<!-- 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/><!-- 默认false-->
<!-- 是否允许单个语句返回多结果集(需要数据库驱动支持)。-->
<setting name="multipleResultSetsEnabled" value="true"/><!-- 默认true-->
<!-- 使用列标签代替列名。实际表现依赖于数据库驱动,可参考数据库驱动的相关文档-->
<setting name="useColumnLabel" value="true"/><!-- 默认true-->
<!-- 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键 -->
<setting name="useGeneratedKeys" value="false"/><!-- 默认false-->
<!-- 指定 MyBatis 应如何自动映射列到字段或属性。 -->
<!-- NONE 表示关闭自动映射。 -->
<!-- PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 -->
<!-- FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 -->
<setting name="autoMappingBehavior" value="PARTIAL"/><!-- 默认PARTIAL-->
<!-- 指定发现自动映射目标未知列(或未知属性类型)的行为。 -->
<!-- NONE 不做任何反应 -->
<!-- WARNING 输出警告日志,日志输出等级必须设置为WARN -->
<!-- FAILING 映射失败 (抛出 SqlSessionException) -->
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/><!-- 默认WARNING-->
<!-- 配置默认的执行器。 -->
<!-- SIMPLE 就是普通的执行器 -->
<!-- REUSE 执行器会重用预处理语句(PreparedStatement) -->
<!-- BATCH 执行器不仅重用语句还会执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE"/><!-- 默认SIMPLE-->
<!-- 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 -->
<setting name="defaultStatementTimeout" value="25"/><!-- 任意正整数 -->
<!-- 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 -->
<setting name="defaultFetchSize" value="100"/><!-- 任意正整数 -->
<!-- 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 -->
<setting name="safeRowBoundsEnabled" value="false"/>
<!-- 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 -->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 -->
<!-- SESSION,会缓存一个会话中执行的所有查询。 -->
<!-- STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 -->
<setting name="localCacheScope" value="SESSION"/>
<!-- 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 -->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!-- 指定对象的哪些方法触发一次延迟加载。用逗号分隔的方法列表。 -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
⑦environmentsElement
<!-- 环境配置,解析<environments>节点 -->
<!-- 但是每个SqlSessionFactory 实例只能选择一个运行环境 -->
<environments default="development">
<environment id="development">
<!-- 配置事务管理 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!--下面的属性值必须和db.properties中的key对应 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
⑧databaseIdProviderElement
<!-- MyBatis能够执行不同的语句取决于你提供的数据库供应商。 -->
<!-- mybatis中我们可以使用databaseIdProvider这个元素实现数据库兼容不同厂商,即配置多中数据库。 -->
<databaseIdProvider type="DB_VENDOR">
<!-- 其中的name属性是数据库名称,value是我们自定义的别名 -->
<property name="Oracle" value="oracle"/>
<property name="MySQL" value="mysql"/>
<property name="DB2" value="d2"/>
</databaseIdProvider>
<!-- mapper.xml中使用 -->
<!-- 许多数据库供应商的支持是基于databaseId映射 -->
<select id="getAllProduct" resultType="product" databaseId="mysql">
SELECT * FROM product
</select>
⑨typeHandlerElement
使用TypeHandler 来处理数据库类型与java 类型之间转换
1.TypeHandler 契约了自定义的对参数和返回值 的处理方法。
将JDBC里的相关操作进行了尽可能的提取,而将自定义的需求以TypeHandler的形式向外界暴露。
2.针对传入参数进行处理的ParameterHandler(DefaultParameterHandler作为实现类)
针对返回值进行处理的ResultSetHandler(DefaultResultSetHandler作为唯一实现类)
它俩的实例化是在针对Statement进行处理的StatementHandler的实现类 BaseStatementHandler的构造函数中完成的。
而作为JDBC操作的核心组件的Statement是伴随着每次数据库操作重新生成的。
所以相对应的ParameterHandler和ResultSetHandler的实现类也会相应的重新构建一份。
3.Mybatis在对JDBC整体流程的深刻理解之上,抽取了尽可能多的重复性代码由框架来完成,
在此基础上又尽量保证了灵活性。
并且Mybatis也给了TypeHandler足够的地位,专门分配了顶级package org.apache.ibatis.type。
在该package中,除了相关辅助类外,绝大部分都是TypeHandler的实现类,
Mybatis预定义了一堆针对常见JDK类型的TypeHandler。
⑩mapperElement
解析mappers 主要的crud操作都是在mappers中定义的,定义SQL映射语句,
告诉MyBatis到哪里去找到这些语句。
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 或 -->
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 或 -->
<!-- 使用映射器接口的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 或 -->
<!-- 将包内的映射器接口全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>